Repository navigation
Check the whole OAuth redirect and revoke tokens on sign-out - #43
Merged
Merged
Conversation
The callback was accepted when only its scheme matched the registered redirect. It now must match scheme, host, port and path (loopback localhost and 127.0.0.1 count as one host), carry the state of the pending attempt, and arrive within 10 minutes. The redirect that carries the attempt's state consumes it, so a replayed or duplicate callback finds nothing; the code is exchanged with the redirect URI the attempt started with. A user's sign-out now also revokes the refresh and access tokens at Adobe IMS (POST /ims/revoke with the client id, as documented for public Native App credentials), in the background with a 10 s deadline. Failures are ignored and never hold sign-out up; a sign-out after Adobe rejected the refresh token revokes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
What changes
Before:
AuthManager.isRedirect). The state check stopped a redirect the app never started, but the same attempt (state + PKCE verifier) stayed valid until the next sign-in, with no age limit.After:
localhostand127.0.0.1are the same host, since the listener only ever reports127.0.0.1), and it must carry the state of the pending sign-in. The redirect carrying that state uses the attempt up straight away, before the code exchange, so a replayed or duplicate redirect finds nothing. An attempt older than 10 minutes is refused with "Sign-in expired, please retry." The code is exchanged with the redirect URI the attempt started with.How
core/auth/OAuthCallback.kt:matchesparses the URIs withjava.net.URI, so it is pure JVM and unit-tested;checkdecides code or failure for a callback against aPendingSignIn;revokeTokenspostsclient_idandtokento/ims/revoke.POST https://ims-na1.adobelogin.com/ims/revoke.client_idis required for "OAuth Native App" and the other public credentials, and only Web credentials send Basic auth, so there is no secret. Both parameters go in the form body, which the docs recommend for sensitive data. The docs don't say whether revoking the refresh token also kills access tokens, so both are revoked.AuthManagerstores the attempt's redirect URI and start time next to the verifier and state (under a lock), takes the pending attempt atomically incompleteRedirect, and gets the app scope for the background revocation.Checks
gradlew.bat :app:testDebugUnitTest :app:verifyRoborazziDebug :app:lintDebugon the workstation: 248 tests, only the 2 known Windows-only screenshot differences (notifications,tabletNotifications); lint "No issues found".OAuthCallbackTesthas 18 new tests: scheme, host, port and path mismatches, default ports, loopback host equivalence, state, expiry, Adobe errors, the revocation request (client id, no Authorization header, tokens de-duplicated) and revocation that never throws.🤖 Generated with Claude Code