@better-auth/oauth-provider 1.7.0-beta.7 → 1.7.0-beta.9

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 { C as OAuthConsent, F as OAuthTokenResponse, L as Prompt, M as OAuthResource, O as OAuthOptions, a as GrantType, l as TokenEndpointAuthMethod, n as AuthServerMetadata, o as OAuthClient, z as Scope } from "./oauth-CPWY2Few.mjs";
1
+ import { C as OAuthConsent, F as OAuthTokenResponse, L as Prompt, M as OAuthResource, O as OAuthOptions, a as GrantType, l as TokenEndpointAuthMethod, n as AuthServerMetadata, o as OAuthClient, z as Scope } from "./oauth-Di-k6QeJ.mjs";
2
2
  import * as better_call0 from "better-call";
3
3
  import * as z from "zod";
4
4
  import * as jose from "jose";
@@ -140,10 +140,13 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
140
140
  }, {
141
141
  jwks_uri?: string | undefined;
142
142
  userinfo_endpoint: string;
143
- acr_values_supported: string[];
143
+ acr_values_supported?: string[] | undefined;
144
144
  subject_types_supported: ("public" | "pairwise")[];
145
145
  claims_supported: string[];
146
+ claims_parameter_supported?: boolean | undefined;
146
147
  end_session_endpoint: string;
148
+ request_parameter_supported?: boolean | undefined;
149
+ request_uri_parameter_supported?: boolean | undefined;
147
150
  prompt_values_supported: Prompt[];
148
151
  issuer: string;
149
152
  authorization_endpoint: string;
@@ -174,41 +177,11 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
174
177
  id_token_signing_alg_values_supported: better_auth_plugins0.JWSAlgorithms[] | ["HS256"];
175
178
  }>;
176
179
  oauth2Authorize: better_call0.StrictEndpoint<"/oauth2/authorize", {
177
- method: "GET";
178
- query: z.ZodObject<{
179
- response_type: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
180
- code: "code";
181
- }>>>;
182
- request_uri: z.ZodOptional<z.ZodString>;
183
- redirect_uri: z.ZodOptional<z.ZodURL>;
184
- scope: z.ZodOptional<z.ZodString>;
185
- state: z.ZodOptional<z.ZodString>;
186
- client_id: z.ZodString;
187
- prompt: z.ZodOptional<z.ZodString>;
188
- display: z.ZodOptional<z.ZodString>;
189
- ui_locales: z.ZodOptional<z.ZodString>;
190
- max_age: z.ZodOptional<z.ZodPipe<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
191
- acr_values: z.ZodOptional<z.ZodString>;
192
- login_hint: z.ZodOptional<z.ZodString>;
193
- id_token_hint: z.ZodOptional<z.ZodString>;
194
- code_challenge: z.ZodOptional<z.ZodString>;
195
- code_challenge_method: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
196
- S256: "S256";
197
- }>>>;
198
- nonce: z.ZodOptional<z.ZodString>;
199
- dpop_jkt: z.ZodOptional<z.ZodString>;
200
- resource: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
201
- }, z.core.$loose>;
180
+ method: ("GET" | "POST")[];
181
+ body: z.ZodObject<{}, z.core.$loose>;
202
182
  redirectOnError: OAuthRedirectOnError<GenericEndpointContext, OAuthRedirectResult>;
203
- errorCodesByField: {
204
- response_type: {
205
- invalid: "unsupported_response_type";
206
- };
207
- resource: {
208
- invalid: "invalid_target";
209
- };
210
- };
211
183
  metadata: {
184
+ allowedMediaTypes: string[];
212
185
  openapi: {
213
186
  description: string;
214
187
  parameters: ({
@@ -313,6 +286,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
313
286
  body: z.ZodObject<{
314
287
  accept: z.ZodBoolean;
315
288
  scope: z.ZodOptional<z.ZodString>;
289
+ claims: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
316
290
  oauth_query: z.ZodOptional<z.ZodString>;
317
291
  }, z.core.$strip>;
318
292
  use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<{
@@ -803,8 +777,12 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
803
777
  }, null | undefined>;
804
778
  oauth2UserInfo: better_call0.StrictEndpoint<"/oauth2/userinfo", {
805
779
  method: ("GET" | "POST")[];
780
+ body: z.ZodOptional<z.ZodObject<{
781
+ access_token: z.ZodOptional<z.ZodString>;
782
+ }, z.core.$loose>>;
806
783
  metadata: {
807
784
  noStore: boolean;
785
+ allowedMediaTypes: string[];
808
786
  openapi: {
809
787
  description: string;
810
788
  security: ({
@@ -915,13 +893,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
915
893
  };
916
894
  };
917
895
  }, {
918
- sub: string;
919
- email?: string | undefined;
920
- email_verified?: boolean | undefined;
921
- name?: string | undefined;
922
- picture?: string | undefined;
923
- given_name?: string | undefined;
924
- family_name?: string | undefined;
896
+ sub: unknown;
925
897
  }>;
926
898
  oauth2EndSession: better_call0.StrictEndpoint<"/oauth2/end-session", {
927
899
  method: "GET";
@@ -2306,10 +2278,19 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2306
2278
  type: "string";
2307
2279
  required: false;
2308
2280
  };
2281
+ authorizationCodeId: {
2282
+ type: "string";
2283
+ required: false;
2284
+ index: true;
2285
+ };
2309
2286
  resources: {
2310
2287
  type: "string[]";
2311
2288
  required: false;
2312
2289
  };
2290
+ requestedUserInfoClaims: {
2291
+ type: "string[]";
2292
+ required: false;
2293
+ };
2313
2294
  expiresAt: {
2314
2295
  type: "date";
2315
2296
  };
@@ -2373,10 +2354,19 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2373
2354
  type: "string";
2374
2355
  required: false;
2375
2356
  };
2357
+ authorizationCodeId: {
2358
+ type: "string";
2359
+ required: false;
2360
+ index: true;
2361
+ };
2376
2362
  resources: {
2377
2363
  type: "string[]";
2378
2364
  required: false;
2379
2365
  };
2366
+ requestedUserInfoClaims: {
2367
+ type: "string[]";
2368
+ required: false;
2369
+ };
2380
2370
  refreshId: {
2381
2371
  type: "string";
2382
2372
  required: false;
@@ -2435,6 +2425,10 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2435
2425
  type: "string[]";
2436
2426
  required: false;
2437
2427
  };
2428
+ requestedUserInfoClaims: {
2429
+ type: "string[]";
2430
+ required: false;
2431
+ };
2438
2432
  scopes: {
2439
2433
  type: "string[]";
2440
2434
  required: true;
@@ -335,10 +335,19 @@ declare const schema: {
335
335
  type: "string";
336
336
  required: false;
337
337
  };
338
+ authorizationCodeId: {
339
+ type: "string";
340
+ required: false;
341
+ index: true;
342
+ };
338
343
  resources: {
339
344
  type: "string[]";
340
345
  required: false;
341
346
  };
347
+ requestedUserInfoClaims: {
348
+ type: "string[]";
349
+ required: false;
350
+ };
342
351
  expiresAt: {
343
352
  type: "date";
344
353
  };
@@ -414,10 +423,19 @@ declare const schema: {
414
423
  type: "string";
415
424
  required: false;
416
425
  };
426
+ authorizationCodeId: {
427
+ type: "string";
428
+ required: false;
429
+ index: true;
430
+ };
417
431
  resources: {
418
432
  type: "string[]";
419
433
  required: false;
420
434
  };
435
+ requestedUserInfoClaims: {
436
+ type: "string[]";
437
+ required: false;
438
+ };
421
439
  refreshId: {
422
440
  type: "string";
423
441
  required: false;
@@ -476,6 +494,10 @@ declare const schema: {
476
494
  type: "string[]";
477
495
  required: false;
478
496
  };
497
+ requestedUserInfoClaims: {
498
+ type: "string[]";
499
+ required: false;
500
+ };
479
501
  scopes: {
480
502
  type: "string[]";
481
503
  required: true;
@@ -670,6 +692,13 @@ interface OAuthTokenIssueParams {
670
692
  resources?: string[];
671
693
  /** Full original authorized resources for the grant, used to seed refresh tokens. */
672
694
  originalResources?: string[];
695
+ /**
696
+ * OIDC UserInfo claim names requested by the authorization request's
697
+ * `claims.userinfo` object. The authorization server persists these names so
698
+ * refresh-token rotation and opaque access tokens continue honoring the same
699
+ * UserInfo request contract.
700
+ */
701
+ requestedUserInfoClaims?: string[];
673
702
  /**
674
703
  * Additional JWT access-token claims for this single issuance.
675
704
  *
@@ -867,6 +896,11 @@ interface OAuthUserInfoExtensionInput {
867
896
  scopes: string[];
868
897
  jwt: JWTPayload;
869
898
  client?: SchemaClient<Scope[]>;
899
+ /**
900
+ * Claim names explicitly requested through the OIDC `claims.userinfo`
901
+ * authorization request parameter.
902
+ */
903
+ requestedClaims: string[];
870
904
  }
871
905
  /**
872
906
  * What a companion plugin contributes to the OAuth Provider, registered through
@@ -1121,8 +1155,9 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1121
1155
  * Allow unauthenticated dynamic client registration.
1122
1156
  *
1123
1157
  * When enabled, the `/oauth2/register` endpoint accepts requests
1124
- * without a session, but only for public clients
1125
- * (`token_endpoint_auth_method: "none"`).
1158
+ * without a session. Public clients use
1159
+ * `token_endpoint_auth_method: "none"`; confidential clients receive a
1160
+ * one-time `client_secret` in the registration response.
1126
1161
  *
1127
1162
  * For verified client discovery (MCP), consider installing the
1128
1163
  * `@better-auth/cimd` plugin, which verifies client identity through
@@ -1139,8 +1174,8 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1139
1174
  * - session-backed: a logged-in user with client-create privileges.
1140
1175
  * - token-backed: a valid initial access token, when
1141
1176
  * {@link OAuthOptions.validateInitialAccessToken} is defined.
1142
- * - open public-only: unauthenticated registration constrained to public
1143
- * clients, when {@link OAuthOptions.allowUnauthenticatedClientRegistration}
1177
+ * - open: unauthenticated registration, when
1178
+ * {@link OAuthOptions.allowUnauthenticatedClientRegistration}
1144
1179
  * is enabled.
1145
1180
  *
1146
1181
  * @default false
@@ -1209,6 +1244,17 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1209
1244
  * @default - clientRegistrationDefaultScopes
1210
1245
  */
1211
1246
  clientRegistrationAllowedScopes?: Scopes;
1247
+ /**
1248
+ * Whether dynamically registered confidential clients require PKCE by default.
1249
+ *
1250
+ * This is server-owned registration policy. Dynamic client registration does
1251
+ * not accept `require_pkce` from the client request, and public clients or
1252
+ * authorization requests with `offline_access` still require PKCE unless the
1253
+ * confidential OIDC request includes both `openid` and `nonce`.
1254
+ *
1255
+ * @default true
1256
+ */
1257
+ clientRegistrationRequirePKCE?: boolean;
1212
1258
  /**
1213
1259
  * How long a dynamically created confidential client
1214
1260
  * should last for.
@@ -1294,11 +1340,16 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1294
1340
  *
1295
1341
  * - `client_id` - The ID of the client.
1296
1342
  * - `scope` - The requested scopes.
1343
+ * - `claims` - The OIDC claims request, when the client requested specific
1344
+ * claims. Consent pages should surface `claims.userinfo` names alongside
1345
+ * scopes because accepted UserInfo claims can affect the UserInfo response.
1297
1346
  * - `code` - The authorization code.
1298
1347
  *
1299
1348
  * once the user consents, you need to call the `/oauth2/consent` endpoint
1300
- * with the code and `accept: true` to complete the authorization. Which will
1301
- * then return the client to the `redirect_uri` with the authorization code.
1349
+ * with the code and `accept: true` to complete the authorization. Include a
1350
+ * `claims` object if the user accepted only some requested UserInfo claims.
1351
+ * The endpoint will then return the client to the `redirect_uri` with the
1352
+ * authorization code.
1302
1353
  *
1303
1354
  * @example
1304
1355
  * ```ts
@@ -1492,6 +1543,11 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1492
1543
  * in the /userinfo request (matches jwt.scopes) */
1493
1544
  scopes: Scopes; /** The access token payload used in the /userinfo request */
1494
1545
  jwt: JWTPayload;
1546
+ /**
1547
+ * Claim names explicitly requested through the OIDC `claims.userinfo`
1548
+ * authorization request parameter.
1549
+ */
1550
+ requestedClaims: string[];
1495
1551
  }) => Awaitable<Record<string, any>>;
1496
1552
  /**
1497
1553
  * Custom claims attached to OIDC id tokens.
@@ -1501,6 +1557,11 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
1501
1557
  * example.com should namespace an organization at
1502
1558
  * https://example.com/organization.
1503
1559
  *
1560
+ * Reserved ID token claim names (`iss`, `sub`, `aud`, `exp`, `nbf`, `iat`,
1561
+ * `jti`, `nonce`, `sid`, `at_hash`, `c_hash`, `s_hash`, `auth_time`, `acr`,
1562
+ * `amr`, `azp`) are stripped at issuance with a warning log. The
1563
+ * authorization server owns these values.
1564
+ *
1504
1565
  * @param info - context that may be useful when creating custom claims
1505
1566
  */
1506
1567
  customIdTokenClaims?: (info: {
@@ -1797,6 +1858,13 @@ interface OAuthAuthorizationQuery {
1797
1858
  * Optional in the query when using request_uri (PAR) — resolved from stored params.
1798
1859
  */
1799
1860
  response_type?: "code";
1861
+ /**
1862
+ * OpenID Connect Request Object by value.
1863
+ *
1864
+ * The parameter is parsed so unsupported use can be rejected with
1865
+ * `request_not_supported`; Better Auth does not process Request Objects yet.
1866
+ */
1867
+ request?: string;
1800
1868
  /**
1801
1869
  * PAR request_uri. When present, other params are resolved from the stored request.
1802
1870
  */
@@ -1904,6 +1972,14 @@ interface OAuthAuthorizationQuery {
1904
1972
  * with the Claim Value being the nonce value sent in the Authentication Request.
1905
1973
  */
1906
1974
  nonce?: string;
1975
+ /**
1976
+ * OIDC Claims request parameter. In an authorization request it is a
1977
+ * form-encoded JSON string; Request Object resolvers may provide the parsed
1978
+ * object form.
1979
+ *
1980
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
1981
+ */
1982
+ claims?: string | Record<string, unknown>;
1907
1983
  /**
1908
1984
  * RFC 9449 authorization request parameter. When present, the authorization
1909
1985
  * code is bound to this JWK thumbprint and the token request must present a
@@ -1998,6 +2074,17 @@ interface OAuthClientResource {
1998
2074
  metadata?: Record<string, unknown> | null;
1999
2075
  createdAt: Date;
2000
2076
  }
2077
+ /**
2078
+ * The authorization request as persisted alongside a minted code. Identical to
2079
+ * {@link OAuthAuthorizationQuery} except `redirect_uri` is optional: a headless
2080
+ * authorization request (first-party-apps / device-style) carries none, and
2081
+ * RFC 6749 §4.1.3 only binds `redirect_uri` at the token endpoint when the
2082
+ * authorization request included one. Mirrors the runtime
2083
+ * `storedAuthorizationQuerySchema`.
2084
+ */
2085
+ interface StoredAuthorizationQuery extends Omit<OAuthAuthorizationQuery, "redirect_uri"> {
2086
+ redirect_uri?: string;
2087
+ }
2001
2088
  /**
2002
2089
  * Stored within the verification.value field
2003
2090
  * in JSON format.
@@ -2007,7 +2094,7 @@ interface OAuthClientResource {
2007
2094
  */
2008
2095
  interface VerificationValue {
2009
2096
  type: "authorization_code";
2010
- query: OAuthAuthorizationQuery;
2097
+ query: StoredAuthorizationQuery;
2011
2098
  sessionId: string;
2012
2099
  userId: string;
2013
2100
  resource?: string[];
@@ -2098,7 +2185,7 @@ interface SchemaClient<Scopes extends readonly Scope[] = InternallySupportedScop
2098
2185
  tokenEndpointAuthMethod?: TokenEndpointAuthMethod;
2099
2186
  grantTypes?: GrantType[];
2100
2187
  responseTypes?: "code"[];
2101
- /** Client's JSON Web Key Set for `private_key_jwt` authentication. Mutually exclusive with `jwksUri`. */
2188
+ /** Client's JSON Web Key Set metadata. Mutually exclusive with `jwksUri`. */
2102
2189
  jwks?: string;
2103
2190
  /** URI for the client's JSON Web Key Set. Mutually exclusive with `jwks`. Must be HTTPS. */
2104
2191
  jwksUri?: string;
@@ -2181,6 +2268,11 @@ interface OAuthOpaqueAccessToken<Scopes extends readonly Scope[] = InternallySup
2181
2268
  * where no user is involved.
2182
2269
  */
2183
2270
  referenceId?: string;
2271
+ /**
2272
+ * Stored authorization-code identifier that produced this token family.
2273
+ * Used to revoke tokens after authorization-code replay is detected.
2274
+ */
2275
+ authorizationCodeId?: string;
2184
2276
  /**
2185
2277
  * The refresh token the access token is associated with.
2186
2278
  *
@@ -2207,6 +2299,11 @@ interface OAuthOpaqueAccessToken<Scopes extends readonly Scope[] = InternallySup
2207
2299
  * Resources allowed for this access token.
2208
2300
  */
2209
2301
  resources?: string[];
2302
+ /**
2303
+ * OIDC UserInfo claim names requested by the authorization request's
2304
+ * `claims.userinfo` object.
2305
+ */
2306
+ requestedUserInfoClaims?: string[];
2210
2307
  /**
2211
2308
  * RFC 7800 `cnf` confirmation that sender-constrains this access token (for
2212
2309
  * example DPoP `{ jkt }`). Surfaced as the `cnf` claim at introspection.
@@ -2221,6 +2318,7 @@ interface OAuthRefreshToken<Scopes extends readonly Scope[] = InternallySupporte
2221
2318
  sessionId?: string;
2222
2319
  userId: string;
2223
2320
  referenceId?: string;
2321
+ authorizationCodeId?: string;
2224
2322
  clientId?: string;
2225
2323
  expiresAt: Date;
2226
2324
  createdAt: Date;
@@ -2243,6 +2341,11 @@ interface OAuthRefreshToken<Scopes extends readonly Scope[] = InternallySupporte
2243
2341
  * Resources allowed for this refresh token
2244
2342
  */
2245
2343
  resources?: string[];
2344
+ /**
2345
+ * OIDC UserInfo claim names requested by the authorization request's
2346
+ * `claims.userinfo` object. Carried forward on rotation.
2347
+ */
2348
+ requestedUserInfoClaims?: string[];
2246
2349
  /**
2247
2350
  * RFC 7800 `cnf` confirmation that sender-constrains this refresh-token
2248
2351
  * family (for example DPoP `{ jkt }`). Carried forward on rotation.
@@ -2258,6 +2361,11 @@ type OAuthConsent<Scopes extends readonly Scope[] = InternallySupportedScopes[]>
2258
2361
  userId: string;
2259
2362
  resources?: string[];
2260
2363
  referenceId?: string;
2364
+ /**
2365
+ * OIDC UserInfo claim names consented from the authorization request's
2366
+ * `claims.userinfo` object.
2367
+ */
2368
+ requestedUserInfoClaims?: string[];
2261
2369
  scopes: Scopes;
2262
2370
  createdAt: Date;
2263
2371
  updatedAt: Date;
@@ -2506,19 +2614,16 @@ interface OIDCMetadata extends AuthServerMetadata {
2506
2614
  */
2507
2615
  userinfo_endpoint: string;
2508
2616
  /**
2509
- * acr_values supported.
2510
- *
2511
- * - `urn:mace:incommon:iap:silver`: Silver level of assurance
2512
- * - `urn:mace:incommon:iap:bronze`: Bronze level of assurance
2617
+ * Authentication Context Class Reference values supported.
2513
2618
  *
2514
- * Determination of acr_value is considered bronze by default.
2515
- * Silver level determination coming soon.
2619
+ * Better Auth does not advertise this field by default because it does not
2620
+ * currently evaluate requested Authentication Context Class References.
2516
2621
  *
2517
2622
  * @default
2518
- * ["urn:mace:incommon:iap:bronze"]
2519
- * @see https://incommon.org/federation/attributes.html
2623
+ * undefined
2624
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
2520
2625
  */
2521
- acr_values_supported: string[];
2626
+ acr_values_supported?: string[];
2522
2627
  /**
2523
2628
  * Supported subject types.
2524
2629
  *
@@ -2546,12 +2651,36 @@ interface OIDCMetadata extends AuthServerMetadata {
2546
2651
  * ["sub", "iss", "aud", "exp", "nbf", "iat", "jti", "email", "email_verified", "name", "family_name", "given_name", "sid", "scope", "azp"]
2547
2652
  */
2548
2653
  claims_supported: string[];
2654
+ /**
2655
+ * Whether the OP supports the OIDC `claims` request parameter.
2656
+ *
2657
+ * @default true
2658
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
2659
+ */
2660
+ claims_parameter_supported?: boolean;
2549
2661
  /**
2550
2662
  * RP-Initiated Logout Endpoint
2551
2663
  *
2552
2664
  * @see https://openid.net/specs/openid-connect-rpinitiated-1_0.html
2553
2665
  */
2554
2666
  end_session_endpoint: string;
2667
+ /**
2668
+ * Whether the OP supports OpenID Connect Request Objects by value.
2669
+ *
2670
+ * @default false
2671
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
2672
+ */
2673
+ request_parameter_supported?: boolean;
2674
+ /**
2675
+ * Whether the OP supports OpenID Connect Request Objects by reference.
2676
+ *
2677
+ * Discovery defaults this field to true when omitted, so providers that do
2678
+ * not support it should advertise false explicitly.
2679
+ *
2680
+ * @default false
2681
+ * @see https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
2682
+ */
2683
+ request_uri_parameter_supported?: boolean;
2555
2684
  /**
2556
2685
  * Prompt values supported by this OIDC server
2557
2686
  *
@@ -2619,8 +2748,9 @@ interface OAuthClient {
2619
2748
  *
2620
2749
  * @default true
2621
2750
  *
2622
- * Note: PKCE is always required for public clients and when
2623
- * requesting offline_access scope, regardless of this setting.
2751
+ * Note: PKCE is always required for public clients. When requesting
2752
+ * offline_access without PKCE, confidential OIDC clients must send both
2753
+ * `openid` and `nonce`.
2624
2754
  */
2625
2755
  require_pkce?: boolean;
2626
2756
  /**
@@ -2676,4 +2806,4 @@ interface ResourceServerMetadata {
2676
2806
  dpop_bound_access_tokens_required?: boolean;
2677
2807
  }
2678
2808
  //#endregion
2679
- export { OAuthProviderExtension as A, StoreTokenType as B, OAuthConsent as C, OAuthOpaqueAccessToken as D, OAuthMetadataExtensionInput as E, OAuthTokenResponse as F, ClientRegistrationRequest as H, OAuthUserInfoExtensionInput as I, Prompt as L, OAuthResource as M, OAuthResourceInput as N, OAuthOptions as O, OAuthTokenIssueParams as P, SchemaClient as R, OAuthClientResource as S, OAuthExtensionGrantHandlerInput as T, ResourceUriSchema as U, VerificationValue as V, OAuthClaimExtensionInput as _, GrantType as a, OAuthClientAuthenticationResult as b, ResourceServerMetadata as c, ActiveAccessTokenPayload as d, AuthorizePrompt as f, OAuthAuthorizationQuery as g, OAuthAuthenticatedClient as h, Confirmation as i, OAuthRefreshToken as j, OAuthProviderApi as k, TokenEndpointAuthMethod as l, InitialAccessTokenAuthorization as m, AuthServerMetadata as n, OAuthClient as o, ClientDiscovery as p, BearerMethodsSupported as r, OIDCMetadata as s, AuthMethod as t, TokenType as u, OAuthClientAuthenticationInput as v, OAuthExtensionGrantHandler as w, OAuthClientAuthenticationStrategy as x, OAuthClientAuthenticationRequest as y, Scope as z };
2809
+ export { OAuthProviderExtension as A, StoreTokenType as B, OAuthConsent as C, OAuthOpaqueAccessToken as D, OAuthMetadataExtensionInput as E, OAuthTokenResponse as F, VerificationValue as H, OAuthUserInfoExtensionInput as I, Prompt as L, OAuthResource as M, OAuthResourceInput as N, OAuthOptions as O, OAuthTokenIssueParams as P, SchemaClient as R, OAuthClientResource as S, OAuthExtensionGrantHandlerInput as T, ClientRegistrationRequest as U, StoredAuthorizationQuery as V, ResourceUriSchema as W, OAuthClaimExtensionInput as _, GrantType as a, OAuthClientAuthenticationResult as b, ResourceServerMetadata as c, ActiveAccessTokenPayload as d, AuthorizePrompt as f, OAuthAuthorizationQuery as g, OAuthAuthenticatedClient as h, Confirmation as i, OAuthRefreshToken as j, OAuthProviderApi as k, TokenEndpointAuthMethod as l, InitialAccessTokenAuthorization as m, AuthServerMetadata as n, OAuthClient as o, ClientDiscovery as p, BearerMethodsSupported as r, OIDCMetadata as s, AuthMethod as t, TokenType as u, OAuthClientAuthenticationInput as v, OAuthExtensionGrantHandler as w, OAuthClientAuthenticationStrategy as x, OAuthClientAuthenticationRequest as y, Scope as z };
@@ -1,4 +1,4 @@
1
- import { n as canonicalizeOAuthQueryParams } from "./signed-query-CFv2jNMT.mjs";
1
+ import { n as canonicalizeOAuthQueryParams } from "./signed-query-Df1MNiSH.mjs";
2
2
  import { constantTimeEqual, makeSignature, symmetricDecrypt, symmetricEncrypt } from "better-auth/crypto";
3
3
  import { APIError } from "better-call";
4
4
  import { logger } from "@better-auth/core/env";
@@ -631,7 +631,7 @@ async function extractClientCredentials(ctx, opts, expectedAudience) {
631
631
  confirmation: result.confirmation
632
632
  };
633
633
  }
634
- const { verifyClientAssertion: verify } = await import("./client-assertion-CctbJywV.mjs").then((n) => n.t);
634
+ const { verifyClientAssertion: verify } = await import("./client-assertion-D-tAYsKC.mjs").then((n) => n.t);
635
635
  return {
636
636
  kind: "pre_verified",
637
637
  method: "private_key_jwt",
@@ -736,27 +736,32 @@ function removeMaxAgeFromQuery(query) {
736
736
  }
737
737
  var PKCERequirementErrors = /* @__PURE__ */ function(PKCERequirementErrors) {
738
738
  PKCERequirementErrors["PUBLIC_CLIENT"] = "pkce is required for public clients";
739
- PKCERequirementErrors["OFFLINE_ACCESS_SCOPE"] = "pkce is required when requesting offline_access scope";
739
+ PKCERequirementErrors["OFFLINE_ACCESS_SCOPE"] = "pkce or OIDC nonce is required when requesting offline_access scope";
740
740
  PKCERequirementErrors["CLIENT_REQUIRE_PKCE"] = "pkce is required for this client";
741
741
  return PKCERequirementErrors;
742
742
  }(PKCERequirementErrors || {});
743
743
  /**
744
- * Determines if PKCE is required for a given client and scope.
744
+ * Determines if PKCE is required for a given client and authorization request.
745
745
  *
746
746
  * PKCE is always required for:
747
747
  * 1. Public clients (cannot securely store client_secret)
748
- * 2. Requests with offline_access scope (refresh token security)
748
+ * 2. Requests with offline_access scope unless a confidential OIDC request
749
+ * already uses nonce as its authorization-code injection countermeasure.
749
750
  *
750
- * For confidential clients without offline_access:
751
+ * For confidential clients:
751
752
  * - Uses client.requirePKCE if set (defaults to true)
752
753
  *
753
754
  * Returns false if PKCE is not required, or the reason it is required.
754
755
  *
755
756
  * @internal
756
757
  */
757
- function isPKCERequired(client, requestedScopes) {
758
+ function isPKCERequired(client, request) {
758
759
  if (client.tokenEndpointAuthMethod === "none" || client.type === "native" || client.type === "user-agent-based" || client.public === true) return PKCERequirementErrors.PUBLIC_CLIENT;
759
- if (requestedScopes?.includes("offline_access")) return PKCERequirementErrors.OFFLINE_ACCESS_SCOPE;
760
+ const requestScopes = request?.scopes ?? [];
761
+ const isOpenIdRequest = requestScopes.includes("openid");
762
+ const hasNonce = typeof request?.nonce === "string" && request.nonce.length > 0;
763
+ const hasOidcNonce = isOpenIdRequest && hasNonce;
764
+ if (requestScopes.includes("offline_access") && !hasOidcNonce) return PKCERequirementErrors.OFFLINE_ACCESS_SCOPE;
760
765
  if (client.requirePKCE ?? true) return PKCERequirementErrors.CLIENT_REQUIRE_PKCE;
761
766
  return false;
762
767
  }
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.0-beta.7";
3
+ const PACKAGE_VERSION = "1.7.0-beta.9";
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.7.0-beta.7",
3
+ "version": "1.7.0-beta.9",
4
4
  "description": "An oauth provider plugin for Better Auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -63,15 +63,15 @@
63
63
  "devDependencies": {
64
64
  "listhen": "^1.9.0",
65
65
  "tsdown": "0.21.1",
66
- "@better-auth/core": "1.7.0-beta.7",
67
- "better-auth": "1.7.0-beta.7"
66
+ "@better-auth/core": "1.7.0-beta.9",
67
+ "better-auth": "1.7.0-beta.9"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@better-auth/utils": "0.4.2",
71
71
  "@better-fetch/fetch": "1.3.1",
72
72
  "better-call": "1.3.6",
73
- "@better-auth/core": "^1.7.0-beta.7",
74
- "better-auth": "^1.7.0-beta.7"
73
+ "@better-auth/core": "^1.7.0-beta.9",
74
+ "better-auth": "^1.7.0-beta.9"
75
75
  },
76
76
  "scripts": {
77
77
  "build": "tsdown",