@better-auth/oauth-provider 1.7.0-beta.0 → 1.7.0-beta.2

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.
@@ -315,6 +315,46 @@ type InternallySupportedScopes = "openid" | "profile" | "email" | "offline_acces
315
315
  type Scope = LiteralString | InternallySupportedScopes;
316
316
  type Prompt = "none" | "consent" | "login" | "create" | "select_account";
317
317
  type AuthorizePrompt = Prompt | "login consent" | "select_account consent";
318
+ /**
319
+ * Describes how to resolve a `client_id` from an external source (a URL-based
320
+ * metadata document, a federated registry, an attestation header, etc.) and
321
+ * what fields that source contributes to discovery metadata.
322
+ *
323
+ * Plugins install one of these onto {@link OAuthOptions.clientDiscovery}.
324
+ * The host walks the configured entries in order and returns the first
325
+ * non-null `resolve()` result.
326
+ */
327
+ interface ClientDiscovery<Scopes extends readonly Scope[] = InternallySupportedScopes[]> {
328
+ /**
329
+ * Stable identifier used in error messages and diagnostics. Convention
330
+ * is to match the plugin id (for example `"cimd"`).
331
+ */
332
+ readonly id: string;
333
+ /**
334
+ * Return `true` if this discovery handles the given `client_id`. Called
335
+ * on every `getClient()` lookup for every configured discovery, so keep
336
+ * it cheap and synchronous.
337
+ */
338
+ matches: (clientId: string) => boolean;
339
+ /**
340
+ * Resolve a client when this discovery matches. Receives the existing DB
341
+ * record (or `null`) so an implementation can decide between creating,
342
+ * refreshing, or passing through to the database result.
343
+ *
344
+ * Return:
345
+ * - a client record: `getClient()` returns it (creation / refresh / takeover).
346
+ * - `null`: `getClient()` falls through to the next matching discovery
347
+ * or to the database record (if any).
348
+ */
349
+ resolve: (ctx: GenericEndpointContext, clientId: string, existing: SchemaClient<Scopes> | null) => Awaitable<SchemaClient<Scopes> | null>;
350
+ /**
351
+ * Fields merged into `/.well-known/oauth-authorization-server` and
352
+ * `/.well-known/openid-configuration` responses. Useful for advertising
353
+ * RFC-registered discovery flags like
354
+ * `client_id_metadata_document_supported`.
355
+ */
356
+ discoveryMetadata?: Record<string, unknown>;
357
+ }
318
358
  interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScopes[]> {
319
359
  /**
320
360
  * Custom schema definitions
@@ -412,10 +452,13 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
412
452
  /**
413
453
  * Allow unauthenticated dynamic client registration.
414
454
  *
415
- * Support for `allowUnauthenticatedClientRegistration` **will be deprecated**
416
- * when the MCP protocol standardizes unauthenticated dynamic client registration.
417
- * As of writing, both [Client ID Metadata Documents](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/991)
418
- * and [`software_statement` and `jwks_uri`](https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1032) are under debate.
455
+ * When enabled, the `/oauth2/register` endpoint accepts requests
456
+ * without a session, but only for public clients
457
+ * (`token_endpoint_auth_method: "none"`).
458
+ *
459
+ * For verified client discovery (MCP), consider installing the
460
+ * `@better-auth/cimd` plugin, which verifies client identity through
461
+ * domain ownership via Client ID Metadata Documents.
419
462
  *
420
463
  * @default false
421
464
  */
@@ -426,6 +469,21 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
426
469
  * @default false
427
470
  */
428
471
  allowDynamicClientRegistration?: boolean;
472
+ /**
473
+ * Discovery implementations consulted by `getClient()` when resolving
474
+ * a `client_id`. Each entry decides whether it handles the `client_id`
475
+ * via {@link ClientDiscovery.matches}, then creates, refreshes, or
476
+ * passes on a client record. Entries run in order; the first one to
477
+ * return a client wins.
478
+ *
479
+ * Each entry also contributes {@link ClientDiscovery.discoveryMetadata}
480
+ * into the `/.well-known/oauth-authorization-server` and
481
+ * `/.well-known/openid-configuration` responses.
482
+ *
483
+ * Plugins such as `@better-auth/cimd` install an entry here at init
484
+ * time; users can also pass discovery implementations directly.
485
+ */
486
+ clientDiscovery?: ClientDiscovery<Scopes> | ClientDiscovery<Scopes>[];
429
487
  /**
430
488
  * List of scopes for newly registered clients
431
489
  * if not requested.
@@ -768,6 +826,34 @@ interface OAuthOptions<Scopes extends readonly Scope[] = InternallySupportedScop
768
826
  resource?: string; /** oAuthClient metadata */
769
827
  metadata?: Record<string, any>;
770
828
  }) => Awaitable<Record<string, any>>;
829
+ /**
830
+ * Custom fields to include in the token response body.
831
+ *
832
+ * Unlike `customAccessTokenClaims` (which adds claims inside the JWT payload),
833
+ * this adds fields to the JSON response envelope alongside `access_token`,
834
+ * `token_type`, etc. Standard OAuth fields (`access_token`, `token_type`,
835
+ * `expires_in`, `expires_at`, `refresh_token`, `scope`, `id_token`) cannot
836
+ * be overridden.
837
+ *
838
+ * @param info - context that may be useful when creating custom fields
839
+ */
840
+ customTokenResponseFields?: (info: {
841
+ /** The grant type being processed */grantType: GrantType;
842
+ /**
843
+ * The user, if applicable.
844
+ * Undefined for `client_credentials` (M2M, no user).
845
+ * Always present for `authorization_code` and `refresh_token`.
846
+ */
847
+ user?: (User & Record<string, unknown>) | null; /** Scopes granted for this token */
848
+ scopes: Scopes; /** oAuthClient metadata */
849
+ metadata?: Record<string, any>;
850
+ /**
851
+ * The authorization code verification value.
852
+ * Only present for `authorization_code` grant. Contains the original
853
+ * authorization request parameters (`query`), `referenceId`, `sessionId`, etc.
854
+ */
855
+ verificationValue?: VerificationValue;
856
+ }) => Awaitable<Record<string, unknown>>;
771
857
  /**
772
858
  * Overwrite specific /.well-known/openid-configuration
773
859
  * values so they are not available publically.
@@ -1476,6 +1562,17 @@ interface AuthServerMetadata {
1476
1562
  * @default true
1477
1563
  */
1478
1564
  authorization_response_iss_parameter_supported?: boolean;
1565
+ /**
1566
+ * Whether the authorization server supports discovering clients via
1567
+ * [Client ID Metadata Documents](https://datatracker.ietf.org/doc/draft-ietf-oauth-client-id-metadata-document/)
1568
+ * (an HTTPS URL as `client_id`).
1569
+ *
1570
+ * Set at runtime by the `@better-auth/cimd` plugin (or any other
1571
+ * `ClientDiscovery` that contributes this field via
1572
+ * {@link ClientDiscovery.discoveryMetadata}). oauth-provider never sets
1573
+ * it on its own.
1574
+ */
1575
+ client_id_metadata_document_supported?: boolean;
1479
1576
  }
1480
1577
  /**
1481
1578
  * Metadata returned by the openid-configuration endpoint:
@@ -1642,4 +1739,4 @@ interface ResourceServerMetadata {
1642
1739
  dpop_bound_access_tokens_required?: boolean;
1643
1740
  }
1644
1741
  //#endregion
1645
- export { Awaitable as _, ResourceServerMetadata as a, OAuthConsent as c, OAuthRefreshToken as d, Prompt as f, VerificationValue as g, StoreTokenType as h, OIDCMetadata as i, OAuthOpaqueAccessToken as l, Scope as m, GrantType as n, AuthorizePrompt as o, SchemaClient as p, OAuthClient as r, OAuthAuthorizationQuery as s, AuthServerMetadata as t, OAuthOptions as u };
1742
+ export { Scope as _, OIDCMetadata as a, Awaitable as b, AuthorizePrompt as c, OAuthConsent as d, OAuthOpaqueAccessToken as f, SchemaClient as g, Prompt as h, OAuthClient as i, ClientDiscovery as l, OAuthRefreshToken as m, AuthServerMetadata as n, ResourceServerMetadata as o, OAuthOptions as p, GrantType as r, TokenEndpointAuthMethod as s, AuthMethod as t, OAuthAuthorizationQuery as u, StoreTokenType as v, VerificationValue as y };
@@ -1,10 +1,40 @@
1
- import { c as OAuthConsent, i as OIDCMetadata, m as Scope, r as OAuthClient, t as AuthServerMetadata, u as OAuthOptions } from "./oauth-C8aTlaAC.mjs";
1
+ import { _ as Scope, d as OAuthConsent, h as Prompt, i as OAuthClient, p as OAuthOptions, r as GrantType, s as TokenEndpointAuthMethod, t as AuthMethod } from "./oauth-CU79t-eG.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";
5
5
  import * as jose from "jose";
6
6
  import * as better_auth0 from "better-auth";
7
7
 
8
+ //#region src/oauth-endpoint.d.ts
9
+ /**
10
+ * Canonical OAuth 2.0 / OpenID Connect error codes. The union is the single
11
+ * vocabulary for every error-emitting surface in this plugin: token, authorize,
12
+ * revoke, introspect, register, userinfo, logout, consent, and the redirect
13
+ * error channel. Entries are grouped by source RFC so the declaration doubles
14
+ * as a specification map.
15
+ *
16
+ * The trailing `(string & {})` keeps the type open for product-specific codes
17
+ * (e.g. `"invalid_verification"`, `"invalid_user"`) while preserving editor
18
+ * autocomplete for the listed standard codes. Prefer a standard code whenever
19
+ * one applies; fall back to a custom string only for states no RFC covers.
20
+ */
21
+ type OAuthErrorCode = "invalid_request" | "invalid_client" | "invalid_grant" | "unauthorized_client" | "unsupported_grant_type" | "unsupported_response_type" | "invalid_scope" | "access_denied" | "server_error" | "temporarily_unavailable" | "invalid_token" | "unsupported_token_type" | "invalid_redirect_uri" | "invalid_client_metadata" | "invalid_software_statement" | "unapproved_software_statement" | "invalid_target" | "invalid_request_object" | "login_required" | "consent_required" | "interaction_required" | "account_selection_required" | "invalid_request_uri" | "request_not_supported" | "request_uri_not_supported" | "registration_not_supported" | (string & {});
22
+ type OAuthFieldErrorCodeMap = {
23
+ missing?: OAuthErrorCode;
24
+ invalid?: OAuthErrorCode;
25
+ };
26
+ type OAuthFieldErrorCode = OAuthErrorCode | OAuthFieldErrorCodeMap;
27
+ interface OAuthEndpointErrorResult {
28
+ error: OAuthErrorCode;
29
+ error_description: string;
30
+ }
31
+ interface OAuthEndpointRedirectContext<Ctx = unknown> {
32
+ error: OAuthErrorCode;
33
+ error_description: string;
34
+ ctx: Ctx;
35
+ }
36
+ type OAuthRedirectOnError<Ctx = any> = (result: OAuthEndpointRedirectContext<Ctx>) => unknown;
37
+ //#endregion
8
38
  //#region src/oauth.d.ts
9
39
  declare module "@better-auth/core" {
10
40
  interface BetterAuthPluginRegistry<AuthOptions, Options> {
@@ -54,7 +84,64 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
54
84
  metadata: {
55
85
  SERVER_ONLY: true;
56
86
  };
57
- }, AuthServerMetadata>;
87
+ }, {
88
+ jwks_uri?: string | undefined;
89
+ userinfo_endpoint: string;
90
+ acr_values_supported: string[];
91
+ subject_types_supported: ("public" | "pairwise")[];
92
+ claims_supported: string[];
93
+ end_session_endpoint: string;
94
+ prompt_values_supported: Prompt[];
95
+ issuer: string;
96
+ authorization_endpoint: string;
97
+ token_endpoint: string;
98
+ registration_endpoint: string;
99
+ scopes_supported?: string[] | undefined;
100
+ response_types_supported: "code"[];
101
+ response_modes_supported: "query"[];
102
+ grant_types_supported: GrantType[];
103
+ token_endpoint_auth_methods_supported?: TokenEndpointAuthMethod[] | undefined;
104
+ token_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
105
+ service_documentation?: string | undefined;
106
+ ui_locales_supported?: string[] | undefined;
107
+ op_policy_uri?: string | undefined;
108
+ op_tos_uri?: string | undefined;
109
+ revocation_endpoint?: string | undefined;
110
+ revocation_endpoint_auth_methods_supported?: AuthMethod[] | undefined;
111
+ revocation_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
112
+ introspection_endpoint?: string | undefined;
113
+ introspection_endpoint_auth_methods_supported?: AuthMethod[] | undefined;
114
+ introspection_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
115
+ code_challenge_methods_supported: "S256"[];
116
+ authorization_response_iss_parameter_supported?: boolean | undefined;
117
+ client_id_metadata_document_supported?: boolean | undefined;
118
+ id_token_signing_alg_values_supported: better_auth_plugins0.JWSAlgorithms[] | ["HS256"];
119
+ } | {
120
+ issuer: string;
121
+ authorization_endpoint: string;
122
+ token_endpoint: string;
123
+ jwks_uri?: string;
124
+ registration_endpoint: string;
125
+ scopes_supported?: string[];
126
+ response_types_supported: "code"[];
127
+ response_modes_supported: "query"[];
128
+ grant_types_supported: GrantType[];
129
+ token_endpoint_auth_methods_supported?: TokenEndpointAuthMethod[];
130
+ token_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[];
131
+ service_documentation?: string;
132
+ ui_locales_supported?: string[];
133
+ op_policy_uri?: string;
134
+ op_tos_uri?: string;
135
+ revocation_endpoint?: string;
136
+ revocation_endpoint_auth_methods_supported?: AuthMethod[];
137
+ revocation_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[];
138
+ introspection_endpoint?: string;
139
+ introspection_endpoint_auth_methods_supported?: AuthMethod[];
140
+ introspection_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[];
141
+ code_challenge_methods_supported: "S256"[];
142
+ authorization_response_iss_parameter_supported?: boolean;
143
+ client_id_metadata_document_supported?: boolean;
144
+ }>;
58
145
  /**
59
146
  * A server-only endpoint that helps provide the
60
147
  * OpenId configuration at the well-known endpoint.
@@ -67,26 +154,56 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
67
154
  metadata: {
68
155
  SERVER_ONLY: true;
69
156
  };
70
- }, Omit<OIDCMetadata, "id_token_signing_alg_values_supported"> & {
157
+ }, {
158
+ jwks_uri?: string | undefined;
159
+ userinfo_endpoint: string;
160
+ acr_values_supported: string[];
161
+ subject_types_supported: ("public" | "pairwise")[];
162
+ claims_supported: string[];
163
+ end_session_endpoint: string;
164
+ prompt_values_supported: Prompt[];
165
+ issuer: string;
166
+ authorization_endpoint: string;
167
+ token_endpoint: string;
168
+ registration_endpoint: string;
169
+ scopes_supported?: string[] | undefined;
170
+ response_types_supported: "code"[];
171
+ response_modes_supported: "query"[];
172
+ grant_types_supported: GrantType[];
173
+ token_endpoint_auth_methods_supported?: TokenEndpointAuthMethod[] | undefined;
174
+ token_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
175
+ service_documentation?: string | undefined;
176
+ ui_locales_supported?: string[] | undefined;
177
+ op_policy_uri?: string | undefined;
178
+ op_tos_uri?: string | undefined;
179
+ revocation_endpoint?: string | undefined;
180
+ revocation_endpoint_auth_methods_supported?: AuthMethod[] | undefined;
181
+ revocation_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
182
+ introspection_endpoint?: string | undefined;
183
+ introspection_endpoint_auth_methods_supported?: AuthMethod[] | undefined;
184
+ introspection_endpoint_auth_signing_alg_values_supported?: better_auth0.AssertionSigningAlgorithm[] | undefined;
185
+ code_challenge_methods_supported: "S256"[];
186
+ authorization_response_iss_parameter_supported?: boolean | undefined;
187
+ client_id_metadata_document_supported?: boolean | undefined;
71
188
  id_token_signing_alg_values_supported: better_auth_plugins0.JWSAlgorithms[] | ["HS256"];
72
189
  }>;
73
190
  oauth2Authorize: better_call0.StrictEndpoint<"/oauth2/authorize", {
74
191
  method: "GET";
75
192
  query: z.ZodObject<{
76
- response_type: z.ZodOptional<z.ZodEnum<{
193
+ response_type: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
77
194
  code: "code";
78
- }>>;
195
+ }>>>;
79
196
  client_id: z.ZodString;
80
197
  redirect_uri: z.ZodOptional<z.ZodURL>;
81
198
  scope: z.ZodOptional<z.ZodString>;
82
199
  state: z.ZodOptional<z.ZodString>;
83
200
  request_uri: z.ZodOptional<z.ZodString>;
84
201
  code_challenge: z.ZodOptional<z.ZodString>;
85
- code_challenge_method: z.ZodOptional<z.ZodEnum<{
202
+ code_challenge_method: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
86
203
  S256: "S256";
87
- }>>;
204
+ }>>>;
88
205
  nonce: z.ZodOptional<z.ZodString>;
89
- prompt: z.ZodOptional<z.ZodEnum<{
206
+ prompt: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodEnum<{
90
207
  none: "none";
91
208
  consent: "consent";
92
209
  login: "login";
@@ -94,8 +211,14 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
94
211
  select_account: "select_account";
95
212
  "login consent": "login consent";
96
213
  "select_account consent": "select_account consent";
97
- }>>;
214
+ }>>>;
98
215
  }, z.core.$strip>;
216
+ redirectOnError: OAuthRedirectOnError<better_auth0.GenericEndpointContext>;
217
+ errorCodesByField: {
218
+ response_type: {
219
+ invalid: "unsupported_response_type";
220
+ };
221
+ };
99
222
  metadata: {
100
223
  openapi: {
101
224
  description: string;
@@ -291,11 +414,11 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
291
414
  oauth2Token: better_call0.StrictEndpoint<"/oauth2/token", {
292
415
  method: "POST";
293
416
  body: z.ZodObject<{
294
- grant_type: z.ZodEnum<{
417
+ grant_type: z.ZodPipe<z.ZodString, z.ZodEnum<{
295
418
  authorization_code: "authorization_code";
296
419
  client_credentials: "client_credentials";
297
420
  refresh_token: "refresh_token";
298
- }>;
421
+ }>>;
299
422
  client_id: z.ZodOptional<z.ZodString>;
300
423
  client_secret: z.ZodOptional<z.ZodString>;
301
424
  client_assertion: z.ZodOptional<z.ZodString>;
@@ -307,6 +430,12 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
307
430
  resource: z.ZodOptional<z.ZodString>;
308
431
  scope: z.ZodOptional<z.ZodString>;
309
432
  }, z.core.$strip>;
433
+ errorCodesByField: {
434
+ grant_type: {
435
+ missing: "invalid_request";
436
+ invalid: "unsupported_grant_type";
437
+ };
438
+ };
310
439
  metadata: {
311
440
  allowedMediaTypes: string[];
312
441
  openapi: {
@@ -430,8 +559,10 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
430
559
  access_token: string;
431
560
  expires_in: number;
432
561
  expires_at: number;
433
- token_type: string;
562
+ token_type: "Bearer";
563
+ refresh_token: string | undefined;
434
564
  scope: string;
565
+ id_token: string | undefined;
435
566
  }>;
436
567
  oauth2Introspect: better_call0.StrictEndpoint<"/oauth2/introspect", {
437
568
  method: "POST";
@@ -441,10 +572,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
441
572
  client_assertion: z.ZodOptional<z.ZodString>;
442
573
  client_assertion_type: z.ZodOptional<z.ZodString>;
443
574
  token: z.ZodString;
444
- token_type_hint: z.ZodOptional<z.ZodEnum<{
445
- refresh_token: "refresh_token";
446
- access_token: "access_token";
447
- }>>;
575
+ token_type_hint: z.ZodOptional<z.ZodString>;
448
576
  }, z.core.$strip>;
449
577
  metadata: {
450
578
  allowedMediaTypes: string[];
@@ -471,7 +599,6 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
471
599
  };
472
600
  token_type_hint: {
473
601
  type: string;
474
- enum: string[];
475
602
  description: string;
476
603
  };
477
604
  resource: {
@@ -580,10 +707,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
580
707
  client_assertion: z.ZodOptional<z.ZodString>;
581
708
  client_assertion_type: z.ZodOptional<z.ZodString>;
582
709
  token: z.ZodString;
583
- token_type_hint: z.ZodOptional<z.ZodEnum<{
584
- refresh_token: "refresh_token";
585
- access_token: "access_token";
586
- }>>;
710
+ token_type_hint: z.ZodOptional<z.ZodString>;
587
711
  }, z.core.$strip>;
588
712
  metadata: {
589
713
  allowedMediaTypes: string[];
@@ -610,7 +734,6 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
610
734
  };
611
735
  token_type_hint: {
612
736
  type: string;
613
- enum: string[];
614
737
  description: string;
615
738
  };
616
739
  };
@@ -862,6 +985,12 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
862
985
  }>>;
863
986
  skip_consent: z.ZodOptional<z.ZodNever>;
864
987
  }, z.core.$strip>;
988
+ errorCodesByField: {
989
+ redirect_uris: "invalid_redirect_uri";
990
+ post_logout_redirect_uris: "invalid_redirect_uri";
991
+ software_statement: "invalid_software_statement";
992
+ };
993
+ defaultError: "invalid_client_metadata";
865
994
  metadata: {
866
995
  openapi: {
867
996
  description: string;
@@ -2107,4 +2236,4 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
2107
2236
  })[];
2108
2237
  };
2109
2238
  //#endregion
2110
- export { oauthProvider as n, getOAuthProviderState as t };
2239
+ export { OAuthErrorCode as a, OAuthRedirectOnError as c, OAuthEndpointRedirectContext as i, oauthProvider as n, OAuthFieldErrorCode as o, OAuthEndpointErrorResult as r, OAuthFieldErrorCodeMap as s, getOAuthProviderState as t };
@@ -89,17 +89,49 @@ async function verifyOAuthQueryParams(oauth_query, secret) {
89
89
  async function getClient(ctx, options, clientId) {
90
90
  const trustedClient = cachedTrustedClients.get(clientId);
91
91
  if (trustedClient) return Object.assign({}, trustedClient);
92
- const dbClient = await ctx.context.adapter.findOne({
92
+ let dbClient = await ctx.context.adapter.findOne({
93
93
  model: options.schema?.oauthClient?.modelName ?? "oauthClient",
94
94
  where: [{
95
95
  field: "clientId",
96
96
  value: clientId
97
97
  }]
98
98
  });
99
+ const discoveries = toClientDiscoveryArray(options.clientDiscovery);
100
+ for (const discovery of discoveries) {
101
+ if (!discovery.matches(clientId)) continue;
102
+ const resolved = await discovery.resolve(ctx, clientId, dbClient);
103
+ if (resolved) {
104
+ dbClient = resolved;
105
+ break;
106
+ }
107
+ }
99
108
  if (dbClient && options.cachedTrustedClients?.has(clientId)) cachedTrustedClients.set(clientId, Object.assign({}, dbClient));
100
109
  return dbClient;
101
110
  }
102
111
  /**
112
+ * Normalize the `clientDiscovery` option into an array. Accepts a single
113
+ * {@link ClientDiscovery}, an array of them, or `undefined`.
114
+ *
115
+ * @internal
116
+ */
117
+ function toClientDiscoveryArray(discovery) {
118
+ if (!discovery) return [];
119
+ return Array.isArray(discovery) ? discovery : [discovery];
120
+ }
121
+ /**
122
+ * Merge `discoveryMetadata` from every configured {@link ClientDiscovery}
123
+ * into a single object. Entries are spread in order; later entries override
124
+ * earlier ones on key collisions.
125
+ *
126
+ * @internal
127
+ */
128
+ function mergeDiscoveryMetadata(discovery) {
129
+ return toClientDiscoveryArray(discovery).reduce((acc, d) => ({
130
+ ...acc,
131
+ ...d.discoveryMetadata ?? {}
132
+ }), {});
133
+ }
134
+ /**
103
135
  * Default client secret hasher using SHA-256
104
136
  *
105
137
  * @internal
@@ -292,7 +324,7 @@ async function extractClientCredentials(ctx, opts, expectedAudience) {
292
324
  error_description: "client_assertion cannot be combined with client_secret or Basic auth",
293
325
  error: "invalid_client"
294
326
  });
295
- const { verifyClientAssertion: verify } = await import("./client-assertion-DZqo-L5j.mjs").then((n) => n.t);
327
+ const { verifyClientAssertion: verify } = await import("./client-assertion-CderPEmR.mjs").then((n) => n.t);
296
328
  const result = await verify(ctx, opts, body.client_assertion, body.client_assertion_type, body.client_id, expectedAudience);
297
329
  return {
298
330
  method: "private_key_jwt",
@@ -414,4 +446,4 @@ function isPKCERequired(client, requestedScopes) {
414
446
  return false;
415
447
  }
416
448
  //#endregion
417
- export { storeToken as _, getClient as a, getStoredToken as c, parseClientMetadata as d, parsePrompt as f, storeClientSecret as g, searchParamsToQuery as h, extractClientCredentials as i, isPKCERequired as l, resolveSubjectIdentifier as m, deleteFromPrompt as n, getJwtPlugin as o, resolveSessionAuthTime as p, destructureCredentials as r, getOAuthProviderPlugin as s, decryptStoredClientSecret as t, normalizeTimestampValue as u, validateClientCredentials as v, verifyOAuthQueryParams as y };
449
+ export { storeClientSecret as _, getClient as a, validateClientCredentials as b, getStoredToken as c, normalizeTimestampValue as d, parseClientMetadata as f, searchParamsToQuery as g, resolveSubjectIdentifier as h, extractClientCredentials as i, isPKCERequired as l, resolveSessionAuthTime as m, deleteFromPrompt as n, getJwtPlugin as o, parsePrompt as p, destructureCredentials as r, getOAuthProviderPlugin as s, decryptStoredClientSecret as t, mergeDiscoveryMetadata as u, storeToken as v, verifyOAuthQueryParams as x, toClientDiscoveryArray as y };
@@ -1,5 +1,5 @@
1
1
  //#endregion
2
2
  //#region src/version.ts
3
- const PACKAGE_VERSION = "1.7.0-beta.0";
3
+ const PACKAGE_VERSION = "1.7.0-beta.2";
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.0",
3
+ "version": "1.7.0-beta.2",
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.7.0-beta.0",
68
- "better-auth": "1.7.0-beta.0"
67
+ "@better-auth/core": "1.7.0-beta.2",
68
+ "better-auth": "1.7.0-beta.2"
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.7.0-beta.0",
75
- "better-auth": "^1.7.0-beta.0"
74
+ "@better-auth/core": "^1.7.0-beta.2",
75
+ "better-auth": "^1.7.0-beta.2"
76
76
  },
77
77
  "scripts": {
78
78
  "build": "tsdown",