fix(client): use form-urlencoded for OAuth token endpoint#24
Open
omarshahine wants to merge 2 commits intosteipete:mainfrom
Open
fix(client): use form-urlencoded for OAuth token endpoint#24omarshahine wants to merge 2 commits intosteipete:mainfrom
omarshahine wants to merge 2 commits intosteipete:mainfrom
Conversation
The Eight Sleep auth server (auth-api.8slp.net/v1/tokens) expects standard OAuth2 form-urlencoded requests, not JSON. The previous implementation sent JSON with hardcoded "sleep-client" credentials, which caused a 400 from Joi validation. The fallback to legacy /login then tripped the rate limiter, resulting in a permanent 429 loop. Changes: - Send application/x-www-form-urlencoded instead of application/json - Use c.ClientID and c.ClientSecret (the real app creds extracted from the Android APK) instead of hardcoded "sleep-client"/"" - Make authURL a var so tests can point it at a local server - Add tests for form encoding, credential passthrough, and legacy login fallback Fixes steipete#7, fixes steipete#8, fixes steipete#12 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The do() method sends Accept-Encoding: gzip but never decompresses
the response body, causing json.Decode to fail with:
invalid character '\x1f' looking for beginning of value
(0x1f is the gzip magic byte.)
Check Content-Encoding: gzip on responses and wrap the body in a
gzip.Reader before decoding. Added test with a mock gzip response.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4 tasks
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.
Problem
Two bugs in the API client that make
eightctlfail:1. Wrong OAuth content type and credentials in token endpoint
authTokenEndpoint()sendsapplication/jsonbut the Eight Sleep auth server (auth-api.8slp.net/v1/tokens) expects standard OAuth2application/x-www-form-urlencoded. It also hardcodesclient_id: "sleep-client"with an emptyclient_secretinstead of using the actual app credentials.The JSON request always returns 400 (Joi validation), causing every authentication to fall through to
authLegacyLogin(). After a few attempts the rate limiter kicks in → permanent 429 loop.Note: PR #15 correctly identified the credential issue but kept JSON encoding, which is why the token endpoint still fails even with that fix applied.
2. Gzip responses not decompressed
do()sendsAccept-Encoding: gzipbut never decompresses the response body, causing:(
0x1fis the gzip magic byte.)This bug is masked when the token cache isn't working (e.g., macOS Keychain inaccessible in headless/daemon environments), because every call re-authenticates via the token endpoint which returns plain JSON. Once tokens cache properly, subsequent API calls hit the gzip issue.
Fix
Commit 1 — OAuth auth:
application/x-www-form-urlencodedcontent type (standard OAuth2 token request format)c.ClientIDandc.ClientSecretinstead of hardcoded"sleep-client"/""authURLa package-levelvar(wasconst) so tests can redirect it to a local serverCommit 2 — Gzip decompression:
Content-Encoding: gzipon responses and wrap body ingzip.Readerbefore JSON decodingTests added:
Tested against a live Pod 2 Pro — both auth and API calls work correctly.
Fixes #7, fixes #8, fixes #12
Note on headless/daemon environments: The
99designs/keyringlibrary tries macOS Keychain first, which hangs indefinitely in LaunchAgent/launchd contexts (error -61: "User interaction is not allowed"). For daemon use, consider changingAllowedBackendsto preferFileBackendoverKeychainBackend. This is not included in this PR as it's a broader design decision.