Skip to content

Block sign-in for unverified email addresses#1192

Draft
codenem wants to merge 2 commits into
mainfrom
codenem/email-verification-guard-5bde
Draft

Block sign-in for unverified email addresses#1192
codenem wants to merge 2 commits into
mainfrom
codenem/email-verification-guard-5bde

Conversation

@codenem

@codenem codenem commented May 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Resolves ENG-401: Users with unverified email addresses can no longer authenticate via password sign-in. When blocked, they are redirected to a dedicated verification page where they can request a new verification email.

Changes

Backend

  • pkg/iam/errors.go — Added ErrEmailNotVerified error type for the IAM package.
  • pkg/iam/auth_service.goCheckCredentials now checks EmailAddressVerified after successful password verification. If the flag is false, it returns ErrEmailNotVerified. Added SendEmailVerification(email) method that looks up the identity and sends a verification email (silently succeeds if identity not found or already verified, to prevent information leakage).
  • pkg/server/api/connect/v1/graphql/session.graphql — Added resendVerificationEmail mutation (@session(required: NONE)), with ResendVerificationEmailInput and ResendVerificationEmailPayload.
  • pkg/server/api/connect/v1/session_resolvers.go — The SignIn resolver catches ErrEmailNotVerified and returns a GraphQL error with code EMAIL_NOT_VERIFIED. New ResendVerificationEmail resolver delegates to AuthService.SendEmailVerification.

Frontend

  • apps/console/src/pages/iam/auth/ResendVerificationEmailPage.tsx — New page modeled after ForgotPasswordPage: email input (prefilled from query string), submit calls resendVerificationEmail mutation, then shows a "check your email" confirmation with a "try again" link.
  • apps/console/src/pages/iam/auth/sign-in/PasswordSignInPage.tsx — On EMAIL_NOT_VERIFIED, redirects to /auth/resend-verification-email?email=<addr> instead of showing a toast.
  • apps/console/src/routes.tsx — Added /auth/resend-verification-email route.

How it works

  1. User tries to sign in with email + password
  2. Password is validated successfully
  3. If email_address_verified is false, authentication is rejected with EMAIL_NOT_VERIFIED
  4. The frontend redirects to /auth/resend-verification-email?email=<addr>
  5. User sees a form with their email prefilled, submits to request a new verification email
  6. After submission, a confirmation screen shows with "check your email" instructions

Unaffected flows

SAML, OIDC, magic-link, and invitation-activation flows are unaffected because they already set email_address_verified = true upon identity creation or update.

Linear Issue: ENG-401

Open in Web Open in Cursor 

Summary by cubic

Blocks password sign-in for unverified accounts and redirects users to a dedicated verification flow. Implements ENG-401 by returning EMAIL_NOT_VERIFIED and adding a safe resend path; SAML, OIDC, magic-link, and invitation activation stay the same.

  • New Features
    • Backend: CheckCredentials rejects unverified identities via ErrEmailNotVerified. Added SendEmailVerification(ctx, email) which no-ops if the account doesn’t exist or is already verified to avoid info leaks.
    • API: SignIn now only returns a GraphQL error with code EMAIL_NOT_VERIFIED (no auto-resend). Added resendVerificationEmail mutation.
    • Frontend: On EMAIL_NOT_VERIFIED, password sign-in redirects to /auth/resend-verification-email?email=<addr>. New ResendVerificationEmailPage lets users enter an email, call the mutation, and see a confirmation; route added in routes.tsx.

Written for commit bd9f6fa. Summary will update on new commits. Review in cubic

@cursor cursor Bot force-pushed the codenem/email-verification-guard-5bde branch from dd4dbe5 to 4604949 Compare May 14, 2026 13:19
cursoragent and others added 2 commits May 26, 2026 16:24
CheckCredentials now rejects password authentication when the
identity's email_address_verified flag is false, returning a new
ErrEmailNotVerified error.

The SignIn resolver catches this error, resends a verification
email so the user gets a fresh link, and returns a GraphQL error
with code EMAIL_NOT_VERIFIED.

The frontend PasswordSignInPage handles this code to display a
specific toast telling the user to check their inbox.

SAML, OIDC, magic-link, and invitation-activation flows are
unaffected because they already set email_address_verified = true
upon identity creation or update.

Co-authored-by: Émile Ré <[email protected]>
Signed-off-by: Cursor Agent <[email protected]>
Replace the automatic email resend on sign-in rejection with a
dedicated resend-verification-email page. The PasswordSignInPage
now redirects to /auth/resend-verification-email?email=<addr>
when the EMAIL_NOT_VERIFIED error is returned.

The new page follows the same UX pattern as forgot-password: an
email input (prefilled from the query string), a submit button
that calls the new resendVerificationEmail GraphQL mutation, and
a confirmation screen with a 'try again' link.

The SendEmailVerification method now takes an email address and
silently succeeds when the identity is not found or already
verified, preventing information leakage.

Co-authored-by: Émile Ré <[email protected]>
Signed-off-by: Cursor Agent <[email protected]>
@codenem codenem force-pushed the codenem/email-verification-guard-5bde branch from 4604949 to bd9f6fa Compare May 26, 2026 14:25
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.

2 participants