Add JWT configuration and improve email error handling
Build And Push Image / docker (push) Successful in 2m51s Details

- Add jwtSecret to runtime config with fallback to sessionSecret
- Enhance email error tracking in portal account creation API
- Fix jsonwebtoken imports and improve type safety
- Include detailed email error information in API responses
This commit is contained in:
Matt 2025-08-09 16:55:59 +02:00
parent 97653b7307
commit bff89bd89d
3 changed files with 19 additions and 10 deletions

View File

@ -127,6 +127,7 @@ export default defineNuxtConfig({
}, },
sessionSecret: process.env.NUXT_SESSION_SECRET || "", sessionSecret: process.env.NUXT_SESSION_SECRET || "",
encryptionKey: process.env.NUXT_ENCRYPTION_KEY || "", encryptionKey: process.env.NUXT_ENCRYPTION_KEY || "",
jwtSecret: process.env.NUXT_JWT_SECRET || process.env.NUXT_SESSION_SECRET || "",
public: { public: {
// Client-side configuration // Client-side configuration
appName: "MonacoUSA Portal", appName: "MonacoUSA Portal",

View File

@ -119,6 +119,8 @@ export default defineEventHandler(async (event) => {
// 9. Send welcome/verification email using our custom email system // 9. Send welcome/verification email using our custom email system
console.log('[api/members/[id]/create-portal-account.post] Attempting to send welcome/verification email...'); console.log('[api/members/[id]/create-portal-account.post] Attempting to send welcome/verification email...');
let emailSent = false; let emailSent = false;
let emailError: string | null = null;
try { try {
const { getEmailService } = await import('~/server/utils/email'); const { getEmailService } = await import('~/server/utils/email');
const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens'); const { generateEmailVerificationToken } = await import('~/server/utils/email-tokens');
@ -137,8 +139,13 @@ export default defineEventHandler(async (event) => {
emailSent = true; emailSent = true;
console.log('[api/members/[id]/create-portal-account.post] Welcome email sent successfully'); console.log('[api/members/[id]/create-portal-account.post] Welcome email sent successfully');
} catch (emailError: any) { } catch (error: any) {
console.error('[api/members/[id]/create-portal-account.post] Failed to send welcome email:', emailError.message); emailError = error.message || 'Unknown email error';
console.error('[api/members/[id]/create-portal-account.post] Failed to send welcome email:', emailError);
// Log the full error for debugging
console.error('[api/members/[id]/create-portal-account.post] Full email error:', error);
// Don't fail the account creation if email fails - user can resend verification email later // Don't fail the account creation if email fails - user can resend verification email later
} }
@ -148,13 +155,14 @@ export default defineEventHandler(async (event) => {
success: true, success: true,
message: emailSent message: emailSent
? 'Portal account created successfully. The member will receive an email to verify their account and set their password.' ? 'Portal account created successfully. The member will receive an email to verify their account and set their password.'
: 'Portal account created successfully. Email sending is not configured - the member will need to request a password reset to access their account.', : `Portal account created successfully. Email sending failed: ${emailError || 'Unknown error'}. The member can use "Forgot Password" to access their account.`,
data: { data: {
keycloak_id: keycloakId, keycloak_id: keycloakId,
member_id: memberId, member_id: memberId,
email: member.email, email: member.email,
name: `${member.first_name} ${member.last_name}`, name: `${member.first_name} ${member.last_name}`,
email_sent: emailSent email_sent: emailSent,
email_error: emailError
} }
}; };

View File

@ -1,4 +1,4 @@
import { sign, verify } from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
export interface EmailVerificationTokenPayload { export interface EmailVerificationTokenPayload {
userId: string; userId: string;
@ -27,7 +27,7 @@ export async function generateEmailVerificationToken(userId: string, email: stri
iat: Date.now() iat: Date.now()
}; };
const token = sign(payload, runtimeConfig.jwtSecret, { const token = jwt.sign(payload, runtimeConfig.jwtSecret as string, {
expiresIn: '24h', expiresIn: '24h',
issuer: 'monacousa-portal', issuer: 'monacousa-portal',
audience: 'email-verification' audience: 'email-verification'
@ -62,10 +62,10 @@ export async function verifyEmailToken(token: string): Promise<{ userId: string;
try { try {
// Verify JWT signature and expiration // Verify JWT signature and expiration
const decoded = verify(token, runtimeConfig.jwtSecret, { const decoded = jwt.verify(token, runtimeConfig.jwtSecret as string, {
issuer: 'monacousa-portal', issuer: 'monacousa-portal',
audience: 'email-verification' audience: 'email-verification'
}) as EmailVerificationTokenPayload; }) as any as EmailVerificationTokenPayload;
// Validate token purpose // Validate token purpose
if (decoded.purpose !== 'email-verification') { if (decoded.purpose !== 'email-verification') {
@ -118,10 +118,10 @@ export async function isTokenValid(token: string): Promise<boolean> {
return false; return false;
} }
const decoded = verify(token, runtimeConfig.jwtSecret, { const decoded = jwt.verify(token, runtimeConfig.jwtSecret as string, {
issuer: 'monacousa-portal', issuer: 'monacousa-portal',
audience: 'email-verification' audience: 'email-verification'
}) as EmailVerificationTokenPayload; }) as any as EmailVerificationTokenPayload;
return decoded.purpose === 'email-verification' && activeTokens.has(token); return decoded.purpose === 'email-verification' && activeTokens.has(token);
} catch (error) { } catch (error) {