feat(website): route auth through Mystira Identity - #42
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 40c384cf97
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <div className="flex flex-col items-center justify-center gap-4 sm:flex-row"> | ||
| {state === "success" ? ( | ||
| <Link | ||
| href="/integration" |
There was a problem hiding this comment.
Forward successful sign-ins to the app
For the alpha CTAs now routed through /login, a successful callback only stores the token response in this site's sessionStorage and then sends the user to /integration, which is the documentation flow rather than the deployed app; a repo-wide search shows CODEFLOW_SESSION_KEY is only used in auth/oidc.ts, and sessionStorage will not carry over to https://app.codeflow.io. As a result, users who click “Try Alpha Preview” can complete Identity login but the actual CodeFlow app never receives that authenticated state. Redirect to the app with a real handoff, or make the app origin own the callback/session.
Useful? React with 👍 / 👎.
| authorizeUrl.searchParams.set("client_id", CODEFLOW_WEBSITE_CLIENT_ID); | ||
| authorizeUrl.searchParams.set("redirect_uri", redirectUri); | ||
| authorizeUrl.searchParams.set("response_type", "code"); | ||
| authorizeUrl.searchParams.set("scope", "openid profile email offline_access"); |
There was a problem hiding this comment.
Avoid requesting refresh tokens from the browser
When Mystira Identity grants offline_access, this browser-only flow stores the entire token response, including any refresh_token, in sessionStorage even though the site never uses it. In deployments where the identity client is allowed to issue refresh tokens, that creates an unnecessary long-lived credential exposure for any client-side script/XSS on the marketing site; remove offline_access unless there is a protected backend/BFF or refresh-token rotation flow that actually consumes it.
Useful? React with 👍 / 👎.
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughAdds Mystira Identity OIDC authentication with PKCE, new login/signup pages, callback handling, token persistence, and internal navigation from homepage CTAs and the site header. ChangesOIDC authentication
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant AuthStart
participant MystiraIdentity
participant AuthCallback
participant SessionStorage
Visitor->>AuthStart: Click sign in or sign up
AuthStart->>SessionStorage: Store state, nonce, and PKCE verifier
AuthStart->>MystiraIdentity: Redirect to authorization endpoint
MystiraIdentity-->>AuthCallback: Return authorization code and state
AuthCallback->>SessionStorage: Load stored request
AuthCallback->>MystiraIdentity: Exchange code at token endpoint
MystiraIdentity-->>AuthCallback: Return token response
AuthCallback->>SessionStorage: Store token response
AuthCallback-->>Visitor: Show success or error state
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/app/auth/AuthCallback.tsx`:
- Around line 13-44: Update the AuthCallback useEffect to use a useRef guard
that returns when the effect has already started, and mark it before invoking
completeMystiraIdentityFlow. Keep the existing callback validation, exchange,
state updates, and cleanup behavior unchanged so React Strict Mode replay cannot
start a second sign-in attempt.
In `@website/app/auth/oidc.ts`:
- Line 76: Update the OIDC authorization flow around authorizeUrl and
CODEFLOW_SESSION_KEY so refresh tokens are not requested or stored in
browser-readable sessionStorage: remove offline_access from the requested scope
for this browser-only client, unless the flow is migrated to a BFF using
HttpOnly session cookies.
- Around line 116-119: Update completeMystiraIdentityFlow() to validate
tokenResponse.id_token before persisting CODEFLOW_SESSION_KEY. Verify the ID
token signature, issuer, audience, expiry, and generated nonce, and only then
write the session and remove CODEFLOW_AUTH_STATE_KEY; reject invalid tokens
without persisting the session.
- Around line 61-82: Update the authorization URL construction around the
`intent` and `authorizeUrl` symbols so the `prompt` parameter uses the
provider’s registration value (`create`) when `intent` is signup, while
retaining `login` for normal authentication. Keep the persisted
`StoredAuthRequest` behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d49cebd8-0452-46e3-b4a0-2797c2210b76
📒 Files selected for processing (8)
website/app/auth/AuthCallback.tsxwebsite/app/auth/AuthStart.tsxwebsite/app/auth/callback/page.tsxwebsite/app/auth/oidc.tswebsite/app/components/Header.tsxwebsite/app/login/page.tsxwebsite/app/page.tsxwebsite/app/signup/page.tsx
| const storedRequest: StoredAuthRequest = { | ||
| intent, | ||
| state, | ||
| nonce, | ||
| codeVerifier, | ||
| redirectUri, | ||
| createdAt: Date.now(), | ||
| }; | ||
|
|
||
| sessionStorage.setItem(CODEFLOW_AUTH_STATE_KEY, JSON.stringify(storedRequest)); | ||
|
|
||
| const authorizeUrl = new URL("/connect/authorize", MYSTIRA_IDENTITY_BASE_URL); | ||
| authorizeUrl.searchParams.set("client_id", CODEFLOW_WEBSITE_CLIENT_ID); | ||
| authorizeUrl.searchParams.set("redirect_uri", redirectUri); | ||
| authorizeUrl.searchParams.set("response_type", "code"); | ||
| authorizeUrl.searchParams.set("scope", "openid profile email offline_access"); | ||
| authorizeUrl.searchParams.set("code_challenge", codeChallenge); | ||
| authorizeUrl.searchParams.set("code_challenge_method", "S256"); | ||
| authorizeUrl.searchParams.set("state", state); | ||
| authorizeUrl.searchParams.set("nonce", nonce); | ||
| authorizeUrl.searchParams.set("prompt", "login"); | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
What authorization-request parameter does Mystira Identity require to open account registration or a signup screen for an OpenID Connect public client?
💡 Result:
To open the account registration or signup screen within an OpenID Connect authorization request, Mystira Identity—like other standards-compliant OpenID Connect providers—uses the prompt parameter with the value create [1][2]. Specifically, the "Initiating User Registration via OpenID Connect 1.0" specification defines the prompt=create parameter [1]. When a client includes prompt=create in its authorization request, it instructs the OpenID Provider to present the user with an account creation interface rather than the standard login flow [1][2]. After the registration process is completed by the user, the identity provider redirects them back to the client application to finish the authentication flow [1][2].
Citations:
- 1: https://openid.net/specs/openid-connect-prompt-create-1_0.html
- 2: https://duendesoftware.com/blog/20260331-triggering-user-registration-via-openid-connect-with-duende-identityserver
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate file =="
fd -a 'oidc\.ts$' . | sed 's#^\./##'
echo "== inspect target =="
sed -n '1,130p' website/app/auth/oidc.ts
echo "== references to signup/auth flow =="
rg -n "intent|signup|/signup|CREATE|create|prompt|StoredAuthRequest|CODEFLOW_AUTH_STATE_KEY" website/app website || trueRepository: phoenixvc/codeflow-engine
Length of output: 17580
🌐 Web query:
Mystira Identity prompt=create OpenID Connect user registration
💡 Result:
Mystira is an early-childhood developmental observation platform founded in 2025 that utilizes interactive storytelling to assess children [1][2]. Technical documentation and source code references indicate that Mystira operates as an OpenID Connect (OIDC) identity provider, allowing third-party applications to integrate authentication via standard OIDC flows [3][4][5]. For developers implementing user registration or authentication using Mystira as an IdP, the following technical requirements and practices are observed in existing implementations: 1. Authentication Integration: Applications typically integrate Mystira using frameworks like Auth.js v5 [3][5]. The authentication flow replaces legacy credential-based systems (such as bcrypt/JWT) with OIDC-based login, redirecting users to the Mystira platform to complete authentication [3][5]. 2. Account Linking and Security: In internal implementations, account linking is handled by matching the user's email address [4]. To prevent security vulnerabilities such as account takeover or privilege escalation, systems are required to validate the email_verified claim (accepting either boolean true or the string "true") before linking the OIDC identity to a local user account [4]. 3. Future Roadmap: Developers are encouraged to migrate from email-based matching to matching on the immutable sub (subject) claim to improve security and robustness [4]. Because Mystira functions as an OIDC provider, user registration is managed at the Mystira platform level (e.g., via their interactive app/dashboard) rather than through a direct API-based registration prompt in the consuming application [3][1]. Applications trigger the flow by initiating an OIDC sign-in request, which offloads the registration and login process to the Mystira-hosted service [3].
Citations:
- 1: https://www.linkedin.com/posts/mystira_today-mystira-is-live-for-the-past-year-activity-7477626985352069120-yB4E
- 2: https://mystira.app
- 3: neuralliquid/house-of-veritas@312e243
- 4: neuralliquid/house-of-veritas@6f47665
- 5: neuralliquid/house-of-veritas@neuralliquid:9a92985...neuralliquid:0e5a917
Make the signup intent change the IDP prompt.
intent is only persisted, so both login and signup request /connect/authorize?prompt=login. Set prompt to the provider’s registration value for signup—typically prompt=create for OpenID Connect flows—to request account creation rather than login.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/app/auth/oidc.ts` around lines 61 - 82, Update the authorization URL
construction around the `intent` and `authorizeUrl` symbols so the `prompt`
parameter uses the provider’s registration value (`create`) when `intent` is
signup, while retaining `login` for normal authentication. Keep the persisted
`StoredAuthRequest` behavior unchanged.
| authorizeUrl.searchParams.set("client_id", CODEFLOW_WEBSITE_CLIENT_ID); | ||
| authorizeUrl.searchParams.set("redirect_uri", redirectUri); | ||
| authorizeUrl.searchParams.set("response_type", "code"); | ||
| authorizeUrl.searchParams.set("scope", "openid profile email offline_access"); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== locate oidc.ts =="
fd -a 'oidc\.ts$' . | sed 's#^\./##'
echo "== inspect website/app/auth/oidc.ts size and relevant lines =="
if [ -f website/app/auth/oidc.ts ]; then
wc -l website/app/auth/oidc.ts
sed -n '1,160p' website/app/auth/oidc.ts | nl -ba
else
echo "website/app/auth/oidc.ts not found"
fi
echo "== search sessionStorage/refresh token/usages =="
rg -n "offline_access|refresh_token|sessionStorage|authorizeUrl|token" website/app/auth website -g '*.{js,jsx,ts,tsx}' || trueRepository: phoenixvc/codeflow-engine
Length of output: 361
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== inspect website/app/auth/oidc.ts =="
awk '{printf "%5d\t%s\n", NR, $0}' website/app/auth/oidc.ts | sed -n '1,140p'
echo "== search secure/session storage token usages =="
rg -n "offline_access|refresh_token|sessionStorage|authorizeUrl|token" website/app/auth website -g '*.{js,jsx,ts,tsx}' || true
echo "== git diff stat/name (if available) =="
git diff --stat website/app/auth/oidc.ts || trueRepository: phoenixvc/codeflow-engine
Length of output: 9314
Do not retain OIDC refresh tokens in browser-readable storage.
website/app/auth/oidc.ts:76 requests offline_access, and website/app/auth/oidc.ts:116-117 stores the complete token response in sessionStorage via CODEFLOW_SESSION_KEY, exposing refresh tokens to same-origin XSS. Use a BFF with HttpOnly session cookies, or omit offline_access for this browser-only client.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/app/auth/oidc.ts` at line 76, Update the OIDC authorization flow
around authorizeUrl and CODEFLOW_SESSION_KEY so refresh tokens are not requested
or stored in browser-readable sessionStorage: remove offline_access from the
requested scope for this browser-only client, unless the flow is migrated to a
BFF using HttpOnly session cookies.
40c384c to
1f2141d
Compare
Summary
Validation
Note: Next build exits 0 and exports the static routes. It still reports the existing ESLint config warning: cannot find package @eslint/eslintrc from website/eslint.config.mjs.
Summary by CodeRabbit