feat(auth): include displayName claim in platform access token

Adds an optional displayName claim to the platform access token so
downstream product backends can source the user's display name from the
JWT (single source of truth = platform auth), not from per-product DB
copies. verifyToken already exposes displayName; this populates it at all
token-minting sites (password login, register, refresh, SSO, OAuth,
magic-link, passkeys, QR, enterprise SAML/OIDC). Additive and backward
compatible.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Saravanakumar Dhandapani 2026-05-31 04:00:03 -07:00 committed by Saravanakumar D
parent 6709862c1a
commit 65c7d09584
7 changed files with 13 additions and 0 deletions

View File

@ -223,6 +223,7 @@ export async function enterpriseRoutes(app: FastifyInstance) {
role: user.role as string,
productId: idp.productId,
plan: (user.plan ?? 'free') as 'free' | 'pro' | 'enterprise',
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({
sub: user.id,
@ -332,6 +333,7 @@ export async function enterpriseRoutes(app: FastifyInstance) {
role: user.role as string,
productId: idp.productId,
plan: (user.plan ?? 'free') as 'free' | 'pro' | 'enterprise',
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({
sub: user.id,

View File

@ -17,6 +17,7 @@ export async function createAccessToken(payload: {
role: string;
productId: string;
plan?: 'free' | 'pro' | 'enterprise';
displayName?: string;
}): Promise<string> {
return new SignJWT({ ...payload, type: 'access' })
.setProtectedHeader({ alg: 'HS256' })
@ -44,6 +45,7 @@ export async function verifyToken(token: string): Promise<{
role?: string;
productId?: string;
plan?: 'free' | 'pro' | 'enterprise';
displayName?: string;
type?: string;
}> {
const { payload } = await jwtVerify(token, getSecret(), {
@ -55,6 +57,7 @@ export async function verifyToken(token: string): Promise<{
role?: string;
productId?: string;
plan?: 'free' | 'pro' | 'enterprise';
displayName?: string;
type?: string;
};
}

View File

@ -121,6 +121,7 @@ export async function magicLinkRoutes(app: FastifyInstance) {
role: user.role as string,
productId: user.productId,
plan: (user.plan ?? 'free') as 'free' | 'pro' | 'enterprise',
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({
sub: user.id,

View File

@ -175,6 +175,7 @@ export async function oauthRoutes(app: FastifyInstance) {
role: user.role,
productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });

View File

@ -247,6 +247,7 @@ export async function passkeyRoutes(app: FastifyInstance) {
role: user.role,
productId: body.productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId: body.productId });

View File

@ -70,6 +70,7 @@ export async function qrAuthRoutes(app: FastifyInstance) {
role: user.role as 'user' | 'admin' | 'super_admin',
productId: challenge.productId,
plan: (user.plan ?? 'free') as 'free' | 'pro' | 'enterprise',
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({
sub: user.id,

View File

@ -272,6 +272,7 @@ export async function authRoutes(app: FastifyInstance) {
role: user.role,
productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });
@ -380,6 +381,7 @@ export async function authRoutes(app: FastifyInstance) {
role: user.role,
productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });
@ -464,6 +466,7 @@ export async function authRoutes(app: FastifyInstance) {
role: user.role,
productId: user.productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({
sub: user.id,
@ -679,6 +682,7 @@ export async function authRoutes(app: FastifyInstance) {
role: user.role,
productId,
plan: user.plan,
displayName: user.displayName,
});
const refreshToken = await jwt.createRefreshToken({ sub: user.id, productId });