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,
};
}