feat: User onboarding finished

This commit is contained in:
Eric Gullickson
2025-12-23 10:26:10 -06:00
parent 55cf4923b8
commit 96ee43ea94
19 changed files with 698 additions and 67 deletions

View File

@@ -127,4 +127,87 @@ export class AuthService {
throw error;
}
}
/**
* Resend verification email by email address (public endpoint)
* Looks up user by email and sends verification if not verified
*/
async resendVerificationByEmail(email: string): Promise<ResendVerificationResponse> {
try {
// Look up user by email in our database
const userProfile = await this.userProfileRepository.getByEmail(email);
if (!userProfile) {
// Don't reveal if email exists - return success message regardless
logger.info('Resend verification requested for unknown email', {
email: email.substring(0, 3) + '***',
});
return {
message: 'If an account exists with this email, a verification link will be sent.',
};
}
// Check if already verified via Auth0
const verified = await auth0ManagementClient.checkEmailVerified(userProfile.auth0Sub);
if (verified) {
logger.info('Email already verified, skipping resend', { email: email.substring(0, 3) + '***' });
return {
message: 'If an account exists with this email, a verification link will be sent.',
};
}
// Request Auth0 to resend verification email
await auth0ManagementClient.resendVerificationEmail(userProfile.auth0Sub);
logger.info('Verification email resent via public endpoint', {
email: email.substring(0, 3) + '***',
});
return {
message: 'If an account exists with this email, a verification link will be sent.',
};
} catch (error) {
logger.error('Failed to resend verification email by email', {
email: email.substring(0, 3) + '***',
error,
});
// Don't reveal error details - return generic success for security
return {
message: 'If an account exists with this email, a verification link will be sent.',
};
}
}
/**
* Get user status for routing decisions
* Returns email verification and onboarding completion status
*/
async getUserStatus(auth0Sub: string): Promise<{
emailVerified: boolean;
onboardingCompleted: boolean;
email: string;
}> {
try {
// Get user details from Auth0
const auth0User = await auth0ManagementClient.getUser(auth0Sub);
// Get local profile for onboarding status
const localProfile = await this.userProfileRepository.getByAuth0Sub(auth0Sub);
// Sync email verification status if needed
if (localProfile && localProfile.emailVerified !== auth0User.emailVerified) {
await this.userProfileRepository.updateEmailVerified(auth0Sub, auth0User.emailVerified);
}
return {
emailVerified: auth0User.emailVerified,
onboardingCompleted: localProfile?.onboardingCompletedAt !== null,
email: auth0User.email,
};
} catch (error) {
logger.error('Failed to get user status', { auth0Sub, error });
throw error;
}
}
}