From 5c688d713e46bbe1c5da214c722447fcb226223f Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:05:44 +0100 Subject: [PATCH] fix(mana-core-auth): return real refreshToken in SSO session-to-token exchange The sessionToToken method was incorrectly returning the session cookie token instead of the actual refreshToken from the database. This caused "No refresh token available" errors when users logged in via SSO (cross-domain cookie) because the /api/v1/auth/refresh endpoint expects the refreshToken field from the sessions table, not the cookie token. Now the method: - Fetches the session from database by cookie token - Uses existing refreshToken if available - Generates and stores a new refreshToken if missing - Returns the actual refreshToken that works with token refresh Co-Authored-By: Claude Opus 4.5 --- .../src/auth/services/better-auth.service.ts | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/services/mana-core-auth/src/auth/services/better-auth.service.ts b/services/mana-core-auth/src/auth/services/better-auth.service.ts index 7251755ec..3c602ddb9 100644 --- a/services/mana-core-auth/src/auth/services/better-auth.service.ts +++ b/services/mana-core-auth/src/auth/services/better-auth.service.ts @@ -1479,6 +1479,46 @@ export class BetterAuthService { email: user.email, }); + // Get the actual session from database to retrieve the real refreshToken + const db = getDb(this.databaseUrl); + const { sessions } = await import('../../db/schema'); + const { eq } = await import('drizzle-orm'); + const { nanoid } = await import('nanoid'); + + // Find the session by its token (session.token is the cookie token) + const [dbSession] = await db + .select() + .from(sessions) + .where(eq(sessions.token, session.token || sessionToken)) + .limit(1); + + let actualRefreshToken: string; + + if (dbSession?.refreshToken) { + // Session already has a refreshToken - use it + actualRefreshToken = dbSession.refreshToken; + this.logger.debug('SSO: Using existing refreshToken from session'); + } else if (dbSession) { + // Session exists but no refreshToken - generate one and update the session + actualRefreshToken = nanoid(64); + const refreshTokenExpiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 days + + await db + .update(sessions) + .set({ + refreshToken: actualRefreshToken, + refreshTokenExpiresAt, + updatedAt: new Date(), + }) + .where(eq(sessions.id, dbSession.id)); + + this.logger.debug('SSO: Generated new refreshToken for existing session'); + } else { + // No session found in DB - this shouldn't happen, but handle it + this.logger.warn('SSO: Session not found in database, using session token as fallback'); + actualRefreshToken = session.token || sessionToken; + } + // Generate JWT access token using Better Auth's JWT plugin let accessToken = ''; try { @@ -1520,7 +1560,7 @@ export class BetterAuthService { role: user.role, }, accessToken, - refreshToken: session.token || sessionToken, + refreshToken: actualRefreshToken, expiresIn: 15 * 60, // 15 minutes in seconds }; } catch (error) {