fix(auth): persist pending OAuth across process death (closes #15) #32
No reviewers
Labels
No labels
auth
bug
composer
correctness
database
durability
enhancement
feeds
interactions
notifications
performance
reliability
safety
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
starsetbyte/peregrine!32
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/oauth-pending-auth-process-death"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
The in-flight OAuth authorization — DPoP key pair, PKCE code verifier, CSRF
state, and discovery info — was held only in memory: onAtProtoOAuthClient(a@Singleton) and duplicated aspendingStateonOAuthLoginViewModel. Android routinely kills the app while the user is authorizing in the browser (a heavy foreground Custom Tab). On return, both were gone andcompleteLoginfailed withNo pending auth, forcing a full restart (issue #15).Fix
New
PendingAuthStorepersists the pending request toEncryptedSharedPreferences(master key in the Android Keystore, mirroringOAuthSessionStore) before the browser opens, andcompleteLoginrestores it when the in-memory copy is missing.beginLoginsaves (synchronouscommit()) inside its IO block.completeLogin(redirectUrl)drops the caller-suppliedexpectedState: it restores pending (memory → disk) and validates the redirect'sstateagainstpending.state. This also removes the ViewModel's separately-lostpendingStatefield entirely — the persisted state is now the single source of truth for CSRF validation.Tests
PendingAuthStoreTest— encode/decode round-trip preserves all fields and the EC key pair (so the token-exchange DPoP proof still matches thedpop_jktfrom PAR). Written test-first.AtProtoOAuthClientTest— new process-death test: begin login, null out the in-memory pending, stub the store to return it, and assertcompleteLoginrestores and succeeds (and clears the store).AtProtoOAuthClient/OAuthLoginViewModel/NetworkModulewiring tests for the new signatures.Full unit suite green except the pre-existing, unrelated
NotificationsRepositoryTestfailure (fixed in PR #29).Follow-up (out of scope)
Hardware-backed, non-exportable DPoP keys (generated in the Keystore) for both the pending and session stores — today both export the EC key into encrypted prefs. The issue mentions "private key behind Android Keystore"; this PR matches the existing session-key handling (encrypted-at-rest via the Keystore master key) rather than diverging the two key paths in one change.
🤖 Generated with Claude Code
https://claude.ai/code/session_0151pbAku7AeA9fgzWKVQLPE