Skip to content

fix: login after guest flow#8945

Open
mikeallisonJS wants to merge 4 commits intomainfrom
26-00-MA-fix-guest-login-workflow-firebase
Open

fix: login after guest flow#8945
mikeallisonJS wants to merge 4 commits intomainfrom
26-00-MA-fix-guest-login-workflow-firebase

Conversation

@mikeallisonJS
Copy link
Copy Markdown
Collaborator

@mikeallisonJS mikeallisonJS commented Apr 2, 2026

Summary by CodeRabbit

  • New Features

    • Journey transfer from guest accounts: guests can transfer journeys to an authenticated user and assign them to a team.
    • Enhanced sign-in flows: guest journey state is preserved and used during email/password and social sign-ins; server-side transfer support added.
    • Automatic team assignment: transferred journeys default to the user's managed team when none is chosen.
  • Tests

    • Added comprehensive tests covering transfer flows, auth edge cases, and pending-guest persistence.

@mikeallisonJS mikeallisonJS requested a review from Kneesal April 2, 2026 00:47
@mikeallisonJS mikeallisonJS self-assigned this Apr 2, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

Warning

Rate limit exceeded

@mikeallisonJS has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 0 minutes and 17 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 17 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 8896c356-9f41-4751-8c7a-291a64cb9929

📥 Commits

Reviewing files that changed from the base of the PR and between 1259fb3 and 95590b8.

📒 Files selected for processing (3)
  • apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.ts
  • apps/journeys-admin/pages/templates/[journeyId]/customize.tsx
  • apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.ts

Walkthrough

Adds a guest-to-authenticated-user journey transfer: GraphQL mutation and resolver for transferring anonymous-owned journeys, frontend sessionStorage helpers to persist pending guest journeys, and sign-in flow changes to trigger transfers and redirect with a transfer flag.

Changes

Cohort / File(s) Summary
GraphQL Schemas
apis/api-gateway/schema.graphql, apis/api-journeys-modern/schema.graphql
Added journeyTransferFromAnonymous(journeyId: ID!, teamId: ID): Journey! mutation to gateway and journeys-modern schemas (mapped to API_JOURNEYS_MODERN).
Journeys Backend
apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.ts, apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.spec.ts, apis/api-journeys-modern/src/schema/journey/index.ts
Implemented resolver with ownership checks, team resolution fallback, transaction updates, old-team cleanup, and comprehensive tests; imported new mutation into journey schema index.
Pending Guest Journey Lib
apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.ts, apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.spec.ts, apps/journeys-admin/src/libs/pendingGuestJourney/index.ts
New sessionStorage-based API: setPendingGuestJourney, getPendingGuestJourney, clearPendingGuestJourney plus tests and re-export index.
Sign-In Components & Tests
apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.tsx, .../PasswordPage.spec.tsx, .../RegisterPage/RegisterPage.tsx, .../RegisterPage/RegisterPage.spec.tsx, .../SignInServiceButton/SignInServiceButton.tsx, .../SignInServiceButton/SignInServiceButton.spec.tsx
Detect and handle pending guest journeys during sign-in/register/provider flows: sign out anonymous before sign-in, handle credential-already-in-use via OAuth credential fallback, obtain ID token and call login(idToken), and redirect to sign-in with transfer param when pending journey exists. Tests updated/added accordingly.
Sign-In Redirect Utility
apps/journeys-admin/src/components/SignIn/utils/addTransferParam/addTransferParam.ts, .../addTransferParam.spec.ts, .../index.ts
Added addTransferParam utility to append/update transfer=true on redirect URLs with tests and re-export.
Customize Page & Guest Preview
apps/journeys-admin/pages/templates/[journeyId]/customize.tsx, apps/journeys-admin/src/components/TemplateCustomization/.../GuestPreviewScreen/GuestPreviewScreen.tsx
Persist pending guest journey before redirecting to sign-in, client-side call to journeyTransferFromAnonymous when pending journey present; server-side getServerSideProps will call mutation when transfer=true query param is present (errors swallowed into diagnostics).
Email Verification Redirects
apps/journeys-admin/pages/users/verify.tsx
Replaced inline redirect objects with delegated redirectToApp(ctx) for verified/validated paths.

Sequence Diagram

sequenceDiagram
    participant Guest as Guest User
    participant GuestUI as GuestPreviewScreen
    participant Store as sessionStorage
    participant SignIn as Sign-In Flow (Client)
    participant Auth as Firebase Auth
    participant Frontend as Customize Page
    participant Backend as journeyTransferFromAnonymous
    participant DB as Prisma/Database

    Guest->>GuestUI: Click "Continue with account"
    GuestUI->>Store: setPendingGuestJourney(journeyId, originalTemplateId)
    GuestUI->>SignIn: Redirect to /users/sign-in

    SignIn->>Auth: signInWithEmailAndPassword / provider link
    Auth-->>SignIn: UserCredential
    SignIn->>Store: getPendingGuestJourney()
    Store-->>SignIn: pending journey

    alt pending exists
        SignIn->>Auth: getIdToken()
        Auth-->>SignIn: idToken
        SignIn->>Frontend: login(idToken)
        SignIn->>Guest: Redirect to /templates/[journeyId]/customize?transfer=true
    end

    Guest->>Frontend: Load customize page
    Frontend->>Store: getPendingGuestJourney()
    Store-->>Frontend: pending journey
    Frontend->>Backend: journeyTransferFromAnonymous(journeyId, teamId?)
    Backend->>DB: fetch journey, owner, userTeams
    Backend->>DB: validate anonymous owner, resolve target team
    Backend->>DB: begin transaction (delete/create userJourney, update journey.teamId, cleanup empty team)
    DB-->>Backend: updated journey
    Backend-->>Frontend: return journey
    Frontend->>Store: clearPendingGuestJourney()
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the pull request: implementing a login flow after a guest journey transfer, which is the core change across authentication and journey transfer features.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 26-00-MA-fix-guest-login-workflow-firebase

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Warnings
⚠️ ❗ Big PR (1204 changes)

(change count - 1204): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against 95590b8

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Apr 2, 2026

View your CI Pipeline Execution ↗ for commit 95590b8

Command Status Duration Result
nx run watch-e2e:e2e ✅ Succeeded 19s View ↗
nx run resources-e2e:e2e ✅ Succeeded 14s View ↗
nx run journeys-admin-e2e:e2e ✅ Succeeded 28s View ↗
nx run journeys-e2e:e2e ✅ Succeeded 18s View ↗
nx run short-links-e2e:e2e ✅ Succeeded 3s View ↗
nx run player-e2e:e2e ✅ Succeeded 3s View ↗
nx run videos-admin-e2e:e2e ✅ Succeeded 4s View ↗
nx run-many --target=vercel-alias --projects=jo... ✅ Succeeded 2s View ↗
Additional runs (20) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2026-04-02 01:26:32 UTC

@github-actions github-actions bot temporarily deployed to Preview - journeys April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - short-links April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - watch April 2, 2026 00:49 Inactive
@github-actions github-actions bot temporarily deployed to Preview - player April 2, 2026 00:49 Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys ✅ Ready journeys preview Thu Apr 2 14:21:02 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
short-links ✅ Ready short-links preview Thu Apr 2 14:20:42 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
player ✅ Ready player preview Thu Apr 2 14:21:24 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
videos-admin ✅ Ready videos-admin preview Thu Apr 2 14:20:50 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
resources ✅ Ready resources preview Thu Apr 2 14:20:55 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
watch ✅ Ready watch preview Thu Apr 2 14:20:58 NZDT 2026

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

The latest updates on your projects.

Name Status Preview Updated (UTC)
journeys-admin ✅ Ready journeys-admin preview Thu Apr 2 14:22:53 NZDT 2026

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/journeys-admin/pages/users/verify.tsx (1)

258-263: ⚠️ Potential issue | 🔴 Critical

destination is undefined — this will crash at runtime.

The variable destination on line 261 is never defined. When a user clicks the email verification link with valid email and token query params, the VALIDATE_EMAIL mutation succeeds, then this code path executes and throws a ReferenceError.

It appears the computation of destination was removed during the refactor to use redirectToApp, but this redirect block was not updated accordingly.

🐛 Proposed fix: use redirectToApp or compute destination

Option 1 — Use redirectToApp consistently (preferred if it handles the redirect query param):

       await apolloClient.mutate({
         mutation: VALIDATE_EMAIL,
         variables: { email, token }
       })
-      return {
-        redirect: {
-          permanent: false,
-          destination
-        }
-      }
+      return redirectToApp(ctx)

Option 2 — Define destination if custom redirect logic is needed:

   if (email != null && token != null) {
     try {
       await apolloClient.mutate({
         mutation: VALIDATE_EMAIL,
         variables: { email, token }
       })
+      const redirectPath = typeof ctx.query.redirect === 'string' ? ctx.query.redirect : '/'
       return {
         redirect: {
           permanent: false,
-          destination
+          destination: redirectPath
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/journeys-admin/pages/users/verify.tsx` around lines 258 - 263, The code
returns a redirect using an undefined variable destination after the
VALIDATE_EMAIL mutation; fix by replacing the redirect block to use the existing
redirect helper or compute destination explicitly: call redirectToApp(...) (the
same helper used elsewhere) to produce the destination or derive destination
from the request's redirect query param with a safe fallback (e.g., home path)
and then return that value in the redirect object; update the code paths around
VALIDATE_EMAIL and the verify handler so redirectToApp or the computed
destination is used instead of the undefined destination variable.
🧹 Nitpick comments (2)
apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.spec.ts (1)

232-260: Add the anonymous-owner/same-team regression case.

This suite only exercises the no-op branch when the authenticated user already owns the journey. A case where journey.teamId === resolvedTeamId but ownership is still anonymous would catch the ownership-transfer short-circuit in the resolver and keep that fix from regressing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.spec.ts`
around lines 232 - 260, Add a new spec in
journeyTransferFromAnonymous.mutation.spec.ts that covers the regression where
the journey.teamId equals the resolvedTeamId but the owner is still anonymous:
mock prismaMock.journey.findUnique to return a journey with teamId
'targetTeamId' and userJourneys either empty or containing an anonymous owner
(no userId), ensure prismaMock.userTeam.findFirst resolves to mockUserTeam and
prismaMock.journey.findUniqueOrThrow returns { id: 'journeyId', teamId:
'targetTeamId' }, then call authClient with JOURNEY_TRANSFER_MUTATION and assert
result.errors is undefined, result.data?.journeyTransferFromAnonymous.id ===
'journeyId' and prismaMock.$transaction was not called to validate the no-op
short-circuit for anonymous-owner/same-team.
apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx (1)

188-221: Cover the pending-guest redirect branch in this new anonymous-user block.

PasswordPage now has a second post-login path when getPendingGuestJourney() is non-null, but this test only exercises the null case. Add one case that returns a pending journey and asserts the redirect sent back through /users/sign-in includes transfer=true; otherwise the guest-login path can regress without a failing spec.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx`
around lines 188 - 221, Add a new test case alongside the anonymous-user spec
that mocks getPendingGuestJourney to return a non-null pending journey (e.g., {
id: 'pending-1' }) before triggering the sign-in flow in PasswordPage; reuse
mockGetFirebaseAuth, mockSignInWithEmailAndPassword and mockFirebaseSignOut,
then after the sign-in completes assert the component performs the guest
redirect by checking the navigation/redirect target includes "/users/sign-in"
with the query "transfer=true" (assert whichever redirect helper your tests use
— history.push, window.location.assign, or router mock — receives the URL with
transfer=true).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.ts`:
- Around line 23-29: The resolver uses prisma.journey.findUnique without
filtering out soft-deleted rows so a deleted journey can be claimed; update the
initial prisma.journey.findUnique call in
journeyTransferFromAnonymous.mutation.ts to use a where object that includes
deletedAt: null (e.g., where: { id: journeyId, deletedAt: null }), and then
propagate that same deletedAt: null guard to every subsequent Prisma access for
the same journey (any prisma.journey.update / findUnique / findFirst / count
calls and related reads such as blocks or userJourneys) so all later journey
read/update/count paths respect soft deletes.
- Around line 105-110: The early return inside the journey.teamId ===
resolvedTeamId branch short-circuits the transfer flow and skips removing the
anonymous owner row and creating the new owner row; remove this early return and
ensure the transfer logic still runs even when journey.teamId equals
resolvedTeamId: after fetching the journey via prisma.journey.findUniqueOrThrow
(the block that currently returns) continue to execute the owner-transfer steps
(delete the anonymous owner row and insert the new owner row) before returning
the journey so the guest ownership is properly removed and the authenticated
user is added as owner.
- Around line 138-142: The current transaction swallows errors from
tx.team.delete by using .catch(() => undefined), which can leave userTeam rows
deleted while the team remains; remove the .catch and let tx.team.delete throw
so the surrounding transaction in journeyTransferFromAnonymous (the block that
checks remainingJourneys === 0 and calls tx.userTeam.deleteMany and
tx.team.delete) will roll back on failure — ensure both operations run inside
the same tx and do not suppress errors from tx.team.delete so the transaction
fails atomically.

In `@apps/journeys-admin/pages/templates/`[journeyId]/customize.tsx:
- Around line 135-152: The effect currently consumes the pending guest marker
unconditionally; change it to first verify pending.journeyId === data.journey.id
and that user is authenticated, then call transferJourney and only on successful
resolution (no catch failure) set transferAttempted.current = true and call
clearPendingGuestJourney(); do not clear the marker nor set transferAttempted on
mutation failure or if the journey IDs don’t match so the pending marker can be
retried later. Ensure you still use getPendingGuestJourney and
clearPendingGuestJourney, and call transferJourney({ variables: { journeyId:
data.journey.id } }) from inside the guarded branch.

In `@apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.tsx`:
- Around line 65-72: The redirect back to /users/sign-in must include the
transfer marker: import addTransferParam (from ../utils/addTransferParam) and
wrap the computed redirectUrl with addTransferParam before encoding and
assigning to window.location.href; update the branch that handles
getPendingGuestJourney()/credential.user.getIdToken()/login(...) to compute
redirectUrl (existingRedirect ?? `/templates/${pending.journeyId}/customize`),
then call addTransferParam(redirectUrl) and use encodeURIComponent on that
result when building `/users/sign-in?redirect=...`.

In `@apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx`:
- Around line 157-181: The recovery branch for anonymous users (inside
getFirebaseAuth() check where signInWithEmailAndPassword is used) skips the
journeyPublish step, causing a different flow than new sign-up; update this
branch to call and await the same journeyPublish invocation used in the
anonymous happy path (use the same pending = getPendingGuestJourney() data and
call journeyPublish(pending.journeyId, pending.input || ...) as in the earlier
flow), handle its promise/errors the same way, then proceed to obtain idToken,
call login(idToken), and perform the redirect; ensure you reference and update
the signInWithEmailAndPassword branch, getPendingGuestJourney, journeyPublish,
and login calls so behavior matches the original anonymous sign-up path.

In
`@apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.spec.tsx`:
- Around line 293-297: The test references an out-of-scope symbol: change the
cast that uses typeof OAuthProvider.credentialFromError to use the imported
alias MockedOAuthProvider instead; update the
mockCredentialFromError.mockReturnValueOnce call to cast to ReturnType<typeof
MockedOAuthProvider.credentialFromError> so the type reference matches the
import (symbols to locate: mockCredentialFromError,
OAuthProvider.credentialFromError, MockedOAuthProvider).

In
`@apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx`:
- Around line 89-117: The recovery branch for 'auth/credential-already-in-use'
signs in the existing provider account but skips the anonymous-to-provider
completion step (the publish performed in linkAnonymousUserWithProvider),
causing inconsistent flows; after successfully obtaining idToken via
signInWithCredential and before performing the redirect/reload (i.e., inside the
oauthCredential != null block, after await login(idToken)), invoke the same
publish/complete logic used by linkAnonymousUserWithProvider (the journey
publish function or inline publish steps currently executed in
linkAnonymousUserWithProvider) with the pending guest journey data so the guest
journey is published/cleared the same way, then proceed to redirect or reload.

In `@apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.ts`:
- Around line 20-24: getPendingGuestJourney currently casts JSON.parse(raw) to
PendingGuestJourney which allows malformed data from sessionStorage to slip
through; update getPendingGuestJourney to perform a runtime shape check on the
parsed value (ensure it's a plain object and contains the required fields such
as a string journeyId and any other required properties of PendingGuestJourney)
before returning it, and return null if the parsed value fails validation;
reference the STORAGE_KEY and PendingGuestJourney types and add the validation
inside getPendingGuestJourney so invalid arrays/primitives/partial objects never
propagate into the post-login redirect/transfer flow.

---

Outside diff comments:
In `@apps/journeys-admin/pages/users/verify.tsx`:
- Around line 258-263: The code returns a redirect using an undefined variable
destination after the VALIDATE_EMAIL mutation; fix by replacing the redirect
block to use the existing redirect helper or compute destination explicitly:
call redirectToApp(...) (the same helper used elsewhere) to produce the
destination or derive destination from the request's redirect query param with a
safe fallback (e.g., home path) and then return that value in the redirect
object; update the code paths around VALIDATE_EMAIL and the verify handler so
redirectToApp or the computed destination is used instead of the undefined
destination variable.

---

Nitpick comments:
In
`@apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.spec.ts`:
- Around line 232-260: Add a new spec in
journeyTransferFromAnonymous.mutation.spec.ts that covers the regression where
the journey.teamId equals the resolvedTeamId but the owner is still anonymous:
mock prismaMock.journey.findUnique to return a journey with teamId
'targetTeamId' and userJourneys either empty or containing an anonymous owner
(no userId), ensure prismaMock.userTeam.findFirst resolves to mockUserTeam and
prismaMock.journey.findUniqueOrThrow returns { id: 'journeyId', teamId:
'targetTeamId' }, then call authClient with JOURNEY_TRANSFER_MUTATION and assert
result.errors is undefined, result.data?.journeyTransferFromAnonymous.id ===
'journeyId' and prismaMock.$transaction was not called to validate the no-op
short-circuit for anonymous-owner/same-team.

In
`@apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx`:
- Around line 188-221: Add a new test case alongside the anonymous-user spec
that mocks getPendingGuestJourney to return a non-null pending journey (e.g., {
id: 'pending-1' }) before triggering the sign-in flow in PasswordPage; reuse
mockGetFirebaseAuth, mockSignInWithEmailAndPassword and mockFirebaseSignOut,
then after the sign-in completes assert the component performs the guest
redirect by checking the navigation/redirect target includes "/users/sign-in"
with the query "transfer=true" (assert whichever redirect helper your tests use
— history.push, window.location.assign, or router mock — receives the URL with
transfer=true).
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d16271a0-f136-4ec1-9de2-2e8033257796

📥 Commits

Reviewing files that changed from the base of the PR and between 97c5d50 and fbb0d13.

⛔ Files ignored due to path filters (3)
  • apis/api-journeys/src/__generated__/graphql.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/JourneyTransferFromAnonymous.ts is excluded by !**/__generated__/**
  • libs/shared/gql/src/__generated__/graphql-env.d.ts is excluded by !**/__generated__/**
📒 Files selected for processing (20)
  • apis/api-gateway/schema.graphql
  • apis/api-journeys-modern/schema.graphql
  • apis/api-journeys-modern/src/schema/journey/index.ts
  • apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.spec.ts
  • apis/api-journeys-modern/src/schema/journey/journeyTransferFromAnonymous.mutation.ts
  • apps/journeys-admin/pages/templates/[journeyId]/customize.tsx
  • apps/journeys-admin/pages/users/verify.tsx
  • apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx
  • apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.tsx
  • apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.spec.tsx
  • apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx
  • apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.spec.tsx
  • apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx
  • apps/journeys-admin/src/components/SignIn/utils/addTransferParam/addTransferParam.spec.ts
  • apps/journeys-admin/src/components/SignIn/utils/addTransferParam/addTransferParam.ts
  • apps/journeys-admin/src/components/SignIn/utils/addTransferParam/index.ts
  • apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/GuestPreviewScreen/GuestPreviewScreen.tsx
  • apps/journeys-admin/src/libs/pendingGuestJourney/index.ts
  • apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.spec.ts
  • apps/journeys-admin/src/libs/pendingGuestJourney/pendingGuestJourney.ts

@github-actions github-actions bot temporarily deployed to Preview - player April 2, 2026 00:59 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys April 2, 2026 00:59 Inactive
@github-actions github-actions bot requested a deployment to Preview - journeys-admin April 2, 2026 00:59 Pending
@github-actions github-actions bot temporarily deployed to Preview - videos-admin April 2, 2026 00:59 Inactive
@github-actions github-actions bot temporarily deployed to Preview - short-links April 2, 2026 00:59 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources April 2, 2026 00:59 Inactive
@github-actions github-actions bot temporarily deployed to Preview - short-links April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - watch April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - player April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys April 2, 2026 01:04 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin April 2, 2026 01:04 Inactive
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx (1)

110-124: Duplicate sign-in call before redirect may cause issues.

After linkWithCredential succeeds, the code calls signInWithEmailAndPassword and login(idToken) (Lines 112-118), then redirects to /users/sign-in. However, the linkWithCredential already authenticated the user. This creates an extra sign-in roundtrip that may not be necessary.

Consider whether calling user.getIdToken() directly on userCredential.user from line 100 would suffice, rather than re-authenticating with signInWithEmailAndPassword.

♻️ Suggested simplification
     const pending = getPendingGuestJourney()
     if (pending != null) {
-      const signInCredential = await signInWithEmailAndPassword(
-        auth,
-        email,
-        password
-      )
-      const idToken = await signInCredential.user.getIdToken()
+      const idToken = await userCredential.user.getIdToken()
       await login(idToken)
       const existingRedirect = router.query.redirect as string | undefined
       const redirectUrl =
         existingRedirect ?? `/templates/${pending.journeyId}/customize`
       window.location.href = `/users/sign-in?redirect=${encodeURIComponent(redirectUrl)}`
       return
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx`
around lines 110 - 124, The code is doing an unnecessary re-authentication:
after linking credentials with linkWithCredential and obtaining userCredential,
avoid calling signInWithEmailAndPassword again; instead call
userCredential.user.getIdToken() and pass that token to login(idToken) (replace
the signInWithEmailAndPassword + getIdToken sequence in the pending branch),
then perform the same redirect to `/users/sign-in?redirect=...`; update
references in this block (getPendingGuestJourney, signInWithEmailAndPassword,
login, linkWithCredential, userCredential.user.getIdToken) so the pending flow
uses the already-authenticated user token and removes the extra sign-in
roundtrip.
apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx (1)

85-90: Unsafe type coercion for error handling.

Casting err to { code?: string } (Line 86) and then to FirebaseError (Line 89) is fragile. If the error is not a Firebase error, credentialFromError may behave unexpectedly.

Consider using a type guard or checking the error type more robustly:

♻️ Suggested improvement
     } catch (err: unknown) {
-      const firebaseErr = err as { code?: string }
-      if (firebaseErr.code === 'auth/credential-already-in-use') {
+      if (
+        err instanceof Error &&
+        'code' in err &&
+        err.code === 'auth/credential-already-in-use'
+      ) {
         const oauthCredential = OAuthProvider.credentialFromError(
-          err as FirebaseError
+          err as Error & { code: string }
         )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx`
around lines 85 - 90, The catch block currently unsafely coerces err to `{
code?: string }` and to `FirebaseError` before calling
OAuthProvider.credentialFromError; add a runtime type guard (e.g., an
isFirebaseError function that checks typeof err === 'object' && err !== null &&
'code' in err and/or err instanceof FirebaseError) and use that guard to only
call OAuthProvider.credentialFromError when the error is confirmed to be a
FirebaseError, otherwise handle the non-Firebase error path (log/throw or
fallback). Update the code around the catch block and references to firebaseErr
and OAuthProvider.credentialFromError to use this guard so credentialFromError
is never called with an unrelated error type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.tsx`:
- Around line 51-53: The code in PasswordPage currently signs out an anonymous
user (firebaseSignOut(auth)) before attempting signInWithEmailAndPassword, which
can orphan pendingGuestJourney stored in sessionStorage if sign-in fails; update
PasswordPage to defer signing out the anonymous user until after
signInWithEmailAndPassword succeeds, or wrap the sign-out and sign-in sequence
in a try-catch that only clears or removes pendingGuestJourney on successful
authentication, and on auth errors explicitly clear the
pendingGuestJourney/sessionStorage fallback instead of destroying the anonymous
session prematurely; refer to the firebaseSignOut, signInWithEmailAndPassword
calls and the pendingGuestJourney/sessionStorage handling in PasswordPage to
implement this control flow change.

---

Nitpick comments:
In `@apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx`:
- Around line 110-124: The code is doing an unnecessary re-authentication: after
linking credentials with linkWithCredential and obtaining userCredential, avoid
calling signInWithEmailAndPassword again; instead call
userCredential.user.getIdToken() and pass that token to login(idToken) (replace
the signInWithEmailAndPassword + getIdToken sequence in the pending branch),
then perform the same redirect to `/users/sign-in?redirect=...`; update
references in this block (getPendingGuestJourney, signInWithEmailAndPassword,
login, linkWithCredential, userCredential.user.getIdToken) so the pending flow
uses the already-authenticated user token and removes the extra sign-in
roundtrip.

In
`@apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx`:
- Around line 85-90: The catch block currently unsafely coerces err to `{ code?:
string }` and to `FirebaseError` before calling
OAuthProvider.credentialFromError; add a runtime type guard (e.g., an
isFirebaseError function that checks typeof err === 'object' && err !== null &&
'code' in err and/or err instanceof FirebaseError) and use that guard to only
call OAuthProvider.credentialFromError when the error is confirmed to be a
FirebaseError, otherwise handle the non-Firebase error path (log/throw or
fallback). Update the code around the catch block and references to firebaseErr
and OAuthProvider.credentialFromError to use this guard so credentialFromError
is never called with an unrelated error type.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5e43d675-fd46-42c5-b4fa-8c4651db63fc

📥 Commits

Reviewing files that changed from the base of the PR and between fbb0d13 and 1259fb3.

📒 Files selected for processing (8)
  • apps/journeys-admin/pages/users/verify.tsx
  • apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx
  • apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.tsx
  • apps/journeys-admin/src/components/SignIn/RegisterPage/RegisterPage.tsx
  • apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.spec.tsx
  • apps/journeys-admin/src/components/SignIn/SignInServiceButton/SignInServiceButton.tsx
  • apps/journeys-admin/src/components/SignIn/utils/addTransferParam/addTransferParam.spec.ts
  • apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/GuestPreviewScreen/GuestPreviewScreen.tsx
✅ Files skipped from review due to trivial changes (1)
  • apps/journeys-admin/src/components/SignIn/utils/addTransferParam/addTransferParam.spec.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/journeys-admin/src/components/SignIn/PasswordPage/PasswordPage.spec.tsx
  • apps/journeys-admin/pages/users/verify.tsx

- Fix early-return bug: only short-circuit when user already owns AND
  team is correct (prevents skipping ownership transfer)
- Harden client-side transfer effect: validate pending.journeyId matches
  current journey, only clear marker on success, retry on failure
- Add runtime shape validation to getPendingGuestJourney to guard
  against malformed sessionStorage data
- Fix OAuthProvider import and lint issues in test files

Made-with: Cursor
@github-actions github-actions bot temporarily deployed to Preview - watch April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - short-links April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - videos-admin April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - player April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - journeys-admin April 2, 2026 01:19 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources April 2, 2026 01:19 Inactive
@stage-branch-merger
Copy link
Copy Markdown

I see you added the "on stage" label, I'll get this merged to the stage branch!

@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants