fix mfa login after keycloak

This commit is contained in:
Danel Kungla
2025-10-04 17:36:36 +03:00
parent 7d90b5b910
commit 3f17b82fdb
5 changed files with 58 additions and 14 deletions

View File

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

View File

@@ -47,6 +47,11 @@ export async function GET(request: NextRequest) {
const service = createAuthCallbackService(getSupabaseServerClient()); const service = createAuthCallbackService(getSupabaseServerClient());
const oauthResult = await service.exchangeCodeForSession(authCode); const oauthResult = await service.exchangeCodeForSession(authCode);
if (oauthResult.requiresMultiFactorAuthentication) {
redirect(pathsConfig.auth.verifyMfa);
}
if (!('isSuccess' in oauthResult)) { if (!('isSuccess' in oauthResult)) {
return redirectOnError(oauthResult.searchParams); return redirectOnError(oauthResult.searchParams);
} }

View File

@@ -44,12 +44,7 @@ async function VerifyPage(props: Props) {
!!nextPath && nextPath.length > 0 ? nextPath : pathsConfig.app.home; !!nextPath && nextPath.length > 0 ? nextPath : pathsConfig.app.home;
return ( return (
<MultiFactorChallengeContainer <MultiFactorChallengeContainer userId={user.id} paths={{ redirectPath }} />
userId={user.id}
paths={{
redirectPath,
}}
/>
); );
} }

View File

@@ -46,8 +46,13 @@ export function MultiFactorChallengeContainer({
const router = useRouter(); const router = useRouter();
const verifyMFAChallenge = useVerifyMFAChallenge({ const verifyMFAChallenge = useVerifyMFAChallenge({
onSuccess: () => { onSuccess: async () => {
try {
await fetch('/api/after-mfa', { method: 'POST' });
router.replace(paths.redirectPath); router.replace(paths.redirectPath);
} catch (err) {
// ignore
}
}, },
}); });

View File

@@ -1,7 +1,5 @@
import 'server-only'; import 'server-only';
import getBaseWebpackConfig from 'next/dist/build/webpack-config';
import { import {
AuthError, AuthError,
type EmailOtpType, type EmailOtpType,
@@ -9,6 +7,8 @@ import {
User, User,
} from '@supabase/supabase-js'; } from '@supabase/supabase-js';
import { checkRequiresMultiFactorAuthentication } from './check-requires-mfa';
/** /**
* @name createAuthCallbackService * @name createAuthCallbackService
* @description Creates an instance of the AuthCallbackService * @description Creates an instance of the AuthCallbackService
@@ -137,10 +137,12 @@ class AuthCallbackService {
| { | {
isSuccess: boolean; isSuccess: boolean;
user: User; user: User;
requiresMultiFactorAuthentication: boolean;
} }
| ErrorURLParameters | ErrorURLParameters
> { > {
let user: User; let user: User;
let requiresMultiFactorAuthentication: boolean;
try { try {
const { data, error } = const { data, error } =
await this.client.auth.exchangeCodeForSession(authCode); await this.client.auth.exchangeCodeForSession(authCode);
@@ -153,8 +155,14 @@ class AuthCallbackService {
}); });
} }
// Handle Keycloak users - set up Medusa integration requiresMultiFactorAuthentication =
if (data?.user && this.isKeycloakUser(data.user)) { await checkRequiresMultiFactorAuthentication(this.client);
if (
!requiresMultiFactorAuthentication &&
data?.user &&
this.isKeycloakUser(data.user)
) {
await this.setupMedusaUserForKeycloak(data.user); await this.setupMedusaUserForKeycloak(data.user);
} }
@@ -179,20 +187,21 @@ class AuthCallbackService {
return { return {
isSuccess: true, isSuccess: true,
user, user,
requiresMultiFactorAuthentication,
}; };
} }
/** /**
* Check if user is from Keycloak provider * Check if user is from Keycloak provider
*/ */
private isKeycloakUser(user: any): boolean { isKeycloakUser(user: any): boolean {
return ( return (
user?.app_metadata?.provider === 'keycloak' || user?.app_metadata?.provider === 'keycloak' ||
user?.app_metadata?.providers?.includes('keycloak') user?.app_metadata?.providers?.includes('keycloak')
); );
} }
private async setupMedusaUserForKeycloak(user: any): Promise<void> { async setupMedusaUserForKeycloak(user: any): Promise<void> {
if (!user.email) { if (!user.email) {
console.warn('Keycloak user has no email, skipping Medusa setup'); console.warn('Keycloak user has no email, skipping Medusa setup');
return; return;
@@ -285,6 +294,7 @@ interface ErrorURLParameters {
error: string; error: string;
code?: string; code?: string;
searchParams: string; searchParams: string;
requiresMultiFactorAuthentication: boolean;
} }
export function getErrorURLParameters({ export function getErrorURLParameters({
@@ -313,6 +323,7 @@ export function getErrorURLParameters({
error: errorMessage, error: errorMessage,
code: code ?? '', code: code ?? '',
searchParams: searchParams.toString(), searchParams: searchParams.toString(),
requiresMultiFactorAuthentication: false,
}; };
} }