@better-auth/oauth-provider 1.6.2 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { a as ResourceServerMetadata } from "./oauth-Cc0nzj5Q.mjs";
1
+ import { a as ResourceServerMetadata } from "./oauth-IpUqx-_N.mjs";
2
2
  import { JWTPayload, JWTVerifyOptions } from "jose";
3
3
  import { Auth } from "better-auth/types";
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { a as getJwtPlugin, o as getOAuthProviderPlugin, y as handleMcpErrors } from "./utils-B9Pj9EPf.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-Usf6Oz_M.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-BlcZ64XB.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { APIError } from "better-call";
5
5
  import { logger } from "@better-auth/core/env";
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as oauthProvider } from "./oauth-CetEXi_Z.mjs";
1
+ import { n as oauthProvider } from "./oauth-DH61OMT6.mjs";
2
2
  import * as _better_fetch_fetch0 from "@better-fetch/fetch";
3
3
 
4
4
  //#region src/client.d.ts
package/dist/client.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as PACKAGE_VERSION } from "./version-Usf6Oz_M.mjs";
1
+ import { t as PACKAGE_VERSION } from "./version-BlcZ64XB.mjs";
2
2
  import { safeJSONParse } from "@better-auth/core/utils/json";
3
3
  //#region src/client.ts
4
4
  function parseSignedQuery(search) {
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { _ as Awaitable, a as ResourceServerMetadata, c as OAuthConsent, d as OAuthRefreshToken, f as Prompt, g as VerificationValue, h as StoreTokenType, i as OIDCMetadata, l as OAuthOpaqueAccessToken, m as Scope, n as GrantType, o as AuthorizePrompt, p as SchemaClient, r as OAuthClient, s as OAuthAuthorizationQuery, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-Cc0nzj5Q.mjs";
2
- import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-CetEXi_Z.mjs";
1
+ import { _ as Awaitable, a as ResourceServerMetadata, c as OAuthConsent, d as OAuthRefreshToken, f as Prompt, g as VerificationValue, h as StoreTokenType, i as OIDCMetadata, l as OAuthOpaqueAccessToken, m as Scope, n as GrantType, o as AuthorizePrompt, p as SchemaClient, r as OAuthClient, s as OAuthAuthorizationQuery, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-IpUqx-_N.mjs";
2
+ import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-DH61OMT6.mjs";
3
3
  import { verifyAccessToken } from "better-auth/oauth2";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
5
  import { JWTPayload } from "jose";
@@ -44,7 +44,7 @@ declare const oauthProviderAuthServerMetadata: <Auth extends {
44
44
  };
45
45
  }>(auth: Auth, opts?: {
46
46
  headers?: HeadersInit;
47
- }) => (_request: Request) => Promise<Response>;
47
+ }) => (request: Request) => Promise<Response>;
48
48
  /**
49
49
  * Provides an exportable `/.well-known/openid-configuration`.
50
50
  *
@@ -59,6 +59,6 @@ declare const oauthProviderOpenIdConfigMetadata: <Auth extends {
59
59
  };
60
60
  }>(auth: Auth, opts?: {
61
61
  headers?: HeadersInit;
62
- }) => (_request: Request) => Promise<Response>;
62
+ }) => (request: Request) => Promise<Response>;
63
63
  //#endregion
64
64
  export { AuthServerMetadata, AuthorizePrompt, OAuthAuthorizationQuery, OAuthClient, OAuthConsent, OAuthOpaqueAccessToken, OAuthOptions, OAuthRefreshToken, OIDCMetadata, Prompt, ResourceServerMetadata, SchemaClient, Scope, StoreTokenType, VerificationValue, authServerMetadata, getOAuthProviderState, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oidcServerMetadata };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { _ as validateClientCredentials, a as getJwtPlugin, b as mcpHandler, c as isPKCERequired, d as parsePrompt, f as resolveSessionAuthTime, g as storeToken, h as storeClientSecret, i as getClient, l as normalizeTimestampValue, m as searchParamsToQuery, n as decryptStoredClientSecret, p as resolveSubjectIdentifier, r as deleteFromPrompt, s as getStoredToken, t as basicToClientCredentials, u as parseClientMetadata, v as verifyOAuthQueryParams } from "./utils-B9Pj9EPf.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-Usf6Oz_M.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-BlcZ64XB.mjs";
3
3
  import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
4
4
  import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
5
5
  import { APIError as APIError$1 } from "better-call";
@@ -153,6 +153,81 @@ async function postLogin(ctx, opts) {
153
153
  };
154
154
  }
155
155
  //#endregion
156
+ //#region src/types/zod.ts
157
+ const DANGEROUS_SCHEMES = [
158
+ "javascript:",
159
+ "data:",
160
+ "vbscript:"
161
+ ];
162
+ function isLocalhost(hostname) {
163
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.endsWith(".localhost");
164
+ }
165
+ /**
166
+ * Runtime schema for OAuthAuthorizationQuery.
167
+ * Uses passthrough to tolerate fields added by future extensions (PAR, FPA, etc.)
168
+ */
169
+ const oauthAuthorizationQuerySchema = z.object({
170
+ response_type: z.literal("code").optional(),
171
+ request_uri: z.string().optional(),
172
+ redirect_uri: z.string(),
173
+ scope: z.string().optional(),
174
+ state: z.string(),
175
+ client_id: z.string(),
176
+ prompt: z.string().optional(),
177
+ display: z.string().optional(),
178
+ ui_locales: z.string().optional(),
179
+ max_age: z.coerce.number().optional(),
180
+ acr_values: z.string().optional(),
181
+ login_hint: z.string().optional(),
182
+ id_token_hint: z.string().optional(),
183
+ code_challenge: z.string().optional(),
184
+ code_challenge_method: z.literal("S256").optional(),
185
+ nonce: z.string().optional()
186
+ }).passthrough();
187
+ /**
188
+ * Runtime schema for the authorization code verification value.
189
+ * Validates structure on deserialization from the JSON blob stored in the DB.
190
+ * Uses passthrough so future fields (e.g. from authorization challenge) don't break parsing.
191
+ */
192
+ const verificationValueSchema = z.object({
193
+ type: z.literal("authorization_code"),
194
+ query: oauthAuthorizationQuerySchema,
195
+ sessionId: z.string(),
196
+ userId: z.string(),
197
+ referenceId: z.string().optional(),
198
+ authTime: z.number().optional()
199
+ }).passthrough();
200
+ /**
201
+ * Reusable URL validation for OAuth redirect URIs.
202
+ * - Blocks dangerous schemes (javascript:, data:, vbscript:)
203
+ * - For http/https: requires HTTPS (HTTP allowed only for localhost)
204
+ * - Allows custom schemes for mobile apps (e.g., myapp://callback)
205
+ */
206
+ const SafeUrlSchema = z.url().superRefine((val, ctx) => {
207
+ if (!URL.canParse(val)) {
208
+ ctx.addIssue({
209
+ code: "custom",
210
+ message: "URL must be parseable",
211
+ fatal: true
212
+ });
213
+ return z.NEVER;
214
+ }
215
+ const u = new URL(val);
216
+ if (DANGEROUS_SCHEMES.includes(u.protocol)) {
217
+ ctx.addIssue({
218
+ code: "custom",
219
+ message: "URL cannot use javascript:, data:, or vbscript: scheme"
220
+ });
221
+ return;
222
+ }
223
+ if (u.protocol === "http:" || u.protocol === "https:") {
224
+ if (u.protocol === "http:" && !isLocalhost(u.hostname)) ctx.addIssue({
225
+ code: "custom",
226
+ message: "Redirect URI must use HTTPS (HTTP allowed only for localhost)"
227
+ });
228
+ }
229
+ });
230
+ //#endregion
156
231
  //#region src/userinfo.ts
157
232
  /**
158
233
  * Provides shared /userinfo and id_token claims functionality
@@ -265,7 +340,7 @@ async function createJwtAccessToken(ctx, opts, user, client, audience, scopes, r
265
340
  options: jwtPluginOptions,
266
341
  payload: {
267
342
  ...customClaims,
268
- sub: user.id,
343
+ sub: user?.id,
269
344
  aud: typeof audience === "string" ? audience : audience?.length === 1 ? audience.at(0) : audience,
270
345
  azp: client.clientId,
271
346
  scope: scopes.join(" "),
@@ -399,21 +474,29 @@ async function checkResource(ctx, opts, scopes) {
399
474
  }
400
475
  return audience?.length === 1 ? audience.at(0) : audience;
401
476
  }
402
- async function createUserTokens(ctx, opts, client, scopes, user, referenceId, sessionId, nonce, additional, authTime) {
477
+ async function createUserTokens(ctx, opts, params) {
478
+ const { client, scopes, user, grantType, referenceId, sessionId, nonce, refreshToken: existingRefreshToken, authTime, verificationValue } = params;
403
479
  const iat = Math.floor(Date.now() / 1e3);
404
- const defaultExp = iat + (opts.accessTokenExpiresIn ?? 3600);
480
+ const defaultExp = iat + (user ? opts.accessTokenExpiresIn ?? 3600 : opts.m2mAccessTokenExpiresIn ?? 3600);
405
481
  const exp = opts.scopeExpirations ? scopes.map((sc) => opts.scopeExpirations?.[sc] ? toExpJWT(opts.scopeExpirations[sc], iat) : defaultExp).reduce((prev, curr) => {
406
482
  return prev < curr ? prev : curr;
407
483
  }, defaultExp) : defaultExp;
408
484
  const audience = await checkResource(ctx, opts, scopes);
409
- const isRefreshToken = additional?.refreshToken?.scopes?.includes("offline_access") || scopes.includes("offline_access");
485
+ const isRefreshToken = user && (existingRefreshToken?.scopes?.includes("offline_access") || scopes.includes("offline_access"));
410
486
  const isJwtAccessToken = audience && !opts.disableJwtPlugin;
411
- const isIdToken = scopes.includes("openid");
412
- const earlyRefreshToken = isRefreshToken && !isJwtAccessToken ? await createRefreshToken(ctx, opts, user, referenceId, client, scopes, {
487
+ const isIdToken = user && scopes.includes("openid");
488
+ const customFields = opts.customTokenResponseFields ? await opts.customTokenResponseFields({
489
+ grantType,
490
+ user,
491
+ scopes,
492
+ metadata: parseClientMetadata(client.metadata),
493
+ verificationValue
494
+ }) : void 0;
495
+ const earlyRefreshToken = isRefreshToken && user && !isJwtAccessToken ? await createRefreshToken(ctx, opts, user, referenceId, client, scopes, {
413
496
  iat,
414
497
  exp: iat + (opts.refreshTokenExpiresIn ?? 2592e3),
415
498
  sid: sessionId
416
- }, additional?.refreshToken, authTime) : void 0;
499
+ }, existingRefreshToken, authTime) : void 0;
417
500
  const [accessToken, refreshToken, idToken] = await Promise.all([
418
501
  isJwtAccessToken ? createJwtAccessToken(ctx, opts, user, client, audience, scopes, referenceId, {
419
502
  iat,
@@ -424,14 +507,15 @@ async function createUserTokens(ctx, opts, client, scopes, user, referenceId, se
424
507
  exp,
425
508
  sid: sessionId
426
509
  }, referenceId, earlyRefreshToken?.id),
427
- earlyRefreshToken ? earlyRefreshToken : isRefreshToken ? createRefreshToken(ctx, opts, user, referenceId, client, scopes, {
510
+ earlyRefreshToken ? earlyRefreshToken : isRefreshToken && user ? createRefreshToken(ctx, opts, user, referenceId, client, scopes, {
428
511
  iat,
429
512
  exp: iat + (opts.refreshTokenExpiresIn ?? 2592e3),
430
513
  sid: sessionId
431
- }, additional?.refreshToken, authTime) : void 0,
432
- isIdToken ? createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime) : void 0
514
+ }, existingRefreshToken, authTime) : void 0,
515
+ isIdToken && user ? createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime) : void 0
433
516
  ]);
434
517
  return ctx.json({
518
+ ...customFields,
435
519
  access_token: accessToken,
436
520
  expires_in: exp - iat,
437
521
  expires_at: exp,
@@ -447,7 +531,6 @@ async function createUserTokens(ctx, opts, client, scopes, user, referenceId, se
447
531
  /** Checks verification value */
448
532
  async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri) {
449
533
  const verification = await ctx.context.internalAdapter.findVerificationValue(await storeToken(opts.storeTokens, code, "authorization_code"));
450
- const verificationValue = verification ? JSON.parse(verification?.value) : void 0;
451
534
  if (!verification) throw new APIError("UNAUTHORIZED", {
452
535
  error_description: "Invalid code",
453
536
  error: "invalid_verification"
@@ -457,22 +540,25 @@ async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri)
457
540
  error_description: "code expired",
458
541
  error: "invalid_verification"
459
542
  });
460
- if (!verificationValue) throw new APIError("UNAUTHORIZED", {
461
- error_description: "missing verification value content",
462
- error: "invalid_verification"
463
- });
464
- if (verificationValue.type !== "authorization_code") throw new APIError("UNAUTHORIZED", {
465
- error_description: "incorrect verification type",
543
+ let rawValue;
544
+ try {
545
+ rawValue = JSON.parse(verification.value);
546
+ } catch {
547
+ throw new APIError("UNAUTHORIZED", {
548
+ error_description: "malformed verification value",
549
+ error: "invalid_verification"
550
+ });
551
+ }
552
+ const parsed = verificationValueSchema.safeParse(rawValue);
553
+ if (!parsed.success) throw new APIError("UNAUTHORIZED", {
554
+ error_description: "malformed verification value",
466
555
  error: "invalid_verification"
467
556
  });
557
+ const verificationValue = parsed.data;
468
558
  if (verificationValue.query.client_id !== client_id) throw new APIError("UNAUTHORIZED", {
469
559
  error_description: "invalid client_id",
470
560
  error: "invalid_client"
471
561
  });
472
- if (!verificationValue.userId) throw new APIError("BAD_REQUEST", {
473
- error_description: "missing user_id on challenge",
474
- error: "invalid_user"
475
- });
476
562
  if (verificationValue.query?.redirect_uri && verificationValue.query?.redirect_uri !== redirect_uri) throw new APIError("BAD_REQUEST", {
477
563
  error_description: "redirect_uri mismatch",
478
564
  error: "invalid_request"
@@ -565,7 +651,17 @@ async function handleAuthorizationCodeGrant(ctx, opts) {
565
651
  error: "invalid_request"
566
652
  });
567
653
  const authTime = verificationValue.authTime != null ? normalizeTimestampValue(verificationValue.authTime) : resolveSessionAuthTime(session);
568
- return createUserTokens(ctx, opts, client, verificationValue.query.scope?.split(" ") ?? [], user, verificationValue.referenceId, session.id, verificationValue.query?.nonce, void 0, authTime);
654
+ return createUserTokens(ctx, opts, {
655
+ client,
656
+ scopes: verificationValue.query.scope?.split(" ") ?? [],
657
+ user,
658
+ grantType: "authorization_code",
659
+ referenceId: verificationValue.referenceId,
660
+ sessionId: session.id,
661
+ nonce: verificationValue.query?.nonce,
662
+ authTime,
663
+ verificationValue
664
+ });
569
665
  }
570
666
  /**
571
667
  * Grant that allows direct access to an API using the application's credentials
@@ -608,43 +704,11 @@ async function handleClientCredentialsGrant(ctx, opts) {
608
704
  });
609
705
  }
610
706
  if (!requestedScopes) requestedScopes = client.scopes ?? opts.clientCredentialGrantDefaultScopes ?? opts.scopes ?? [];
611
- const jwtPluginOptions = opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context).options;
612
- const audience = await checkResource(ctx, opts, requestedScopes);
613
- const iat = Math.floor(Date.now() / 1e3);
614
- const defaultExp = iat + (opts.m2mAccessTokenExpiresIn ?? 3600);
615
- const exp = opts.scopeExpirations && requestedScopes ? requestedScopes.map((sc) => opts.scopeExpirations?.[sc] ? toExpJWT(opts.scopeExpirations[sc], iat) : defaultExp).reduce((prev, curr) => {
616
- return prev < curr ? prev : curr;
617
- }, defaultExp) : defaultExp;
618
- const customClaims = opts.customAccessTokenClaims ? await opts.customAccessTokenClaims({
707
+ return createUserTokens(ctx, opts, {
708
+ client,
619
709
  scopes: requestedScopes,
620
- resource: ctx.body.resource,
621
- metadata: parseClientMetadata(client.metadata)
622
- }) : {};
623
- const accessToken = audience && !opts.disableJwtPlugin ? await signJWT(ctx, {
624
- options: jwtPluginOptions,
625
- payload: {
626
- ...customClaims,
627
- aud: audience,
628
- azp: client.clientId,
629
- scope: requestedScopes.join(" "),
630
- iss: jwtPluginOptions?.jwt?.issuer ?? ctx.context.baseURL,
631
- iat,
632
- exp
633
- }
634
- }) : await createOpaqueAccessToken(ctx, opts, void 0, client, requestedScopes, {
635
- iat,
636
- exp
710
+ grantType: "client_credentials"
637
711
  });
638
- return ctx.json({
639
- access_token: accessToken,
640
- expires_in: exp - iat,
641
- expires_at: exp,
642
- token_type: "Bearer",
643
- scope: requestedScopes.join(" ")
644
- }, { headers: {
645
- "Cache-Control": "no-store",
646
- Pragma: "no-cache"
647
- } });
648
712
  }
649
713
  /**
650
714
  * Obtains new Session Jwt and Refresh Tokens using a refresh token
@@ -720,7 +784,16 @@ async function handleRefreshTokenGrant(ctx, opts) {
720
784
  error: "invalid_request"
721
785
  });
722
786
  const authTime = refreshToken.authTime != null ? normalizeTimestampValue(refreshToken.authTime) : void 0;
723
- return createUserTokens(ctx, opts, client, requestedScopes ?? scopes, user, refreshToken.referenceId, refreshToken.sessionId, void 0, { refreshToken }, authTime);
787
+ return createUserTokens(ctx, opts, {
788
+ client,
789
+ scopes: requestedScopes ?? scopes,
790
+ user,
791
+ grantType: "refresh_token",
792
+ referenceId: refreshToken.referenceId,
793
+ sessionId: refreshToken.sessionId,
794
+ refreshToken,
795
+ authTime
796
+ });
724
797
  }
725
798
  //#endregion
726
799
  //#region src/introspect.ts
@@ -1102,6 +1175,21 @@ const publicSessionMiddleware = (opts) => createAuthMiddleware(async (ctx) => {
1102
1175
  });
1103
1176
  //#endregion
1104
1177
  //#region src/register.ts
1178
+ /**
1179
+ * Resolves the auth method and type for unauthenticated DCR.
1180
+ * Overrides confidential methods to "none" per RFC 7591 Section 3.2.1.
1181
+ * When overriding, clears type "web" since it is only valid for confidential clients.
1182
+ */
1183
+ function resolveUnauthenticatedAuth(body) {
1184
+ if (body.token_endpoint_auth_method === "none") return {
1185
+ tokenEndpointAuthMethod: "none",
1186
+ type: body.type
1187
+ };
1188
+ return {
1189
+ tokenEndpointAuthMethod: "none",
1190
+ type: body.type === "web" ? void 0 : body.type
1191
+ };
1192
+ }
1105
1193
  async function registerEndpoint(ctx, opts) {
1106
1194
  if (!opts.allowDynamicClientRegistration) throw new APIError("FORBIDDEN", {
1107
1195
  error: "access_denied",
@@ -1113,12 +1201,16 @@ async function registerEndpoint(ctx, opts) {
1113
1201
  error: "invalid_token",
1114
1202
  error_description: "Authentication required for client registration"
1115
1203
  });
1116
- const isPublic = body.token_endpoint_auth_method === "none";
1117
- if (!session && !isPublic) throw new APIError("UNAUTHORIZED", {
1118
- error: "invalid_request",
1119
- error_description: "Authentication required for confidential client registration"
1120
- });
1121
- if (!ctx.body.scope) ctx.body.scope = (opts.clientRegistrationDefaultScopes ?? opts.scopes)?.join(" ");
1204
+ if (!session) {
1205
+ if (body.grant_types?.includes("client_credentials")) throw new APIError("BAD_REQUEST", {
1206
+ error: "invalid_client_metadata",
1207
+ error_description: "client_credentials grant requires authenticated registration"
1208
+ });
1209
+ const resolved = resolveUnauthenticatedAuth(body);
1210
+ body.token_endpoint_auth_method = resolved.tokenEndpointAuthMethod;
1211
+ body.type = resolved.type;
1212
+ }
1213
+ if (!body.scope) body.scope = (opts.clientRegistrationDefaultScopes ?? opts.scopes)?.join(" ");
1122
1214
  return createOAuthClientEndpoint(ctx, opts, { isRegister: true });
1123
1215
  }
1124
1216
  async function checkOAuthClient(client, opts, settings) {
@@ -1309,46 +1401,6 @@ function schemaToOAuth(input) {
1309
1401
  };
1310
1402
  }
1311
1403
  //#endregion
1312
- //#region src/types/zod.ts
1313
- const DANGEROUS_SCHEMES = [
1314
- "javascript:",
1315
- "data:",
1316
- "vbscript:"
1317
- ];
1318
- function isLocalhost(hostname) {
1319
- return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname.endsWith(".localhost");
1320
- }
1321
- /**
1322
- * Reusable URL validation for OAuth redirect URIs.
1323
- * - Blocks dangerous schemes (javascript:, data:, vbscript:)
1324
- * - For http/https: requires HTTPS (HTTP allowed only for localhost)
1325
- * - Allows custom schemes for mobile apps (e.g., myapp://callback)
1326
- */
1327
- const SafeUrlSchema = z.url().superRefine((val, ctx) => {
1328
- if (!URL.canParse(val)) {
1329
- ctx.addIssue({
1330
- code: "custom",
1331
- message: "URL must be parseable",
1332
- fatal: true
1333
- });
1334
- return z.NEVER;
1335
- }
1336
- const u = new URL(val);
1337
- if (DANGEROUS_SCHEMES.includes(u.protocol)) {
1338
- ctx.addIssue({
1339
- code: "custom",
1340
- message: "URL cannot use javascript:, data:, or vbscript: scheme"
1341
- });
1342
- return;
1343
- }
1344
- if (u.protocol === "http:" || u.protocol === "https:") {
1345
- if (u.protocol === "http:" && !isLocalhost(u.hostname)) ctx.addIssue({
1346
- code: "custom",
1347
- message: "Redirect URI must use HTTPS (HTTP allowed only for localhost)"
1348
- });
1349
- }
1350
- });
1351
- //#endregion
1352
1404
  //#region src/oauthClient/endpoints.ts
1353
1405
  async function getClientEndpoint(ctx, opts) {
1354
1406
  const session = await getSessionFromCtx(ctx);
@@ -3955,6 +4007,16 @@ function oidcServerMetadata(ctx, opts) {
3955
4007
  ]
3956
4008
  };
3957
4009
  }
4010
+ const METADATA_CACHE_CONTROL = "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400";
4011
+ function metadataResponse(body, extraHeaders) {
4012
+ const headers = new Headers(extraHeaders);
4013
+ if (!headers.has("Cache-Control")) headers.set("Cache-Control", METADATA_CACHE_CONTROL);
4014
+ headers.set("Content-Type", "application/json");
4015
+ return new Response(JSON.stringify(body), {
4016
+ status: 200,
4017
+ headers
4018
+ });
4019
+ }
3958
4020
  /**
3959
4021
  * Provides an exportable `/.well-known/oauth-authorization-server`.
3960
4022
  *
@@ -3964,16 +4026,11 @@ function oidcServerMetadata(ctx, opts) {
3964
4026
  * @external
3965
4027
  */
3966
4028
  const oauthProviderAuthServerMetadata = (auth, opts) => {
3967
- return async (_request) => {
3968
- const res = await auth.api.getOAuthServerConfig();
3969
- return new Response(JSON.stringify(res), {
3970
- status: 200,
3971
- headers: {
3972
- "Cache-Control": "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400",
3973
- ...opts?.headers,
3974
- "Content-Type": "application/json"
3975
- }
3976
- });
4029
+ return async (request) => {
4030
+ return metadataResponse(await auth.api.getOAuthServerConfig({
4031
+ request,
4032
+ asResponse: false
4033
+ }), opts?.headers);
3977
4034
  };
3978
4035
  };
3979
4036
  /**
@@ -3985,16 +4042,11 @@ const oauthProviderAuthServerMetadata = (auth, opts) => {
3985
4042
  * @external
3986
4043
  */
3987
4044
  const oauthProviderOpenIdConfigMetadata = (auth, opts) => {
3988
- return async (_request) => {
3989
- const res = await auth.api.getOpenIdConfig();
3990
- return new Response(JSON.stringify(res), {
3991
- status: 200,
3992
- headers: {
3993
- "Cache-Control": "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400",
3994
- ...opts?.headers,
3995
- "Content-Type": "application/json"
3996
- }
3997
- });
4045
+ return async (request) => {
4046
+ return metadataResponse(await auth.api.getOpenIdConfig({
4047
+ request,
4048
+ asResponse: false
4049
+ }), opts?.headers);
3998
4050
  };
3999
4051
  };
4000
4052
  //#endregion
@@ -1,4 +1,4 @@
1
- import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-Cc0nzj5Q.mjs";
1
+ import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-IpUqx-_N.mjs";
2
2
  import * as better_call0 from "better-call";
3
3
  import * as z from "zod";
4
4
  import * as better_auth_plugins0 from "better-auth/plugins";
@@ -428,8 +428,10 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
428
428
  access_token: string;
429
429
  expires_in: number;
430
430
  expires_at: number;
431
- token_type: string;
431
+ token_type: "Bearer";
432
+ refresh_token: string | undefined;
432
433
  scope: string;
434
+ id_token: string | undefined;
433
435
  }>;
434
436
  oauth2Introspect: better_call0.StrictEndpoint<"/oauth2/introspect", {
435
437
  method: "POST";
@@ -752,6 +752,34 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
752
752
  resource?: string; /** oAuthClient metadata */
753
753
  metadata?: Record<string, any>;
754
754
  }) => Awaitable<Record<string, any>>;
755
+ /**
756
+ * Custom fields to include in the token response body.
757
+ *
758
+ * Unlike `customAccessTokenClaims` (which adds claims inside the JWT payload),
759
+ * this adds fields to the JSON response envelope alongside `access_token`,
760
+ * `token_type`, etc. Standard OAuth fields (`access_token`, `token_type`,
761
+ * `expires_in`, `expires_at`, `refresh_token`, `scope`, `id_token`) cannot
762
+ * be overridden.
763
+ *
764
+ * @param info - context that may be useful when creating custom fields
765
+ */
766
+ customTokenResponseFields?: (info: {
767
+ /** The grant type being processed */grantType: GrantType;
768
+ /**
769
+ * The user, if applicable.
770
+ * Undefined for `client_credentials` (M2M, no user).
771
+ * Always present for `authorization_code` and `refresh_token`.
772
+ */
773
+ user?: (User & Record<string, unknown>) | null; /** Scopes granted for this token */
774
+ scopes: Scopes; /** oAuthClient metadata */
775
+ metadata?: Record<string, any>;
776
+ /**
777
+ * The authorization code verification value.
778
+ * Only present for `authorization_code` grant. Contains the original
779
+ * authorization request parameters (`query`), `referenceId`, `sessionId`, etc.
780
+ */
781
+ verificationValue?: VerificationValue;
782
+ }) => Awaitable<Record<string, unknown>>;
755
783
  /**
756
784
  * Overwrite specific /.well-known/openid-configuration
757
785
  * values so they are not available publically.
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.6.2";
3
+ const PACKAGE_VERSION = "1.6.3";
4
4
  //#endregion
5
5
  export { PACKAGE_VERSION as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/oauth-provider",
3
- "version": "1.6.2",
3
+ "version": "1.6.3",
4
4
  "description": "An oauth provider plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -64,15 +64,15 @@
64
64
  "@modelcontextprotocol/sdk": "^1.27.1",
65
65
  "listhen": "^1.9.0",
66
66
  "tsdown": "0.21.1",
67
- "@better-auth/core": "1.6.2",
68
- "better-auth": "1.6.2"
67
+ "@better-auth/core": "1.6.3",
68
+ "better-auth": "1.6.3"
69
69
  },
70
70
  "peerDependencies": {
71
71
  "@better-auth/utils": "0.4.0",
72
72
  "@better-fetch/fetch": "1.1.21",
73
73
  "better-call": "1.3.5",
74
- "@better-auth/core": "^1.6.2",
75
- "better-auth": "^1.6.2"
74
+ "@better-auth/core": "^1.6.3",
75
+ "better-auth": "^1.6.3"
76
76
  },
77
77
  "scripts": {
78
78
  "build": "tsdown",