Skip to content

fix: auto-refresh token, persist to session, fix Object.keys length b…#101

Open
tibrown wants to merge 1 commit into
tonesto7:masterfrom
tibrown:echo-speaks-server-auth-fix
Open

fix: auto-refresh token, persist to session, fix Object.keys length b…#101
tibrown wants to merge 1 commit into
tonesto7:masterfrom
tibrown:echo-speaks-server-auth-fix

Conversation

@tibrown

@tibrown tibrown commented Jun 29, 2026

Copy link
Copy Markdown

Title: fix: Amazon auth token expiry — scheduled refresh, session persistence, and Object.keys bug

Summary

This PR fixes a longstanding issue where Amazon login sessions expire every 3-4 days despite Amazon's refresh token being valid for 90 days. Three separate bugs were found working together to cause this. All fixes are in index.js.



Background

When a user logs in via the proxy, Amazon issues two things:
- Short-lived session cookies (localCookie, csrf) — valid for approximately 3-4 days
- A long-lived refreshToken — valid for ~90 days

The correct behaviour is to use the refreshToken to silently obtain fresh session cookies well before the short-lived ones expire. The code had scaffolding for this (scheduledUpdatesActive, a commented-out stopScheduledDataUpdates() in the exit handler) but it was never wired up. As a result the server relied entirely on the Hubitat app calling /refreshCookie on its own schedule — and if that schedule was longer than 3-4 days (matching the user's expectation of a 90-day token), auth failed and required a full re-login.



Bug 1 — Object.keys() missing .length (stored session fallback is dead code)

In the alexaLogin flow, after failing to retrieve cookies from the hub, there is a fallback to a locally stored session:

js
// BEFORE (broken)
} else if (sessionData && sessionData.cookieData && Object.keys(sessionData.cookieData) >= 2) {


Object.keys() returns an array. Comparing an array to the number 2 with >= coerces it to NaN, so NaN >= 2 is always false. This branch could never fire. On every server restart the code fell straight through to a full generateAlexaCookie() re-login attempt, discarding any stored session entirely.

js
// AFTER (fixed)
} else if (sessionData && sessionData.cookieData && Object.keys(sessionData.cookieData).length >= 2) {




Bug 2 — Refreshed cookie data not persisted to session.json

In the /refreshCookie endpoint, on a successful refresh the new tokens were stored in memory (runTimeData.savedConfig.cookieData = result) but updSessionItem("cookieData", result) was never called. The session.json file retained the old pre-refresh data. Combined with Bug 1 being fixed, this meant a server restart after a successful refresh would still load stale tokens from disk.

Added updSessionItem("cookieData", result) immediately after the in-memory assignment.



Bug 3 — No scheduled auto-refresh (root cause of the 3-4 day expiry)

The intended design was clearly a background timer that proactively refreshes the token — runTimeData.scheduledUpdatesActive exists, is checked in the exit handler, and the exit handler had a commented-out stopScheduledDataUpdates() stub. But the interval itself was never created.

This PR implements it: after every successful login, a setInterval is started that calls alexaCookie.refreshAlexaCookie(), validates the result, writes it to both memory and session.json, and pushes the fresh tokens to the Hubitat hub via sendCookiesToEndpoint. The interval is guarded by scheduledUpdatesActive so server restarts cannot stack duplicate timers. The exit handler's stub is now wired to clearInterval.



Heroku vs Local interval

The refresh interval is set differently depending on deployment mode:

- Local (useHeroku !== true): every 24 hours. session.json survives restarts so the stored session fallback (Bug 1 fix) handles any gap.
- Heroku (useHeroku === true): every 6 hours. Heroku dynos cycle approximately every 24 hours and wipe the local filesystem, destroying session.json. The hub is the only persistent store in that architecture. Refreshing every 6 hours ensures the hub always holds tokens that are at most 6 hours old when the dyno restarts and re-fetches from the hub — well within Amazon's session TTL.

The startup log clearly states which mode is active:

Starting scheduled cookie refresh (every 6 hours (Heroku))...
// or
Starting scheduled cookie refresh (every 24 hours (local))...




Testing

Verified on a local Linux deployment running under systemd. The session.json restore path (Bug 1) was confirmed working after service restart. No changes to proxy logic, cookie capture, or any other auth flow — only the post-login refresh lifecycle is affected.



Files changed

- index.js — 3 bug fixes + scheduled refresh implementation (~42 lines added, 2 changed)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant