Fondation

This commit is contained in:
alma 2026-01-16 21:58:17 +01:00
parent c341468996
commit c4267a0eeb
8 changed files with 91 additions and 67 deletions

View File

@ -527,7 +527,7 @@ export const authOptions: NextAuthOptions = {
session.refreshToken = token.refreshToken as string | undefined; session.refreshToken = token.refreshToken as string | undefined;
logger.debug('[SESSION_CALLBACK] Session created', { logger.debug('[SESSION_CALLBACK] Session created', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
hasEmail: !!session.user.email, hasEmail: !!session.user.email,
rolesCount: session.user.role.length, rolesCount: session.user.role.length,
}); });

View File

@ -36,7 +36,7 @@ export async function GET(req: NextRequest) {
const cachedData = await getCachedCalendarData(session.user.id); const cachedData = await getCachedCalendarData(session.user.id);
if (cachedData) { if (cachedData) {
logger.debug('[CALENDAR] Using cached calendar data', { logger.debug('[CALENDAR] Using cached calendar data', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
calendarCount: cachedData.length, calendarCount: cachedData.length,
}); });
return NextResponse.json(cachedData); return NextResponse.json(cachedData);
@ -45,7 +45,7 @@ export async function GET(req: NextRequest) {
// If no cache or forcing refresh, fetch from database // If no cache or forcing refresh, fetch from database
logger.debug('[CALENDAR] Fetching calendar data from database', { logger.debug('[CALENDAR] Fetching calendar data from database', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
// Ensure user has a default private calendar (created automatically if missing) // Ensure user has a default private calendar (created automatically if missing)
@ -70,7 +70,7 @@ export async function GET(req: NextRequest) {
} }
}); });
logger.debug('[CALENDAR] Created default private calendar', { logger.debug('[CALENDAR] Created default private calendar', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
} }
@ -201,7 +201,7 @@ export async function GET(req: NextRequest) {
}); });
logger.debug('[CALENDAR] Fetched calendars with events', { logger.debug('[CALENDAR] Fetched calendars with events', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
personalCount: filteredPersonalCalendars.length, personalCount: filteredPersonalCalendars.length,
missionCount: missionCalendars.length, missionCount: missionCalendars.length,
totalCount: sortedCalendars.length, totalCount: sortedCalendars.length,

View File

@ -58,12 +58,17 @@ async function ensureUserExists(session: any): Promise<void> {
}); });
if (existingUser) { if (existingUser) {
logger.debug('[COURRIER_ACCOUNT] User already exists', { userId }); logger.debug('[COURRIER_ACCOUNT] User already exists', {
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
});
return; return;
} }
// User doesn't exist, create it // User doesn't exist, create it
logger.debug('[COURRIER_ACCOUNT] User not found, creating from session data', { userId, email: userEmail.substring(0, 5) + '***' }); logger.debug('[COURRIER_ACCOUNT] User not found, creating from session data', {
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
emailHash: Buffer.from(userEmail.toLowerCase()).toString('base64').slice(0, 12),
});
// Generate a temporary random password (not used for auth, Keycloak handles that) // Generate a temporary random password (not used for auth, Keycloak handles that)
const tempPassword = await bcrypt.hash(Math.random().toString(36).slice(-10), 10); const tempPassword = await bcrypt.hash(Math.random().toString(36).slice(-10), 10);
@ -78,15 +83,20 @@ async function ensureUserExists(session: any): Promise<void> {
} }
}); });
logger.debug('[COURRIER_ACCOUNT] Successfully created user', { userId, email: userEmail.substring(0, 5) + '***' }); logger.debug('[COURRIER_ACCOUNT] Successfully created user', {
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
emailHash: Buffer.from(userEmail.toLowerCase()).toString('base64').slice(0, 12),
});
} catch (error) { } catch (error) {
logger.error('[COURRIER_ACCOUNT] Error ensuring user exists', { logger.error('[COURRIER_ACCOUNT] Error ensuring user exists', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
error: error instanceof Error ? error.message : String(error) error: error instanceof Error ? error.message : String(error)
}); });
// If it's a unique constraint error, user might have been created by another request // If it's a unique constraint error, user might have been created by another request
if (error instanceof Error && error.message.includes('Unique constraint')) { if (error instanceof Error && error.message.includes('Unique constraint')) {
logger.debug('[COURRIER_ACCOUNT] User may have been created by concurrent request', { userId }); logger.debug('[COURRIER_ACCOUNT] User may have been created by concurrent request', {
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
});
return; return;
} }
throw error; throw error;

View File

@ -46,11 +46,11 @@ export async function GET(request: Request) {
// CRITICAL FIX: Log exact parameters received by the API // CRITICAL FIX: Log exact parameters received by the API
logger.debug('[COURRIER_API] Received request', { logger.debug('[COURRIER_API] Received request', {
folder, folder,
accountId, accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : null,
page, page,
checkOnly, checkOnly,
refresh, refresh,
userId: session.user.id userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
// CRITICAL FIX: More robust parameter normalization // CRITICAL FIX: More robust parameter normalization
@ -74,15 +74,15 @@ export async function GET(request: Request) {
// CRITICAL FIX: Enhanced logging for parameter resolution // CRITICAL FIX: Enhanced logging for parameter resolution
logger.debug('[COURRIER_API] Using normalized parameters', { logger.debug('[COURRIER_API] Using normalized parameters', {
folder: normalizedFolder, folder: normalizedFolder,
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
userId: session.user.id userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
// If refresh=true, invalidate cache before fetching // If refresh=true, invalidate cache before fetching
if (refresh) { if (refresh) {
logger.debug('[COURRIER_API] Refresh requested - invalidating cache', { logger.debug('[COURRIER_API] Refresh requested - invalidating cache', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
folder: normalizedFolder folder: normalizedFolder
}); });
await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder); await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder);
@ -92,8 +92,8 @@ export async function GET(request: Request) {
if (!searchQuery && !checkOnly && !refresh) { if (!searchQuery && !checkOnly && !refresh) {
// CRITICAL FIX: Use consistent cache key format with the correct account ID // CRITICAL FIX: Use consistent cache key format with the correct account ID
logger.debug('[COURRIER_API] Checking Redis cache', { logger.debug('[COURRIER_API] Checking Redis cache', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
folder: normalizedFolder, folder: normalizedFolder,
page, page,
perPage perPage
@ -107,8 +107,8 @@ export async function GET(request: Request) {
); );
if (cachedEmails) { if (cachedEmails) {
logger.debug('[COURRIER_API] Using Redis cached emails', { logger.debug('[COURRIER_API] Using Redis cached emails', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
folder: normalizedFolder, folder: normalizedFolder,
page, page,
perPage perPage
@ -118,8 +118,8 @@ export async function GET(request: Request) {
} }
logger.debug('[COURRIER_API] Redis cache miss, fetching from IMAP', { logger.debug('[COURRIER_API] Redis cache miss, fetching from IMAP', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
folder: normalizedFolder, folder: normalizedFolder,
page, page,
perPage perPage
@ -139,8 +139,8 @@ export async function GET(request: Request) {
// CRITICAL FIX: Log when emails are returned from IMAP // CRITICAL FIX: Log when emails are returned from IMAP
logger.debug('[COURRIER_API] Successfully fetched emails from IMAP', { logger.debug('[COURRIER_API] Successfully fetched emails from IMAP', {
count: emailsResult.emails.length, count: emailsResult.emails.length,
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
userId: session.user.id userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
// The result is already cached in the getEmails function (if not checkOnly) // The result is already cached in the getEmails function (if not checkOnly)
@ -148,7 +148,7 @@ export async function GET(request: Request) {
} catch (error: any) { } catch (error: any) {
logger.error('[COURRIER_API] Error fetching emails', { logger.error('[COURRIER_API] Error fetching emails', {
error: error instanceof Error ? error.message : String(error), error: error instanceof Error ? error.message : String(error),
userId: session?.user?.id userIdHash: session?.user?.id ? Buffer.from(session.user.id).toString('base64').slice(0, 12) : null,
}); });
return NextResponse.json( return NextResponse.json(
{ error: "Failed to fetch emails", message: error.message }, { error: "Failed to fetch emails", message: error.message },
@ -180,8 +180,8 @@ export async function POST(request: Request) {
// Log the cache invalidation operation // Log the cache invalidation operation
logger.debug('[COURRIER_API] Invalidating cache', { logger.debug('[COURRIER_API] Invalidating cache', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
accountId: effectiveAccountId, accountIdHash: Buffer.from(effectiveAccountId).toString('base64').slice(0, 12),
folder: normalizedFolder || 'all folders' folder: normalizedFolder || 'all folders'
}); });

View File

@ -151,7 +151,7 @@ export async function GET() {
// Get user with their accounts // Get user with their accounts
logger.debug('[COURRIER_SESSION] Fetching user with ID', { logger.debug('[COURRIER_SESSION] Fetching user with ID', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
const user = await prisma.user.findUnique({ const user = await prisma.user.findUnique({
where: { id: session.user.id }, where: { id: session.user.id },
@ -171,7 +171,7 @@ export async function GET() {
const accounts = (user.mailCredentials || []) as MailCredentials[]; const accounts = (user.mailCredentials || []) as MailCredentials[];
if (accounts.length === 0) { if (accounts.length === 0) {
logger.debug('[COURRIER_SESSION] No email accounts found for user', { logger.debug('[COURRIER_SESSION] No email accounts found for user', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
}); });
return NextResponse.json({ return NextResponse.json({
authenticated: true, authenticated: true,
@ -182,9 +182,8 @@ export async function GET() {
} }
logger.debug('[COURRIER_SESSION] Found accounts for user', { logger.debug('[COURRIER_SESSION] Found accounts for user', {
userId: session.user.id, userIdHash: Buffer.from(session.user.id).toString('base64').slice(0, 12),
count: accounts.length, count: accounts.length,
emails: accounts.map(a => a.email),
}); });
// Fetch folders for each account // Fetch folders for each account

View File

@ -92,7 +92,7 @@ export async function GET(request: Request) {
const username = session.user.email.split('@')[0]; const username = session.user.email.split('@')[0];
if (!username) { if (!username) {
logger.error('[ROCKET_CHAT] No username found in session email', { logger.error('[ROCKET_CHAT] No username found in session email', {
email: session.user.email, emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
}); });
return NextResponse.json({ messages: [] }, { status: 200 }); return NextResponse.json({ messages: [] }, { status: 200 });
} }
@ -123,15 +123,13 @@ export async function GET(request: Request) {
if (!currentUser) { if (!currentUser) {
logger.error('[ROCKET_CHAT] User not found in users list', { logger.error('[ROCKET_CHAT] User not found in users list', {
username, emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
email: session.user.email,
}); });
return NextResponse.json({ messages: [] }, { status: 200 }); return NextResponse.json({ messages: [] }, { status: 200 });
} }
logger.debug('[ROCKET_CHAT] Found user', { logger.debug('[ROCKET_CHAT] Found user', {
username: currentUser.username, emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
id: currentUser._id,
}); });
// Step 3: Create a token for the current user // Step 3: Create a token for the current user
@ -207,8 +205,7 @@ export async function GET(request: Request) {
sum + (sub.unread || 0), 0); sum + (sub.unread || 0), 0);
logger.debug('[ROCKET_CHAT] Filtered user subscriptions', { logger.debug('[ROCKET_CHAT] Filtered user subscriptions', {
userId: currentUser._id, emailHash: Buffer.from(session.user.email.toLowerCase()).toString('base64').slice(0, 12),
username: currentUser.username,
totalSubscriptions: userSubscriptions.length, totalSubscriptions: userSubscriptions.length,
totalUnreadCount: totalUnreadCount, totalUnreadCount: totalUnreadCount,
}); });

View File

@ -190,21 +190,23 @@ export async function getImapConnection(
totalConnectionRequests++; totalConnectionRequests++;
logger.debug('[IMAP] getImapConnection called', { logger.debug('[IMAP] getImapConnection called', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId: accountId ?? 'default', accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
}); });
// Special handling for 'default' accountId - find the first available account // Special handling for 'default' accountId - find the first available account
if (!accountId || accountId === 'default') { if (!accountId || accountId === 'default') {
logger.debug('[IMAP] Resolving default accountId', { userId }); logger.debug('[IMAP] Resolving default accountId', {
userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
});
// Try getting the account ID from cache to avoid database query // Try getting the account ID from cache to avoid database query
const sessionData = await getCachedImapSession(userId); const sessionData = await getCachedImapSession(userId);
if (sessionData && sessionData.defaultAccountId) { if (sessionData && sessionData.defaultAccountId) {
accountId = sessionData.defaultAccountId; accountId = sessionData.defaultAccountId;
logger.debug('[IMAP] Using cached default account ID', { logger.debug('[IMAP] Using cached default account ID', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId, accountIdHash: Buffer.from(accountId).toString('base64').slice(0, 12),
}); });
} else { } else {
// Query to find all accounts for this user // Query to find all accounts for this user
@ -217,9 +219,8 @@ export async function getImapConnection(
if (accounts && accounts.length > 0) { if (accounts && accounts.length > 0) {
const firstAccount = accounts[0]; const firstAccount = accounts[0];
logger.debug('[IMAP] Using first available account from DB', { logger.debug('[IMAP] Using first available account from DB', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId: firstAccount.id, accountIdHash: Buffer.from(firstAccount.id).toString('base64').slice(0, 12),
email: firstAccount.email,
}); });
accountId = firstAccount.id; accountId = firstAccount.id;
@ -304,21 +305,23 @@ export async function getImapConnection(
} }
// If we get here, we need a new connection // If we get here, we need a new connection
logger.debug('[IMAP] Creating new connection', { connectionKey }); logger.debug('[IMAP] Creating new connection', {
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
});
// First try to get credentials from Redis cache // First try to get credentials from Redis cache
let credentials = await getCachedEmailCredentials(userId, accountId); let credentials = await getCachedEmailCredentials(userId, accountId);
logger.debug('[IMAP] Retrieved credentials from Redis cache', { logger.debug('[IMAP] Retrieved credentials from Redis cache', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId, accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
found: !!credentials, found: !!credentials,
}); });
// If not in cache, get from database and cache them // If not in cache, get from database and cache them
if (!credentials) { if (!credentials) {
logger.debug('[IMAP] Credentials not found in cache, querying database', { logger.debug('[IMAP] Credentials not found in cache, querying database', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId, accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
}); });
// Fetch directly from database // Fetch directly from database
@ -333,17 +336,16 @@ export async function getImapConnection(
if (!dbCredentials) { if (!dbCredentials) {
logger.error('[IMAP] No credentials found for user', { logger.error('[IMAP] No credentials found for user', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId, accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
}); });
totalConnectionErrors++; totalConnectionErrors++;
throw new Error('Email account credentials not found'); throw new Error('Email account credentials not found');
} }
logger.debug('[IMAP] Database lookup returned credentials', { logger.debug('[IMAP] Database lookup returned credentials', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
accountId, accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
email: dbCredentials.email,
hasPassword: !!dbCredentials.password, hasPassword: !!dbCredentials.password,
}); });
@ -523,7 +525,7 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
const extendedCreds = credentials as EmailCredentialsExtended; const extendedCreds = credentials as EmailCredentialsExtended;
logger.debug('[IMAP] Creating ImapFlow client with credentials metadata', { logger.debug('[IMAP] Creating ImapFlow client with credentials metadata', {
email: extendedCreds.email, emailHash: Buffer.from(extendedCreds.email || '').toString('base64').slice(0, 12),
host: extendedCreds.host, host: extendedCreds.host,
port: extendedCreds.port, port: extendedCreds.port,
hasPassword: !!extendedCreds.password, hasPassword: !!extendedCreds.password,
@ -537,7 +539,9 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
// Check if we have valid OAuth tokens // Check if we have valid OAuth tokens
if (extendedCreds.useOAuth && extendedCreds.accessToken) { if (extendedCreds.useOAuth && extendedCreds.accessToken) {
logger.debug('[IMAP] Using XOAUTH2 authentication', { connectionKey }); logger.debug('[IMAP] Using XOAUTH2 authentication', {
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
});
// Set auth parameters for ImapFlow // Set auth parameters for ImapFlow
authParams = { authParams = {
@ -545,10 +549,14 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
accessToken: extendedCreds.accessToken accessToken: extendedCreds.accessToken
}; };
logger.debug('[IMAP] XOAUTH2 auth configured', { connectionKey }); logger.debug('[IMAP] XOAUTH2 auth configured', {
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
});
} else if (extendedCreds.password) { } else if (extendedCreds.password) {
// Use regular password authentication // Use regular password authentication
logger.debug('[IMAP] Using password authentication', { connectionKey }); logger.debug('[IMAP] Using password authentication', {
connectionKeyHash: Buffer.from(connectionKey).toString('base64').slice(0, 12),
});
authParams = { authParams = {
user: extendedCreds.email, user: extendedCreds.email,
pass: extendedCreds.password pass: extendedCreds.password
@ -928,11 +936,11 @@ export async function getEmails(
): Promise<EmailListResult> { ): Promise<EmailListResult> {
// Normalize folder name and handle account ID // Normalize folder name and handle account ID
logger.debug('[EMAIL] getEmails called', { logger.debug('[EMAIL] getEmails called', {
userId, userIdHash: Buffer.from(userId).toString('base64').slice(0, 12),
folder, folder,
page, page,
perPage, perPage,
accountId: accountId || 'default', accountIdHash: accountId ? Buffer.from(accountId).toString('base64').slice(0, 12) : 'default',
checkOnly, checkOnly,
}); });

View File

@ -20,7 +20,9 @@ export async function ensureFreshToken(
): Promise<{ accessToken: string; success: boolean }> { ): Promise<{ accessToken: string; success: boolean }> {
try { try {
// Try Redis first (fast path) // Try Redis first (fast path)
logger.debug('[TOKEN_REFRESH] Checking if token refresh is needed', { email: email.substring(0, 5) + '***' }); logger.debug('[TOKEN_REFRESH] Checking if token refresh is needed', {
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
});
const redis = getRedisClient(); const redis = getRedisClient();
const key = KEYS.CREDENTIALS(userId, email); const key = KEYS.CREDENTIALS(userId, email);
let credStr = await redis.get(key); let credStr = await redis.get(key);
@ -30,7 +32,9 @@ export async function ensureFreshToken(
creds = JSON.parse(credStr); creds = JSON.parse(credStr);
} else { } else {
// Redis cache miss - fallback to Prisma database // Redis cache miss - fallback to Prisma database
logger.debug('[TOKEN_REFRESH] No credentials in Redis, checking Prisma', { email: email.substring(0, 5) + '***' }); logger.debug('[TOKEN_REFRESH] No credentials in Redis, checking Prisma', {
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
});
const account = await prisma.mailCredentials.findFirst({ const account = await prisma.mailCredentials.findFirst({
where: { where: {
userId: userId, userId: userId,
@ -54,16 +58,22 @@ export async function ensureFreshToken(
// Re-populate Redis cache // Re-populate Redis cache
await redis.set(key, JSON.stringify(creds), 'EX', 86400); await redis.set(key, JSON.stringify(creds), 'EX', 86400);
logger.debug('[TOKEN_REFRESH] Recovered credentials from Prisma and cached in Redis', { email: email.substring(0, 5) + '***' }); logger.debug('[TOKEN_REFRESH] Recovered credentials from Prisma and cached in Redis', {
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
});
} else { } else {
logger.debug('[TOKEN_REFRESH] No OAuth credentials found in database', { email: email.substring(0, 5) + '***' }); logger.debug('[TOKEN_REFRESH] No OAuth credentials found in database', {
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
});
return { accessToken: '', success: false }; return { accessToken: '', success: false };
} }
} }
// If not OAuth or missing refresh token, return failure // If not OAuth or missing refresh token, return failure
if (!creds.useOAuth || !creds.refreshToken) { if (!creds.useOAuth || !creds.refreshToken) {
logger.debug('[TOKEN_REFRESH] Account not using OAuth or missing refresh token', { email: email.substring(0, 5) + '***' }); logger.debug('[TOKEN_REFRESH] Account not using OAuth or missing refresh token', {
emailHash: Buffer.from(email.toLowerCase()).toString('base64').slice(0, 12),
});
return { accessToken: '', success: false }; return { accessToken: '', success: false };
} }