Skip to content

Feat: Legacy Auth Parity for Eyebrow + Magic Login Flows#303

Open
OtavioXimarelli wants to merge 5 commits into
QuoteVote:mainfrom
OtavioXimarelli:feat/eyebrow-cta-295
Open

Feat: Legacy Auth Parity for Eyebrow + Magic Login Flows#303
OtavioXimarelli wants to merge 5 commits into
QuoteVote:mainfrom
OtavioXimarelli:feat/eyebrow-cta-295

Conversation

@OtavioXimarelli
Copy link
Copy Markdown
Member

Feat: Legacy Auth Parity for Eyebrow + Magic Login Flows

Description

This PR brings the legacy monorepo behavior in line with the Next implementation for the guest eyebrow CTA and magic-login/onboarding entry points.

It adds the missing backend onboarding-link mutation, updates the eyebrow UX to handle all status branches consistently, and adds targeted test coverage for critical parity paths on both client and server.

Changes

  • Eyebrow CTA parity (client)

    • Refactored EyebrowBar flow to branch by checkEmailStatus and show modal-based actions for:
      • registered -> login options modal (magic link or password login)
      • approved_no_password -> onboarding completion modal
    • Added dismiss behavior and preserved layout offset updates (--eyebrow-height).
    • Normalized email input (trim) before network calls.
    • Updated mutation handling to honor backend payloads (success/message) instead of assuming transport-level success.
    • Added fallback behavior: if magic-link mutation returns incomplete-signup response, transition to onboarding modal.
  • Client GraphQL contract updates

    • Added SEND_ONBOARDING_COMPLETION_LINK mutation in client/src/graphql/mutations.jsx.
  • Magic login page hardening (client)

    • Kept token verification/login redirect flow aligned with parity behavior.
    • Added tests for missing token, invalid token, valid token login dispatch + redirect, and expired token messaging.
  • Backend parity (server)

    • Added new resolver sendOnboardingCompletionLink:
      • sends tokenized /auth/signup?token=... onboarding link for approved users without passwords
      • returns generic success for non-eligible accounts to avoid account enumeration
    • Wired resolver into mutation exports and root mutation map.
    • Added schema mutation definition: sendOnboardingCompletionLink(email: String!): JSON.
    • Allowed public access for new onboarding-link mutation via requireAuth allowlist.
  • Automated tests added

    • client/src/components/EyebrowBar/EyebrowBar.test.jsx
    • client/src/views/MagicLoginPage/MagicLoginPage.test.jsx
    • server/app/tests/queries/user/checkEmailStatus.test.js
    • server/app/tests/mutations/user/sendMagicLoginLink.test.js
    • server/app/tests/mutations/user/sendOnboardingCompletionLink.test.js

Verification

  • Client tests

    • npm run test -- src/components/EyebrowBar/EyebrowBar.test.jsx src/views/MagicLoginPage/MagicLoginPage.test.jsx
    • Result: passing (11 tests)
  • Server tests

    • npm run test -- --runInBand app/tests/queries/user/checkEmailStatus.test.js app/tests/mutations/user/sendMagicLoginLink.test.js app/tests/mutations/user/sendOnboardingCompletionLink.test.js
    • Result: passing (11 tests)
  • Lint / style

    • Client files linted and auto-fixed for repository rules; remaining warnings are existing workspace alias-resolution warnings for @/... imports.
    • Prettier was applied to all files changed in this PR.

Notes

  • This PR intentionally focuses on parity-critical auth paths and their regression coverage.
  • A full-suite regression run can be done separately if broader release confidence is required.

- Add EyebrowBar component (fixed above nav for unauthenticated users)
  - Email input + Continue CTA
  - Handles 4 email states: registered, not_requested, requested_pending, approved_no_password
  - Magic link login and password login options for registered users
  - Auto-submits invite request for brand new emails
  - Hides entirely when user is authenticated
  - Mobile-responsive layout
  - CSS var --eyebrow-height to offset AppBar and content

- Add MagicLoginPage (/auth/magic-login?token=...)
  - Validates token via verifyUserPasswordResetToken query
  - Dispatches USER_LOGIN_SUCCESS and redirects to /search

- Add checkEmailStatus GraphQL query (server)
  - Returns status: registered | not_requested | requested_pending | approved_no_password
  - Public (no auth required)

- Add sendMagicLoginLink GraphQL mutation (server)
  - Generates 15-min JWT token via addCreatorToUser
  - Sends email via SendGrid MAGIC_LOGIN template
  - Public (no auth required)

- Update GraphQL type definitions and resolver registrations
- Mark checkEmailStatus and sendMagicLoginLink as public in requireAuth
- Add MAGIC_LOGIN template ID to SendGrid constants

Closes QuoteVote#295
Copilot AI review requested due to automatic review settings March 16, 2026 03:18
@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 16, 2026

Deploy Preview for quotevote ready!

Name Link
🔨 Latest commit ff43b43
🔍 Latest deploy log https://app.netlify.com/projects/quotevote/deploys/69b8e44b31956963a418942b
😎 Deploy Preview https://deploy-preview-303--quotevote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@OtavioXimarelli
Copy link
Copy Markdown
Member Author

I've achieved code parity between the two repos. Let me know what you think and if anything else is needed, thank you! @flyblackbox.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aligns the legacy app’s guest auth entry points with the newer implementation by adding backend support for email-status branching + onboarding-link delivery, and updating the client eyebrow CTA and magic-login route to use those parity flows with targeted tests.

Changes:

  • Added server GraphQL query/mutations for checkEmailStatus, sendMagicLoginLink, and sendOnboardingCompletionLink, plus public access wiring and tests.
  • Introduced a global guest EyebrowBar that branches by email status and drives modal flows (invite request, magic link, onboarding completion).
  • Added /auth/magic-login page + tests, and updated layout offsets to account for the eyebrow height.

Reviewed changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
server/app/tests/queries/user/checkEmailStatus.test.js Adds query-level test coverage for new email-status branching.
server/app/tests/mutations/user/sendOnboardingCompletionLink.test.js Adds mutation tests for onboarding completion link behavior.
server/app/tests/mutations/user/sendMagicLoginLink.test.js Adds mutation tests for magic login link behavior.
server/app/data/utils/send-grid-mail.js Adds SendGrid template ID for the new magic-login email.
server/app/data/utils/requireAuth.js Allows unauthenticated access to new auth-parity operations.
server/app/data/type_definition/query_definition.js Adds checkEmailStatus to the GraphQL schema.
server/app/data/type_definition/mutation_definition.js Adds sendMagicLoginLink + sendOnboardingCompletionLink to the schema.
server/app/data/resolvers/queries/user/index.js Re-exports the new checkEmailStatus query resolver.
server/app/data/resolvers/queries/user/checkEmailStatus.js Implements email status lookup for eyebrow branching.
server/app/data/resolvers/queries.js Wires checkEmailStatus into the root query map.
server/app/data/resolvers/mutations/user/sendOnboardingCompletionLink.js Implements onboarding completion link email sending for approved users without passwords.
server/app/data/resolvers/mutations/user/sendMagicLoginLink.js Implements magic login email sending for registered users.
server/app/data/resolvers/mutations/user/index.js Re-exports new user mutations.
server/app/data/resolvers/mutations.js Wires new mutations into the root mutation map.
client/src/views/MagicLoginPage/MagicLoginPage.test.jsx Adds UI tests for token handling, messaging, dispatch, and redirect.
client/src/views/MagicLoginPage/MagicLoginPage.jsx Adds magic-login verification + login dispatch + redirect flow.
client/src/mui-pro/mui-routes.jsx Adds the /auth/magic-login route.
client/src/main.jsx Mounts EyebrowBar globally above routing.
client/src/graphql/query.jsx Adds CHECK_EMAIL_STATUS query definition.
client/src/graphql/mutations.jsx Adds SEND_MAGIC_LOGIN_LINK + SEND_ONBOARDING_COMPLETION_LINK mutations.
client/src/components/Navbars/MainNavBar.jsx Offsets fixed navbar by --eyebrow-height for layout parity.
client/src/components/EyebrowBar/EyebrowBar.test.jsx Adds tests for all eyebrow flow branches and dismissal.
client/src/components/EyebrowBar/EyebrowBar.jsx Adds new guest eyebrow component with modal-based auth flows.
client/src/assets/jss/material-dashboard-pro-react/layouts/adminStyle.jsx Updates top margin calculations to include eyebrow height.
PR_DESCRIPTION.md Updates PR description to reflect the parity/auth changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +17 to +18
const user = await UserModel.findOne({
email: { $regex: new RegExp(`^${email.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') },
Comment on lines +11 to +15
const { email } = args;
const user = await UserModel.findOne({
email: { $regex: new RegExp(`^${email.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`, 'i') },
});

}

if (!user.hash_password) {
return { success: false, message: 'This account has not completed signup yet.' };
Comment on lines +12 to +18
const user = await UserModel.findOne({
email: {
$regex: new RegExp(
`^${email.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`,
'i',
),
},
Comment on lines +24 to +26
'checkEmailStatus',
'sendMagicLoginLink',
'sendOnboardingCompletionLink',
Comment on lines +76 to +79
// Dispatch login success with user data
dispatch(
USER_LOGIN_SUCCESS({
data: user,
Comment on lines +75 to +79
await new Promise((resolve) => {
setTimeout(resolve, 1100)
})

expect(mockPush).toHaveBeenCalledWith('/search')
@flyblackbox
Copy link
Copy Markdown
Contributor

@OtavioXimarelli Do you know why I get the error "Continue
Response not successful: Received status code 400"? I accessed via the deploy preview link and tried an email address linked to an account.

Copilot AI review requested due to automatic review settings March 17, 2026 03:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Aligns the legacy monorepo auth/onboarding entry behavior with the Next implementation by adding missing backend support for email-status branching and onboarding-link delivery, and updating the client “eyebrow” + magic-login flows with parity-focused UX and regression tests.

Changes:

  • Added backend checkEmailStatus query plus public sendMagicLoginLink / sendOnboardingCompletionLink mutations for parity flows.
  • Implemented new client EyebrowBar CTA flow and /auth/magic-login page with corresponding GraphQL contract updates.
  • Added targeted client/server test coverage for the new parity paths and introduced request-id logging/correlation.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
server/app/tests/queries/user/checkEmailStatus.test.js Adds unit tests for checkEmailStatus resolver status branching + validation error handling.
server/app/tests/mutations/user/sendOnboardingCompletionLink.test.js Adds unit tests for onboarding-link mutation behavior (eligible vs ineligible).
server/app/tests/mutations/user/sendMagicLoginLink.test.js Adds unit tests for magic-login link mutation (unknown, incomplete, registered).
server/app/server.js Adds request-id plumbing + GraphQL request logging; updates Apollo context to include requestId.
server/app/data/utils/send-grid-mail.js Adds MAGIC_LOGIN template ID and includes request-id in SendGrid-related logs.
server/app/data/utils/requireAuth.js Allows new query/mutations to be called without authentication.
server/app/data/type_definition/query_definition.js Adds GraphQL schema entry for checkEmailStatus.
server/app/data/type_definition/mutation_definition.js Adds schema entries for sendMagicLoginLink and sendOnboardingCompletionLink.
server/app/data/resolvers/queries/user/index.js Re-exports checkEmailStatus query resolver.
server/app/data/resolvers/queries/user/checkEmailStatus.js Implements email status branching for eyebrow CTA flow.
server/app/data/resolvers/queries.js Wires checkEmailStatus into root resolver map.
server/app/data/resolvers/mutations/userInvite/requestUserAccess.js Threads requestId into logs and email send call for invite-request confirmation.
server/app/data/resolvers/mutations/user/sendOnboardingCompletionLink.js Implements onboarding completion link email send for approved/no-password users.
server/app/data/resolvers/mutations/user/sendMagicLoginLink.js Implements magic login link email send for registered users.
server/app/data/resolvers/mutations/user/index.js Exports the new user mutations and restores star-exports list.
server/app/data/resolvers/mutations.js Wires new mutations into root mutation map.
client/src/views/MagicLoginPage/MagicLoginPage.test.jsx Adds MagicLogin page tests for token missing/invalid/valid/expired behaviors.
client/src/views/MagicLoginPage/MagicLoginPage.jsx Adds MagicLogin page that verifies token, dispatches login, and redirects.
client/src/mui-pro/mui-routes.jsx Adds /auth/magic-login route entry.
client/src/main.jsx Mounts the EyebrowBar globally above routing.
client/src/graphql/query.jsx Adds CHECK_EMAIL_STATUS client query.
client/src/graphql/mutations.jsx Adds SEND_MAGIC_LOGIN_LINK and SEND_ONBOARDING_COMPLETION_LINK mutations.
client/src/config/apollo.js Optionally adds an x-request-id header per request for correlation.
client/src/components/Navbars/MainNavBar.jsx Offsets fixed AppBar by --eyebrow-height so layout accounts for eyebrow banner.
client/src/components/EyebrowBar/EyebrowBar.test.jsx Adds eyebrow CTA flow tests across status branches + dismissal.
client/src/components/EyebrowBar/EyebrowBar.jsx Implements new eyebrow CTA branching flow with modals + email normalization.
client/src/assets/jss/material-dashboard-pro-react/layouts/adminStyle.jsx Updates layout top margins to include --eyebrow-height.
PR_DESCRIPTION.md Updates PR description to reflect the new parity/auth scope.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +88 to +90
setTimeout(() => {
history.push('/search')
}, 1000)
Comment thread server/app/server.js
Comment on lines +106 to +110
logger.warn('CORS blocked request origin', {
requestId: req.requestId,
origin,
method: req.method,
path: req.path,
Comment on lines +283 to +286
setFeedback('Sending login link...')
const { data } = await sendMagicLink({ variables: { email } })
const result = data?.sendMagicLoginLink

try {
setFeedback('Sending onboarding link...')
const { data } = await sendOnboardingCompletionLink({
variables: { email },
@OtavioXimarelli
Copy link
Copy Markdown
Member Author

Eyebrow Auth Flow

Local backend is working and tests pass for eyebrow-related GraphQL operations. Production https://api.quote.vote/graphql behaves like it is running an older or different backend artifact (auth/schema mismatch).

What Is Verified Locally

  1. Schema fields exist:
  • checkEmailStatus(email: String!): JSON
  • sendMagicLoginLink(email: String!): JSON
  • sendOnboardingCompletionLink(email: String!): JSON
  • requestUserAccess(requestUserAccessInput: RequestUserAccessInput!): User
  1. Resolver wiring exists in:
  • server/app/data/resolvers/queries.js
  • server/app/data/resolvers/mutations.js
  1. Public auth allowlist includes those eyebrow operations in:
  • server/app/data/utils/requireAuth.js
  1. requireAuth was hardened to GraphQL AST root-field matching (no unsafe substring matching).

Local Test Results (Passed)

  1. app/tests/utils/requireAuth.test.js
  • Result: 1 suite, 5 tests passed.
  • Logs: debug-logs/local-requireAuth-test.log
  • Clean logs: debug-logs/local-requireAuth-test.clean.log
  1. checkEmailStatus + sendMagicLoginLink + sendOnboardingCompletionLink
  • Command target files:
    app/tests/queries/user/checkEmailStatus.test.js
    app/tests/mutations/user/sendMagicLoginLink.test.js
    app/tests/mutations/user/sendOnboardingCompletionLink.test.js
  • Result: 3 suites, 11 tests passed.
  • Logs: debug-logs/local-eyebrow-tests.log
  • Clean logs: debug-logs/local-eyebrow-tests.clean.log
  1. Eyebrow email-send path (requestUserAccess, not_requested branch)
  • Test file: app/tests/mutations/userInvite/requestUserAccess.test.js
  • Result: 1 suite, 3 tests passed.
  • Logs: debug-logs/local-eyebrow-requestUserAccess.log
  • Clean logs: debug-logs/local-eyebrow-requestUserAccess.clean.log
  • Key local evidence from logs: confirmation email flow is reached and sendGrid result is logged.

Production Symptoms

Observed responses from https://api.quote.vote/graphql include:

  • Context creation failed: Auth token not found in request
  • Cannot query field "checkEmailStatus" on type "Query"
  • Cannot query field "sendMagicLoginLink" on type "Mutation"
  • Cannot query field "sendOnboardingCompletionLink" on type "Mutation"

Interpretation: this is not a client payload-shape issue; production runtime/schema does not match the verified local backend.

@OtavioXimarelli
Copy link
Copy Markdown
Member Author

@flyblackbox

The production backend (api.quote.vote) is returning auth context and missing-field errors. This indicates a potential runtime or schema drift, as the deployed version does not seem to reflect the latest source code.

✅ Local Verification

The local backend is fully verified and tests pass for the eyebrow auth flow operations:

  • checkEmailStatus
  • sendMagicLoginLink
  • sendOnboardingCompletionLink
  • requestUserAccess (email-send path)

🛠️ Action Items (Railway Checks)

To proceed with the debugging, I need some checks on the actual deployed backend. Please verify the following:

1. Source & Build Artifacts

  • Repository & Branch: Confirm the Railway deploy source repo and branch are correct.
  • Commit SHA: Confirm the deployed commit matches the one containing the eyebrow schema/resolver/auth updates.
  • Build Command: Confirm it compiles the backend from the current source.
  • Start Command: Confirm it runs the newly built artifact (not a stale output).

2. Runtime Environment Variables

Confirm the following variables are properly set in the production environment:

  • SENDGRID_API_KEY
  • SENDGRID_SENDER_EMAIL
  • CLIENT_URL

3. Production Smoke Checks

Once the deployment is confirmed to be running the latest artifact, please run smoke checks for these specific GraphQL fields:

  • checkEmailStatus
  • sendMagicLoginLink
  • sendOnboardingCompletionLink

@flyblackbox
Copy link
Copy Markdown
Contributor

Uh oh... that doesn't sound good. Do you know how to resolve this @OtavioXimarelli?

@OtavioXimarelli
Copy link
Copy Markdown
Member Author

Sorry for the delay, @flyblackbox. I've been very busy lately, and unfortunately, this seems out of my league right now. However, I'm open to working on any other issue you might need help with. Thank you again!

@flyblackbox
Copy link
Copy Markdown
Contributor

Ok @OtavioXimarelli understood, no worries. I will think about it and get back to you soon. Thank you for volunteering!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants