diff --git a/cannamanage-frontend/src/lib/auth.ts b/cannamanage-frontend/src/lib/auth.ts index ad34a71..3bc7d8f 100644 --- a/cannamanage-frontend/src/lib/auth.ts +++ b/cannamanage-frontend/src/lib/auth.ts @@ -2,6 +2,20 @@ import NextAuth from "next-auth" import Credentials from "next-auth/providers/credentials" +/** + * Decode a JWT payload (no signature verification — we only need the claims for + * populating the session). Returns {} on any parse failure. + */ +function decodeJwtPayload(token: string): Record { + try { + const payload = token.split(".")[1] + const json = Buffer.from(payload, "base64url").toString("utf8") + return JSON.parse(json) as Record + } catch { + return {} + } +} + /** Helper: fetch with an AbortController timeout (default 5s) */ async function fetchWithTimeout( url: string, @@ -42,14 +56,17 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ if (!res.ok) return null + // Backend LoginResponse is flat: { accessToken, refreshToken, expiresIn, role }. + // Identity claims (sub=userId, email, tenant_id=clubId) live inside the JWT. const data = await res.json() + const claims = decodeJwtPayload(data.accessToken) return { - id: data.member.id, - email: data.member.email, - name: data.member.clubName, - role: data.member.role, - clubId: data.member.clubId, + id: (claims.sub as string) ?? data.accessToken, + email: (claims.email as string) ?? credentials.email, + name: (claims.email as string) ?? credentials.email, + role: data.role ?? (claims.role as string), + clubId: (claims.tenant_id as string) ?? "", accessToken: data.accessToken, refreshToken: data.refreshToken, expiresAt: Date.now() + data.expiresIn * 1000,