From 72f5e13db9db3ca3151187a5fa69bafba288c470 Mon Sep 17 00:00:00 2001 From: Scott Trinh Date: Tue, 10 Dec 2024 07:45:49 -0500 Subject: [PATCH 1/2] Missing PKCE is success in email verification flow Since end users might verify their email on a different device than the user agent they initiated the sign up (or sign in) flow with, treat this as a success condition. The application will need to detect this case and show a message that confirms that the email is verified, but that the user will need to sign in to complete. --- packages/auth-express/src/index.ts | 7 ++++++- packages/auth-nextjs/src/shared.ts | 10 +++++++--- packages/auth-remix/src/server.ts | 10 +++++++--- packages/auth-sveltekit/src/server.ts | 10 +++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/auth-express/src/index.ts b/packages/auth-express/src/index.ts index e30b699c9..2e8e6bbee 100644 --- a/packages/auth-express/src/index.ts +++ b/packages/auth-express/src/index.ts @@ -498,8 +498,13 @@ export class ExpressAuth { throw new PKCEError("no verification_token in response"); } if (!verifier) { - throw new PKCEError("no pkce verifier cookie found"); + // End user verified email from a different user agent than sign-up. + // This is fine, but the application will need to detect this and + // inform the end user that they will need to initiate a new sign up + // attempt to complete the flow. + return next(); } + const tokenData = await ( await this.core ).verifyEmailPasswordSignup(verificationToken, verifier); diff --git a/packages/auth-nextjs/src/shared.ts b/packages/auth-nextjs/src/shared.ts index e085bcf07..6675899bc 100644 --- a/packages/auth-nextjs/src/shared.ts +++ b/packages/auth-nextjs/src/shared.ts @@ -53,7 +53,7 @@ export interface CreateAuthRouteHandlers { ): Promise; onEmailVerify( params: ParamsOrError< - { tokenData: TokenData }, + { tokenData: TokenData | null }, { verificationToken?: string } >, req: NextRequest, @@ -357,10 +357,14 @@ export abstract class NextAuth extends NextAuthHelpers { ); } if (!verifier) { + // End user verified email from a different user agent than + // sign-up. This is fine, but the application will need to detect + // this and inform the end user that they will need to initiate a + // new sign up attempt to complete the flow. return onEmailVerify( { - error: new PKCEError("no pkce verifier cookie found"), - verificationToken, + error: null, + tokenData: null, }, req, ); diff --git a/packages/auth-remix/src/server.ts b/packages/auth-remix/src/server.ts index d55941ccb..70c64cd58 100644 --- a/packages/auth-remix/src/server.ts +++ b/packages/auth-remix/src/server.ts @@ -86,7 +86,7 @@ export interface CreateAuthRouteHandlers { ): Promise; onEmailVerify( params: ParamsOrError< - { tokenData: TokenData }, + { tokenData: TokenData | null }, { verificationToken?: string } >, ): Promise; @@ -422,9 +422,13 @@ export class RemixServerAuth extends RemixClientAuth { }); } if (!verifier) { + // End user verified email from a different user agent than + // sign-up. This is fine, but the application will need to detect + // this and inform the end user that they will need to initiate a + // new sign up attempt to complete the flow. return cbCall(onEmailVerify, { - error: new PKCEError("no pkce verifier cookie found"), - verificationToken, + error: null, + tokenData: null, }); } let tokenData: TokenData; diff --git a/packages/auth-sveltekit/src/server.ts b/packages/auth-sveltekit/src/server.ts index 253e98c1d..8074cc3af 100644 --- a/packages/auth-sveltekit/src/server.ts +++ b/packages/auth-sveltekit/src/server.ts @@ -63,7 +63,7 @@ export interface AuthRouteHandlers { ) => Promise; onEmailVerify?: ( params: ParamsOrError< - { tokenData: TokenData }, + { tokenData: TokenData | null }, { verificationToken?: string } >, ) => Promise; @@ -653,9 +653,13 @@ async function handleAuthRoutes( }); } if (!verifier) { + // End user verified email from a different user agent than sign-up. + // This is fine, but the application will need to detect this and inform + // the end user that they will need to initiate a new sign up attempt to + // complete the flow. return onEmailVerify({ - error: new PKCEError("no pkce verifier cookie found"), - verificationToken, + error: null, + tokenData: null, }); } let tokenData: TokenData; From d6c1053766fb90d9a1d4187716bb00215dba7d32 Mon Sep 17 00:00:00 2001 From: Scott Trinh Date: Tue, 10 Dec 2024 21:03:02 -0500 Subject: [PATCH 2/2] Update the built-in UI callback as well --- packages/auth-express/src/index.ts | 6 +++++- packages/auth-nextjs/src/shared.ts | 9 ++++++++- packages/auth-remix/src/server.ts | 9 ++++++++- packages/auth-sveltekit/src/server.ts | 9 ++++++++- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/packages/auth-express/src/index.ts b/packages/auth-express/src/index.ts index 2e8e6bbee..d6e652ebe 100644 --- a/packages/auth-express/src/index.ts +++ b/packages/auth-express/src/index.ts @@ -413,7 +413,11 @@ export class ExpressAuth { } const verifier = req.cookies[this.options.pkceVerifierCookieName]; if (!verifier) { - throw new PKCEError("no pkce verifier cookie found"); + // End user verified email from a different user agent than sign-up. + // This is fine, but the application will need to detect this and + // inform the end user that they will need to initiate a new sign up + // attempt to complete the flow. + return next(); } const isSignUp = searchParams.get("isSignUp") === "true"; const tokenData = await (await this.core).getToken(code, verifier); diff --git a/packages/auth-nextjs/src/shared.ts b/packages/auth-nextjs/src/shared.ts index 6675899bc..01283dc45 100644 --- a/packages/auth-nextjs/src/shared.ts +++ b/packages/auth-nextjs/src/shared.ts @@ -564,9 +564,16 @@ export abstract class NextAuth extends NextAuthHelpers { this.options.pkceVerifierCookieName, )?.value; if (!verifier) { + // End user verified email from a different user agent than + // sign-up. This is fine, but the application will need to detect + // this and inform the end user that they will need to initiate a + // new sign up attempt to complete the flow. return onBuiltinUICallback( { - error: new PKCEError("no pkce verifier cookie found"), + error: null, + tokenData: null, + provider: null, + isSignUp: false, }, req, ); diff --git a/packages/auth-remix/src/server.ts b/packages/auth-remix/src/server.ts index 70c64cd58..32e48686e 100644 --- a/packages/auth-remix/src/server.ts +++ b/packages/auth-remix/src/server.ts @@ -358,8 +358,15 @@ export class RemixServerAuth extends RemixClientAuth { parseCookies(req)[this.options.pkceVerifierCookieName]; if (!verifier) { + // End user verified email from a different user agent than + // sign-up. This is fine, but the application will need to detect + // this and inform the end user that they will need to initiate a + // new sign up attempt to complete the flow. return cbCall(onBuiltinUICallback, { - error: new PKCEError("no pkce verifier cookie found"), + error: null, + tokenData: null, + provider: null, + isSignUp: false, }); } const isSignUp = searchParams.get("isSignUp") === "true"; diff --git a/packages/auth-sveltekit/src/server.ts b/packages/auth-sveltekit/src/server.ts index 8074cc3af..b35ed0517 100644 --- a/packages/auth-sveltekit/src/server.ts +++ b/packages/auth-sveltekit/src/server.ts @@ -597,8 +597,15 @@ async function handleAuthRoutes( const verifier = cookies.get(config.pkceVerifierCookieName); if (!verifier) { + // End user verified email from a different user agent than sign-up. + // This is fine, but the application will need to detect this and inform + // the end user that they will need to initiate a new sign up attempt to + // complete the flow. return onBuiltinUICallback({ - error: new PKCEError("no pkce verifier cookie found"), + error: null, + tokenData: null, + provider: null, + isSignUp: false, }); } const isSignUp = searchParams.get("isSignUp") === "true";