diff --git a/app/api/after-mfa/route.ts b/app/api/after-mfa/route.ts new file mode 100644 index 0000000..ad80011 --- /dev/null +++ b/app/api/after-mfa/route.ts @@ -0,0 +1,28 @@ +import { enhanceRouteHandler } from '@/packages/next/src/routes'; +import { createAuthCallbackService } from '@/packages/supabase/src/auth-callback.service'; +import { getSupabaseServerClient } from '@/packages/supabase/src/clients/server-client'; + +export const POST = () => + enhanceRouteHandler( + async () => { + try { + const supabaseClient = getSupabaseServerClient(); + const { + data: { user }, + } = await supabaseClient.auth.getUser(); + const service = createAuthCallbackService(supabaseClient); + + if (user && service.isKeycloakUser(user)) { + await service.setupMedusaUserForKeycloak(user); + } + + return new Response(null, { status: 200 }); + } catch (err) { + console.error('Error on verifying:', { err }); + return new Response(null, { status: 500 }); + } + }, + { + auth: false, + }, + ); diff --git a/app/auth/callback/route.ts b/app/auth/callback/route.ts index a16e023..d36c96a 100644 --- a/app/auth/callback/route.ts +++ b/app/auth/callback/route.ts @@ -47,6 +47,11 @@ export async function GET(request: NextRequest) { const service = createAuthCallbackService(getSupabaseServerClient()); const oauthResult = await service.exchangeCodeForSession(authCode); + + if (oauthResult.requiresMultiFactorAuthentication) { + redirect(pathsConfig.auth.verifyMfa); + } + if (!('isSuccess' in oauthResult)) { return redirectOnError(oauthResult.searchParams); } diff --git a/app/auth/verify/page.tsx b/app/auth/verify/page.tsx index 677c020..b9a584d 100644 --- a/app/auth/verify/page.tsx +++ b/app/auth/verify/page.tsx @@ -44,12 +44,7 @@ async function VerifyPage(props: Props) { !!nextPath && nextPath.length > 0 ? nextPath : pathsConfig.app.home; return ( - + ); } diff --git a/packages/features/auth/src/components/multi-factor-challenge-container.tsx b/packages/features/auth/src/components/multi-factor-challenge-container.tsx index a9a7fdd..5ff28d4 100644 --- a/packages/features/auth/src/components/multi-factor-challenge-container.tsx +++ b/packages/features/auth/src/components/multi-factor-challenge-container.tsx @@ -46,8 +46,13 @@ export function MultiFactorChallengeContainer({ const router = useRouter(); const verifyMFAChallenge = useVerifyMFAChallenge({ - onSuccess: () => { - router.replace(paths.redirectPath); + onSuccess: async () => { + try { + await fetch('/api/after-mfa', { method: 'POST' }); + router.replace(paths.redirectPath); + } catch (err) { + // ignore + } }, }); diff --git a/packages/supabase/src/auth-callback.service.ts b/packages/supabase/src/auth-callback.service.ts index f27cbc1..7bf1ff4 100644 --- a/packages/supabase/src/auth-callback.service.ts +++ b/packages/supabase/src/auth-callback.service.ts @@ -1,7 +1,5 @@ import 'server-only'; -import getBaseWebpackConfig from 'next/dist/build/webpack-config'; - import { AuthError, type EmailOtpType, @@ -9,6 +7,8 @@ import { User, } from '@supabase/supabase-js'; +import { checkRequiresMultiFactorAuthentication } from './check-requires-mfa'; + /** * @name createAuthCallbackService * @description Creates an instance of the AuthCallbackService @@ -137,10 +137,12 @@ class AuthCallbackService { | { isSuccess: boolean; user: User; + requiresMultiFactorAuthentication: boolean; } | ErrorURLParameters > { let user: User; + let requiresMultiFactorAuthentication: boolean; try { const { data, error } = await this.client.auth.exchangeCodeForSession(authCode); @@ -153,8 +155,14 @@ class AuthCallbackService { }); } - // Handle Keycloak users - set up Medusa integration - if (data?.user && this.isKeycloakUser(data.user)) { + requiresMultiFactorAuthentication = + await checkRequiresMultiFactorAuthentication(this.client); + + if ( + !requiresMultiFactorAuthentication && + data?.user && + this.isKeycloakUser(data.user) + ) { await this.setupMedusaUserForKeycloak(data.user); } @@ -179,20 +187,21 @@ class AuthCallbackService { return { isSuccess: true, user, + requiresMultiFactorAuthentication, }; } /** * Check if user is from Keycloak provider */ - private isKeycloakUser(user: any): boolean { + isKeycloakUser(user: any): boolean { return ( user?.app_metadata?.provider === 'keycloak' || user?.app_metadata?.providers?.includes('keycloak') ); } - private async setupMedusaUserForKeycloak(user: any): Promise { + async setupMedusaUserForKeycloak(user: any): Promise { if (!user.email) { console.warn('Keycloak user has no email, skipping Medusa setup'); return; @@ -285,6 +294,7 @@ interface ErrorURLParameters { error: string; code?: string; searchParams: string; + requiresMultiFactorAuthentication: boolean; } export function getErrorURLParameters({ @@ -313,6 +323,7 @@ export function getErrorURLParameters({ error: errorMessage, code: code ?? '', searchParams: searchParams.toString(), + requiresMultiFactorAuthentication: false, }; }