From b2ac809e5e2d394414bcaeb53e4754ad4cff4e69 Mon Sep 17 00:00:00 2001 From: Grant G Date: Tue, 5 Aug 2025 21:17:42 -0700 Subject: [PATCH] feat(oidc-provider): add `client` to getAdditionalUserInfoClaim callback (#3790) * feat: add oidc Client to getAdditionalUserInfoClaim * address cubic comments * run format --- docs/content/docs/plugins/oidc-provider.mdx | 4 ++-- packages/better-auth/src/plugins/mcp/index.ts | 6 ++++- .../better-auth/src/plugins/mcp/mcp.test.ts | 2 +- .../src/plugins/oidc-provider/index.ts | 24 +++++++++++++++++-- .../src/plugins/oidc-provider/oidc.test.ts | 6 ++--- .../src/plugins/oidc-provider/types.ts | 2 ++ 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/docs/content/docs/plugins/oidc-provider.mdx b/docs/content/docs/plugins/oidc-provider.mdx index 083970a0..7c48f1e5 100644 --- a/docs/content/docs/plugins/oidc-provider.mdx +++ b/docs/content/docs/plugins/oidc-provider.mdx @@ -246,7 +246,7 @@ The UserInfo endpoint returns different claims based on the scopes that were gra - With `profile` scope: Returns name, picture, given_name, family_name - With `email` scope: Returns email and email_verified -The `getAdditionalUserInfoClaim` function receives the user object and the requested scopes array, allowing you to conditionally include claims based on the scopes granted during authorization. These additional claims will be included in both the UserInfo endpoint response and the ID token. +The `getAdditionalUserInfoClaim` function receives the user object, requested scopes array, and the client, allowing you to conditionally include claims based on the scopes granted during authorization. These additional claims will be included in both the UserInfo endpoint response and the ID token. ### Consent Screen @@ -561,6 +561,6 @@ Table Name: `oauthConsent` **trustedClients**: `(Client & { skipConsent?: boolean })[]` - Array of trusted clients that are configured directly in the provider options. These clients bypass database lookups and can optionally skip consent screens. -**getAdditionalUserInfoClaim**: `(user: User, scopes: string[]) => Record` - Function to get additional user info claims. +**getAdditionalUserInfoClaim**: `(user: User, scopes: string[], client: Client) => Record` - Function to get additional user info claims. **useJWTPlugin**: `boolean` - When `true`, ID tokens are signed using the JWT plugin's asymmetric keys. When `false` (default), ID tokens are signed with HMAC-SHA256 using the application secret. \ No newline at end of file diff --git a/packages/better-auth/src/plugins/mcp/index.ts b/packages/better-auth/src/plugins/mcp/index.ts index a903c94e..285bbd2c 100644 --- a/packages/better-auth/src/plugins/mcp/index.ts +++ b/packages/better-auth/src/plugins/mcp/index.ts @@ -570,7 +570,11 @@ export const mcp = (options: MCPOptions) => { }; const additionalUserClaims = opts.getAdditionalUserInfoClaim - ? opts.getAdditionalUserInfoClaim(user, requestedScopes) + ? await opts.getAdditionalUserInfoClaim( + user, + requestedScopes, + client, + ) : {}; const idToken = await new SignJWT({ diff --git a/packages/better-auth/src/plugins/mcp/mcp.test.ts b/packages/better-auth/src/plugins/mcp/mcp.test.ts index 268dd978..9ed977d2 100644 --- a/packages/better-auth/src/plugins/mcp/mcp.test.ts +++ b/packages/better-auth/src/plugins/mcp/mcp.test.ts @@ -31,7 +31,7 @@ describe("mcp", async () => { loginPage: "/login", requirePKCE: true, - getAdditionalUserInfoClaim(user, scopes) { + getAdditionalUserInfoClaim(user, scopes, client) { return { custom: "custom value", userId: user.id, diff --git a/packages/better-auth/src/plugins/oidc-provider/index.ts b/packages/better-auth/src/plugins/oidc-provider/index.ts index 0997b46a..242146eb 100644 --- a/packages/better-auth/src/plugins/oidc-provider/index.ts +++ b/packages/better-auth/src/plugins/oidc-provider/index.ts @@ -754,7 +754,11 @@ export const oidcProvider = (options: OIDCOptions) => { }; const additionalUserClaims = options.getAdditionalUserInfoClaim - ? await options.getAdditionalUserInfoClaim(user, requestedScopes) + ? await options.getAdditionalUserInfoClaim( + user, + requestedScopes, + client, + ) : {}; const payload = { @@ -958,6 +962,18 @@ export const oidcProvider = (options: OIDCOptions) => { }); } + const client = await getClient( + accessToken.clientId, + ctx.context.adapter, + trustedClients, + ); + if (!client) { + throw new APIError("UNAUTHORIZED", { + error_description: "client not found", + error: "invalid_token", + }); + } + const user = await ctx.context.internalAdapter.findUserById( accessToken.userId, ); @@ -986,7 +1002,11 @@ export const oidcProvider = (options: OIDCOptions) => { : undefined, }; const userClaims = options.getAdditionalUserInfoClaim - ? await options.getAdditionalUserInfoClaim(user, requestedScopes) + ? await options.getAdditionalUserInfoClaim( + user, + requestedScopes, + client, + ) : baseUserClaims; return ctx.json({ ...baseUserClaims, diff --git a/packages/better-auth/src/plugins/oidc-provider/oidc.test.ts b/packages/better-auth/src/plugins/oidc-provider/oidc.test.ts index f7397b5d..b9c0a3e4 100644 --- a/packages/better-auth/src/plugins/oidc-provider/oidc.test.ts +++ b/packages/better-auth/src/plugins/oidc-provider/oidc.test.ts @@ -32,7 +32,7 @@ describe("oidc", async () => { loginPage: "/login", consentPage: "/oauth2/authorize", requirePKCE: true, - getAdditionalUserInfoClaim(user, scopes) { + getAdditionalUserInfoClaim(user, scopes, client) { return { custom: "custom value", userId: user.id, @@ -376,7 +376,7 @@ describe("oidc storage", async () => { loginPage: "/login", consentPage: "/oauth2/authorize", requirePKCE: true, - getAdditionalUserInfoClaim(user, scopes) { + getAdditionalUserInfoClaim(user, scopes, client) { return { custom: "custom value", userId: user.id, @@ -535,7 +535,7 @@ describe("oidc-jwt", async () => { loginPage: "/login", consentPage: "/oauth2/authorize", requirePKCE: true, - getAdditionalUserInfoClaim(user, scopes) { + getAdditionalUserInfoClaim(user, scopes, client) { return { custom: "custom value", userId: user.id, diff --git a/packages/better-auth/src/plugins/oidc-provider/types.ts b/packages/better-auth/src/plugins/oidc-provider/types.ts index 28a87738..25786162 100644 --- a/packages/better-auth/src/plugins/oidc-provider/types.ts +++ b/packages/better-auth/src/plugins/oidc-provider/types.ts @@ -115,11 +115,13 @@ export interface OIDCOptions { * * @param user - The user object. * @param scopes - The scopes that the client requested. + * @param client - The client object. * @returns The user info claim. */ getAdditionalUserInfoClaim?: ( user: User & Record, scopes: string[], + client: Client, ) => Record | Promise>; /** * Trusted clients that are configured directly in the provider options.