Refactor email verification to use JSON responses instead of redirects
Build And Push Image / docker (push) Successful in 2m54s
Details
Build And Push Image / docker (push) Successful in 2m54s
Details
- Replace server-side redirects with JSON API responses for better error handling - Add support for partial success when Keycloak update fails but token is valid - Improve error messages with specific status codes (410 for expired, 409 for already used) - Extract email from API response instead of URL query parameters - Enable client-side navigation with proper error state management
This commit is contained in:
parent
2b2cd5891f
commit
623ad9c3fd
|
|
@ -173,27 +173,45 @@ const verifyEmail = async () => {
|
||||||
// Call the API endpoint to verify the email
|
// Call the API endpoint to verify the email
|
||||||
const response = await $fetch(`/api/auth/verify-email?token=${token.value}`, {
|
const response = await $fetch(`/api/auth/verify-email?token=${token.value}`, {
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
}) as any;
|
||||||
|
|
||||||
console.log('[auth/verify] Email verification successful:', response);
|
console.log('[auth/verify] Email verification successful:', response);
|
||||||
|
|
||||||
|
// Extract email from response
|
||||||
|
const email = response?.data?.email || '';
|
||||||
|
const partialSuccess = response?.data?.partialSuccess || false;
|
||||||
|
|
||||||
// Redirect to success page with email info
|
// Redirect to success page with email info
|
||||||
const email = route.query.email || '';
|
let redirectUrl = `/auth/verify-success`;
|
||||||
const redirectUrl = `/auth/verify-success${email ? `?email=${encodeURIComponent(email as string)}` : ''}`;
|
const queryParams = [];
|
||||||
|
|
||||||
|
if (email) {
|
||||||
|
queryParams.push(`email=${encodeURIComponent(email)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partialSuccess) {
|
||||||
|
queryParams.push('warning=partial');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
redirectUrl += '?' + queryParams.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
await navigateTo(redirectUrl);
|
await navigateTo(redirectUrl);
|
||||||
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
console.error('[auth/verify] Email verification failed:', err);
|
console.error('[auth/verify] Email verification failed:', err);
|
||||||
|
|
||||||
if (err.statusCode === 400) {
|
if (err.statusCode === 410) {
|
||||||
error.value = 'Invalid or expired verification token. Please request a new verification email.';
|
error.value = 'Verification link has expired. Please request a new verification email.';
|
||||||
|
} else if (err.statusCode === 409) {
|
||||||
|
error.value = 'This verification link has already been used or is invalid.';
|
||||||
|
} else if (err.statusCode === 400) {
|
||||||
|
error.value = 'Invalid verification token. Please request a new verification email.';
|
||||||
} else if (err.statusCode === 404) {
|
} else if (err.statusCode === 404) {
|
||||||
error.value = 'User not found. The verification token may be invalid.';
|
error.value = 'User not found. The verification token may be invalid.';
|
||||||
} else if (err.statusCode === 409) {
|
|
||||||
error.value = 'Email is already verified. You can now log in to your account.';
|
|
||||||
} else {
|
} else {
|
||||||
error.value = err.message || 'Email verification failed. Please try again or contact support.';
|
error.value = err.data?.message || err.message || 'Email verification failed. Please try again or contact support.';
|
||||||
}
|
}
|
||||||
|
|
||||||
verifying.value = false;
|
verifying.value = false;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ export default defineEventHandler(async (event) => {
|
||||||
// Update user verification status in Keycloak
|
// Update user verification status in Keycloak
|
||||||
const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin');
|
const { createKeycloakAdminClient } = await import('~/server/utils/keycloak-admin');
|
||||||
const keycloak = createKeycloakAdminClient();
|
const keycloak = createKeycloakAdminClient();
|
||||||
|
|
||||||
|
let partialSuccess = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await keycloak.updateUserProfile(userId, {
|
await keycloak.updateUserProfile(userId, {
|
||||||
|
|
@ -29,27 +31,38 @@ export default defineEventHandler(async (event) => {
|
||||||
|
|
||||||
console.log('[verify-email] Successfully verified user:', userId, 'email:', email);
|
console.log('[verify-email] Successfully verified user:', userId, 'email:', email);
|
||||||
|
|
||||||
// Redirect to success page
|
|
||||||
return sendRedirect(event, '/auth/verify-success?email=' + encodeURIComponent(email), 302);
|
|
||||||
|
|
||||||
} catch (keycloakError: any) {
|
} catch (keycloakError: any) {
|
||||||
console.error('[verify-email] Keycloak update failed:', keycloakError.message);
|
console.error('[verify-email] Keycloak update failed:', keycloakError.message);
|
||||||
|
|
||||||
// Even if Keycloak update fails, consider verification successful if token was valid
|
// Even if Keycloak update fails, consider verification successful if token was valid
|
||||||
// This prevents user frustration due to backend issues
|
// This prevents user frustration due to backend issues
|
||||||
return sendRedirect(event, '/auth/verify-success?email=' + encodeURIComponent(email) + '&warning=partial', 302);
|
partialSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return JSON response for client-side navigation
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
partialSuccess
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('[verify-email] Verification failed:', error.message);
|
console.error('[verify-email] Verification failed:', error.message);
|
||||||
|
|
||||||
// Handle different error types with appropriate redirects
|
// Return error response
|
||||||
if (error.message?.includes('expired')) {
|
if (error.message?.includes('expired')) {
|
||||||
return sendRedirect(event, '/auth/verify-expired', 302);
|
throw createError({
|
||||||
|
statusCode: 410,
|
||||||
|
statusMessage: 'Verification link has expired. Please request a new one.'
|
||||||
|
});
|
||||||
} else if (error.message?.includes('already used') || error.message?.includes('not found')) {
|
} else if (error.message?.includes('already used') || error.message?.includes('not found')) {
|
||||||
return sendRedirect(event, '/auth/verify-expired?reason=used', 302);
|
throw createError({
|
||||||
|
statusCode: 409,
|
||||||
|
statusMessage: 'This verification link has already been used or is invalid.'
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// For other errors, show a generic error page
|
|
||||||
throw createError({
|
throw createError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
statusMessage: error.message || 'Invalid verification link'
|
statusMessage: error.message || 'Invalid verification link'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue