Conversation
`status` treated any non-empty `ring_token` as proof of a working sign-in,
and `finish` saved whatever `getAuth` returned. Neither can detect the one
failure that matters.
Ring wraps a refresh token as base64 JSON: the credential in `rt` next to
the hardware id. When a sign-in does not actually yield a token,
ring-client-api still returns a wrapped value, but `rt` is `undefined` and
`JSON.stringify` drops it, leaving `{"hid":"…"}`. That string is truthy, so
the existing checks accept it, the panel reports a signed-in account, and
ring-mqtt then fails every authentication attempt against the Ring API with
401 invalid_grant - token is invalid or does not exists
The panel offers no way out, because as far as it knows the user is already
signed in.
Decode the token and require a non-empty `rt` instead:
- `status` reports an unusable token as not authenticated, so the panel
prompts for sign-in again rather than showing a dead connected state.
- `finish` surfaces an error and leaves no state file behind, instead of
saving a token that cannot work and starting the camera service.
A value that is not wrapped JSON is still treated as usable, matching the
fallback in ring-client-api's own `parseAuthConfig`, so raw tokens and the
existing fixtures keep working.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A failed sign-in is saved as a success
statustreats any non-emptyring_tokenas proof of a working sign-in, andfinishsaves whatevergetAuthreturns. Neither can detect the one failure that actually matters.Ring wraps a refresh token as base64 JSON — the credential in
rt, next to the hardware id. When a sign-in does not yield a token,ring-client-apistill returns a wrapped value, butrtisundefinedandJSON.stringifysilently drops it:{"hid":"1906d565-…"}That string is truthy, so:
finishsaves it and starts the camera servicering-mqttthen fails every call with401 invalid_grantThe existing
if (!auth.refresh_token) throwguard cannot catch this — the field is always a non-empty string.This is what made #1 so hard to diagnose. The backend was healthy, all four services were running,
state.jsonsaid"connected": true, andring-mqttlogged nothing at all (everything there routes throughdebug, and the unit sets noDEBUG). From the outside it looked like a working install with no cameras. It took decoding the stored token to see that it contained no credential.What this changes
Decode the token and require a non-empty
rt:statusreports an unusable token as not authenticated, so the panel prompts for sign-in again instead of showing a dead connected state.finishsurfaces a clear error and leaves no state file behind, instead of persisting a token that cannot work and starting the camera service.A value that is not wrapped JSON is still treated as usable, matching the fallback in
ring-client-api's ownparseAuthConfig, so raw tokens and the existing fixtures keep working.Tests
Three added, 47/47 passing:
rtcasesstatusdoes not report a credential-less token as authenticatedinteractiveemits an error, does not start the service, and writes no state fileThe second and third would both have failed before this change.
This is independent of #1 — worth having either way, since it turns an hour of digging into an immediate, accurate error message.
🤖 Generated with Claude Code