feat(sdk): take explicit consent and send a login or signup intent - #1922
Conversation
A login, signup or consent rejection on the callback path now carries the strategy the flow came through, and AuthCallback sets it on the connect error as the frontier-auth-strategy header. The client cannot know it otherwise: an OIDC callback names no strategy, and the flow row is gone by the time the RPC answers. The header is added to the CORS exposed list so a browser client on another origin can read it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 SummarySummary by CodeRabbit
WalkthroughThe change adds strategy-aware authentication rejections, structured API error details, callback rejection routing, SDK error classification, signup consent handling, and strategy-specific error rendering. ChangesAuthentication rejection and consent flow
Priority: ➖ Normal Estimated code review effort: 4 (Complex) | ~60 minutes Merge Risk: 🟡 Moderate · up to This change adds consent-gated signup and clearer authentication errors, but consent updates may be accepted without renewed acknowledgement and some authentication failures may show inconsistent or raw server messages. Resolve these issues before merge. 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
Thanks 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 34334936529Coverage increased (+0.02%) to 50.229%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Opening or submitting the email form now tells the owning view, which drops whatever rejection another button is showing, the same way clicking an OIDC button already did. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Advanced
Run ID: 965d07ca-bdab-4a90-a783-d95ece3e4aff
⛔ Files ignored due to path filters (1)
web/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (25)
core/authenticate/authenticate.gocore/authenticate/service.gocore/authenticate/service_test.gointernal/api/v1beta1connect/authenticate.gointernal/api/v1beta1connect/authenticate_test.gopkg/server/connect_interceptors/cors.gopkg/server/consts/gateway.goweb/apps/client-demo/src/hooks/useAuthRejection.tsweb/apps/client-demo/src/pages/Callback.tsxweb/apps/client-demo/src/pages/Login.tsxweb/apps/client-demo/src/pages/Signup.tsxweb/sdk/client/components/auth-consent/auth-consent.module.cssweb/sdk/client/components/auth-consent/auth-consent.tsxweb/sdk/client/components/auth-consent/index.tsweb/sdk/client/components/auth-error/auth-error.tsweb/sdk/client/components/auth-error/index.tsweb/sdk/client/components/auth-oidc-button/auth-oidc-button.tsxweb/sdk/client/index.tsweb/sdk/client/views/auth/magic-link-verify/magic-link-verify-view.module.cssweb/sdk/client/views/auth/magic-link-verify/magic-link-verify-view.tsxweb/sdk/client/views/auth/magic-link/magic-link-view.module.cssweb/sdk/client/views/auth/magic-link/magic-link-view.tsxweb/sdk/client/views/auth/sign-in/sign-in-view.tsxweb/sdk/client/views/auth/sign-up/sign-up-view.tsxweb/sdk/package.json
💤 Files with no reviewable changes (2)
- web/sdk/client/views/auth/magic-link/magic-link-view.module.css
- web/sdk/client/views/auth/magic-link-verify/magic-link-verify-view.module.css
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Advanced
Run ID: 4a013ffd-ab63-4451-986a-326b90e8a51a
📒 Files selected for processing (3)
internal/api/v1beta1connect/authenticate.gointernal/api/v1beta1connect/authenticate_test.goweb/sdk/client/components/auth-error/auth-error.ts
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.
469ea3c to
9ec8e82
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Advanced
Run ID: 22132699-4b2c-449f-9271-3af4211be6dc
📒 Files selected for processing (3)
internal/api/v1beta1connect/authenticate.gointernal/api/v1beta1connect/authenticate_test.goweb/sdk/client/components/auth-error/auth-error.ts
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.
…trategy detail A callback rejection now attaches an AuthStrategy error detail naming the strategy the flow came through. AuthStrategy is the message ListAuthStrategies already serves, so the SDK reads the detail with the type it renders buttons from, through connect's findDetails, and needs no new proto. This replaces the frontier-auth-strategy header, which needed CORS exposure and carried no structure, and follows the organization delete guard, which attaches a PreconditionFailure the same way. Flow-start rejections attach nothing: the client named the strategy itself. Without a detail the SDK falls back to the group message as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
9ec8e82 to
3c288d8
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/sdk/client/components/auth-error/auth-error.ts (1)
84-84: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse the SDK-owned invalid-request message.
This branch returns backend text instead of
AUTH_ERROR_MESSAGES.invalid_request. Use the mapped message so invalid-request errors follow the new SDK-owned error contract.Proposed fix
- return { kind: 'invalid_request', message: rawMessage, strategy }; + return { + kind: 'invalid_request', + message: AUTH_ERROR_MESSAGES.invalid_request, + strategy + };
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Advanced
Run ID: 277009ae-2e30-436d-97f9-b9796e6bc91f
📒 Files selected for processing (3)
internal/api/v1beta1connect/authenticate.gointernal/api/v1beta1connect/authenticate_test.goweb/sdk/client/components/auth-error/auth-error.ts
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.
AuthConsent had one consumer. The label helpers, the ConsentLabel type and the checkbox markup now live in the sign-up view, and its one CSS rule in the view's module. No export changes: the component was never part of the public index. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The sign-in and sign-up views only ever turned the kind back into a message and a strategy, so they now take that pair directly as `error`, and `errorStrategy` is gone. `describeAuthError` returns the same pair; the kind type, its guard and the message lookup leave the public API, and the OTP view branches on the connect code instead. The client demo routes a callback rejection by its connect code and hands the result of `describeAuthError` straight to the view. Comments this branch had added to the frontend are dropped. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
It is a mapping from a connect error to copy, with no React in it, so it sits with the other client utils and is imported by path like them. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
Restores the consent checkbox as its own component, placed in the sign-up folder and imported only from there, instead of inlined into the view. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
The callback rejection seeds the same state a click writes, so one error is on screen at a time and no flag is needed to hide the first. The strategies are filtered once and mapped once, with the email form rendered where mailotp sits in the list, so a rejection attaches by name alone. The group message is a standalone Field.Error. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
A stable sort after the filter demotes mailotp and leaves the rest in the server's order, so the form's position no longer depends on it. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
A standalone Field.Error throws at runtime because Base UI requires the field root context, so the group message goes back to an empty Field with the error prop. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
The list comes out of the query's select instead of a memo over the response, and defaults to empty while it loads. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
…logic MAIL_OTP_STRATEGY replaces the literal in the four auth views, and visibleAuthStrategies holds the exclude filter and the sort that keeps the email form last, so both page views call the same function. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
…views isUnknownError replaces the derived groupError string, and the Field reads the message off authError directly. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
The magic link view showed the error prop through a local dismissed flag, but both owners already clear the error through onActivate on the same events, so the flag and the derived value go and the prop is read directly. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
A long address wraps the sentence onto two lines, and a wrapped block takes the full container width with its text left-aligned. Centering the text keeps it in line with the title either way. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
No flow can hand the email form a rejection from outside: the OTP email carries only the code, so a mailotp rejection at the callback surfaces in the verify view, and in-view failures land on the email field. The page views inline the per-button error and stop counting mailotp as a button a rejection can attach to. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
The callback page passes the rejection as navigation state and the login and signup pages read it from the location, replacing the in-memory handoff hook. Claude-Session: https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA
Summary
docs/rfcs/0002-explicit-consent-at-signup.md:SignInViewsends a login intent,SignUpViewsends a signup intent, and the sharedMagicLinkViewtakes the intent as a prop.SignUpViewfetchesListConsentDocumentsand gates every sign-up control behind an Apsara checkbox whose label links each document; an empty list renders today's view unchanged. AconsentLabelprop overrides the copy.describeAuthError, which maps theNotFound,AlreadyExistsandFailedPreconditionrejections to SDK-owned copy and returns it with the strategy the rejection came through. Both views take that pair aserrorand render it under the strategy button that caused it, on the email field, or as a message for the whole group; the OTP view uses the same copy. Both views also stop swallowingauthenticatefailures intoconsole.error.AuthCallbackattaches it to the connect error as araystack.frontier.v1beta1.AuthStrategyerror detail, which the SDK reads withfindDetails. An OIDC callback names no strategy and the flow row is gone by then, so this is the only place the client can learn it. No proto change.@raystack/protonis bumped to the build that carries the consent proto.https://claude.ai/code/session_01SwGUtD3mDrTjM1rLWnxrnA