feat(authenticate): map the gate and consent errors on both auth RPCs - #1915
Draft
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Coverage Report for CI Build 33429109741Coverage increased (+0.2%) to 50.11%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
rohanchkrabrty
marked this pull request as draft
August 31, 2026 10:36
This was referenced Aug 31, 2026
The two request fields reach StartFlow, and the three rejections become legible to a client instead of arriving as a 500. Authenticate calls sessionutils.ExtractSessionMetadata itself for the IP. Authenticate and AuthCallback are both on the authentication skip list, so nothing puts session metadata on the context. That helper parses the user agent into an OS and a browser family and drops the raw string, so what reaches the consent record is the IP and nothing else, from when the user accepted rather than from the callback. ErrLoginUserNotFound maps to NotFound, ErrSignupUserExists to AlreadyExists and ErrConsentRequired to FailedPrecondition, from both RPCs. All three had to join the errors AuthCallback handles explicitly, which maps a fixed list to a 4xx and everything else to Internal; they keep their own codes rather than the InvalidArgument the rest of that list gets, because FailedPrecondition is what lets a client separate a consent rejection from a bad code or an expired flow. Both RPCs answer with a code rather than a redirect. Frontier serves no route for the callback URL: it points at a page the application hosts, and that page is what calls AuthCallback over connect, so it already holds the rejection and decides where the user goes next. Handing it a location header instead would oblige every such page to be written to read one, and a 2xx carrying that header reads as success to one that is not. The three codes are distinct, so a client tells the rejections apart without a second vocabulary alongside them. StartFlow gains the consent half of the flow start gate, so a rejection lands before an OTP is sent and before the browser leaves for an identity provider. A signup intent runs ResolveAll there; an unspecified intent runs Resolve, which still catches an unknown id before the redirect while completeness waits for user creation; a login intent checks nothing, because it writes no record. Ids sent with a login intent are rejected by the handler as InvalidArgument, since accepting them silently would leave a client believing it recorded a consent that does not exist. With app.consent disabled both Resolve and ResolveAll resolve nothing and reject nothing, so the ids are ignored rather than rejected and one client build works against both kinds of deployment. Rendering any of this is a separate frontend change: the sign-in and sign-up views for what Authenticate returns, and the callback page for what AuthCallback returns. Refs docs/rfcs/0002-explicit-consent-at-signup.md, Enforcement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hf1XuPCWcHZ7QY5u4WBB2G
rohanchkrabrty
force-pushed
the
feature/featauthenticate-map-the-gate-and-consent-errors-on-both
branch
from
August 31, 2026 19:10
bb8970c to
8e30f1a
Compare
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.
Summary
ErrLoginUserNotFound→NotFound,ErrSignupUserExists→AlreadyExists,ErrConsentRequired→FailedPrecondition, from bothAuthenticateandAuthCallback. All three had to join the fixed listAuthCallbackhandles explicitly, or they fall through toInternalby construction.FailedPreconditionspecifically, because the other errors on that list are allInvalidArgument— the client sent something wrong and resending will not help. A consent rejection is the opposite: the request was well formed and the client can fix it by asking the user to accept what is missing. That distinction is what a client cannot recover from a message string.AuthCallbackover connect, so it already holds the rejection and decides where the user goes. A redirect was implemented first and withdrawn: nothing follows the location header, and a 2xx carrying it reads as success to a client that is not looking for it.StartFlowgains the consent half of the flow-start gate, so a signup rejection lands before an OTP is sent and before the browser leaves for the provider. An unspecified intent checks only that the ids are known, since completeness is not yet knowable; a login intent checks nothing, because it writes no record.app.consentdisabled ids are ignored rather than rejected, which is content and handled one layer down. Rendering any of this is a separate frontend change.