fix(auth): sign in through the cookie flow so Pubky Ring can read the deeplink - #128
Conversation
Sign-in has been broken in prod since #125. Pubky Ring answers "Unrecognized format. Expected a recovery phrase, invite code, auth URL, or session request" and the flow ends there. The bindings bump did not change our call, but it changed what the call does. `start_auth_flow` in the fork now delegates to `start_grant_auth_flow`, and pubky 0.10 renders a grant flow as a new intent: before pubkyauth://signin?caps=…&relay=…&secret=… after pubkyauth://signin_grant?caps=…&relay=…&secret=…&cid=…&cpk=… No released Ring can read that. Every one of them bundles react-native-pubky@0.13.0 — pubky 0.9.x, whose generated binding has zero Grant/Cookie symbols — and its parser knows `signin`, `signup`, `direct_signup` and `session`, nothing else. An unknown intent falls through to `InputAction.Unknown`, which is the message above. Ring is fixing its half on `chore/pubky-0.10.0`, but that branch is unmerged and unbuildable: it calls `signUp(secret, homeserver, token, clientId)` while its own lockfile still resolves 0.13.0, whose binding takes three arguments. npm's latest react-native-pubky is 0.13.0. So this is not one Ring release away, and Loopky cannot wait behind it. The fork still exports the pre-bump flow as `start_cookie_auth_flow` / `await_cookie_auth_approval`, already present in the checked-in bindings on both platforms. It emits exactly the URL Loopky sent before #125, and its session secret is the `session_secret` the rest of this client already expects. Nothing else about #125 is touched — the TLS fixes it was actually for are untouched, and `ClientId` still travels on sign-in, sign-up and secret-key writes. Upstream deprecates the cookie flow, so this is a hold, not a destination. Revisit when a Ring release ships on pubky 0.10. Verified on the Pixel_9 emulator against Ring 1.17 (the released build), debug Loopky, account Cosmic-Crystal-Panda: authUrl=pubkyauth://signin?caps=%2Fpub%2Floopky%2F%3Arw,… Ring parsed it, listed /pub/loopky/ and /pub/pubky.app/ as READ,WRITE on its Authorize screen, and approved. Loopky then logged "complete: got session payload={capabilities, pubky, session_secret=…}", saved the session, wrote its loopky-user self-tag to the homeserver, and loaded a 5-deck library — so the returned secret works for both reads and writes, not just for signing in. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The FFI names the session secret after the flow that minted it — `session_secret` from the cookie flow, `grant_secret` from the grant flow pubky 0.10 added. The parser only knew the first, so a grant payload died on `session payload missing 'session_secret'`. That was live until the previous commit and would have been the *second* failure in the same sign-in: even a grant-capable Ring approving the deeplink would have left Loopky throwing here, one layer past anything the error names. It is dormant now and stays dormant while we ask for the cookie flow, which is the argument for the alias rather than against it — whoever switches back to grant auth should not have to rediscover this. The two values are interchangeable downstream: the FFI's `restore_session` sniffs which kind of token it was handed, so `signOut`, `revalidateSession` and the `*_with_session` writes all take either. Tests cover both spellings and the genuinely-absent case, which had no coverage at all before. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Verified on a real phone, release buildRedmi Note 11 / Android 13 / MIUI, Ring 1.18 (newest release — still pins Signed in end to end: A rollout note that matters more than the fixThe first attempt on that phone still showed the old error, and it was not this branch's fault. Loopky and Ring were looking at two different URLs:
Android stores the launching URL as the base intent of Ring's task with So a user who hit this bug can see the same error once after updating, until Ring's task is gone — and the obvious reaction (reinstall and retry) never clears it. Worth a line in the release notes: if sign-in still fails after updating, close Pubky Ring from recents and try again. Unrelated MIUI detail for anyone testing on that device: Bitkit also registers |
The bug
Sign-in has been broken in prod since #125. Ring answers "Unrecognized format. Expected a recovery phrase, invite code, auth URL, or session request" and the flow ends there.
#125 didn't change our call, but it changed what the call does. The fork's
start_auth_flownow delegates tostart_grant_auth_flow, and pubky 0.10 renders a grant flow as a new intent:No released Ring can read that. Every one bundles
react-native-pubky@0.13.0— pubky 0.9.x, whose generated binding has zero Grant/Cookie symbols — and its parser knowssignin,signup,direct_signup,sessionand nothing else. An unknown intent falls through toInputAction.Unknown, which is that message.Why not just wait for Ring
Ring is fixing its half on
chore/pubky-0.10.0, but it's unmerged and currently unbuildable: it callssignUp(secret, homeserver, token, clientId)while its own lockfile still resolves0.13.0, whose binding takes three arguments. npm's latestreact-native-pubkyis0.13.0— nothing carrying pubky 0.10 has been published. The chain is rn-pubky publishes → Ring merges → Ring releases, and users' installed Ring is behind all three.The change
The fork still exports the pre-bump flow as
start_cookie_auth_flow/await_cookie_auth_approval, already in the checked-in bindings on both platforms. It emits exactly the URL Loopky sent before #125, and returns thesession_secretthe rest of the client already expects.Nothing else about #125 is touched — the TLS fixes it was actually for stay, and
ClientIdstill travels on sign-in, sign-up and secret-key writes. Upstream deprecates the cookie flow, so this is a hold, not a destination; revisit when a Ring release ships on pubky 0.10. Both the interface KDoc and the commit say so.Second commit fixes a bug that was live until the first one and would have been the next failure in the same sign-in: the grant flow returns its secret as
grant_secret, andSessionPayloadParseronly knewsession_secret. Even a grant-capable Ring would have left us throwingsession payload missing 'session_secret'one layer past anything the error names.Verification
On the Pixel_9 emulator against Ring 1.17 — the released build, debug Loopky, account Cosmic-Crystal-Panda:
Ring parsed it, listed
/pub/loopky/and/pub/pubky.app/as READ,WRITE on its Authorize screen, and approved. Loopky loggedcomplete: got session payload={capabilities, pubky, session_secret=…}, saved the session, wrote itsloopky-userself-tag to the homeserver and loaded a 5-deck library — so the secret works for reads and writes, not just for signing in.:shared:allTests,detektAllandlintSwiftall green. iOS is the same two-line swap but is unverified, as ever.🤖 Generated with Claude Code