@better-auth/oauth-provider 1.7.0-rc.2 → 1.7.0-rc.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 { o as getClient } from "./resource-challenge-2qgK1B-a.mjs";
1
+ import { N as getClientDiscoveries, a as getClient } from "./utils-GbnW6qPl.mjs";
2
2
  import { isPublicRoutableHost } from "@better-auth/core/utils/host";
3
3
  import { APIError } from "better-call";
4
4
  import { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS } from "@better-auth/core/oauth2";
@@ -17,18 +17,105 @@ var __exportAll = (all, no_symbols) => {
17
17
  return target;
18
18
  };
19
19
  //#endregion
20
+ //#region src/client-jwks.ts
21
+ const EC_PRIVATE_KEY_JWT_ALGORITHM_BY_CURVE = {
22
+ "P-256": "ES256",
23
+ "P-384": "ES384",
24
+ "P-521": "ES512"
25
+ };
26
+ const OKP_PRIVATE_KEY_JWT_SIGNING_CURVES = ["Ed25519"];
27
+ const PRIVATE_JWK_MEMBER_NAMES = [
28
+ "d",
29
+ "p",
30
+ "q",
31
+ "dp",
32
+ "dq",
33
+ "qi",
34
+ "oth"
35
+ ];
36
+ function isRecord(value) {
37
+ return typeof value === "object" && value !== null && !Array.isArray(value);
38
+ }
39
+ function hasStringMember(key, memberName) {
40
+ return typeof key[memberName] === "string" && key[memberName].length > 0;
41
+ }
42
+ function isSupportedEcSigningCurve(curve) {
43
+ return typeof curve === "string" && Object.prototype.hasOwnProperty.call(EC_PRIVATE_KEY_JWT_ALGORITHM_BY_CURVE, curve);
44
+ }
45
+ function isSupportedOkpSigningCurve(curve) {
46
+ return OKP_PRIVATE_KEY_JWT_SIGNING_CURVES.some((signingCurve) => signingCurve === curve);
47
+ }
48
+ function isSupportedPublicJwk(key) {
49
+ switch (key.kty) {
50
+ case "RSA": return hasStringMember(key, "n") && hasStringMember(key, "e");
51
+ case "EC": return isSupportedEcSigningCurve(key.crv) && hasStringMember(key, "x") && hasStringMember(key, "y");
52
+ case "OKP": return isSupportedOkpSigningCurve(key.crv) && hasStringMember(key, "x");
53
+ default: return false;
54
+ }
55
+ }
56
+ function hasSupportedPrivateKeyJwtAlgorithm(key) {
57
+ if (key.alg === void 0) return true;
58
+ if (typeof key.alg !== "string" || !PRIVATE_KEY_JWT_SIGNING_ALGORITHMS.some((algorithm) => algorithm === key.alg)) return false;
59
+ switch (key.kty) {
60
+ case "RSA": return key.alg.startsWith("RS") || key.alg.startsWith("PS");
61
+ case "EC": return isSupportedEcSigningCurve(key.crv) && EC_PRIVATE_KEY_JWT_ALGORITHM_BY_CURVE[key.crv] === key.alg;
62
+ case "OKP": return isSupportedOkpSigningCurve(key.crv) && key.alg === "EdDSA";
63
+ default: return false;
64
+ }
65
+ }
66
+ /**
67
+ * Validates an OAuth client's public asymmetric JWK set.
68
+ *
69
+ * This boundary accepts only the RFC 7517 `{ keys: [...] }` representation.
70
+ * It performs no I/O and returns the validated set for downstream JOSE
71
+ * verification.
72
+ *
73
+ * @internal
74
+ */
75
+ function validatePublicClientJwks(input) {
76
+ const keys = isRecord(input) && Array.isArray(input.keys) ? input.keys : void 0;
77
+ if (!keys?.length) return {
78
+ valid: false,
79
+ error: "jwks must be an RFC 7517 JWK Set object with a non-empty keys array"
80
+ };
81
+ for (const key of keys) {
82
+ if (!isRecord(key)) return {
83
+ valid: false,
84
+ error: "jwks keys must be supported public JWKs with required key parameters"
85
+ };
86
+ if (key.kty === "oct" || "k" in key || PRIVATE_JWK_MEMBER_NAMES.some((name) => name in key)) return {
87
+ valid: false,
88
+ error: "jwks must contain only public asymmetric keys"
89
+ };
90
+ if (!isSupportedPublicJwk(key)) return {
91
+ valid: false,
92
+ error: "jwks keys must be supported public JWKs with required key parameters"
93
+ };
94
+ if (!hasSupportedPrivateKeyJwtAlgorithm(key)) return {
95
+ valid: false,
96
+ error: "jwks key alg must be supported for private_key_jwt and compatible with its key type and signing curve"
97
+ };
98
+ }
99
+ return {
100
+ valid: true,
101
+ jwks: { keys }
102
+ };
103
+ }
104
+ //#endregion
20
105
  //#region src/utils/client-assertion.ts
21
106
  var client_assertion_exports = /* @__PURE__ */ __exportAll({
22
107
  consumeClientAssertion: () => consumeClientAssertion,
23
108
  isPrivateHostname: () => isPrivateHostname,
24
109
  verifyClientAssertion: () => verifyClientAssertion
25
110
  });
26
- const jwksCache = /* @__PURE__ */ new Map();
111
+ const jwksCaches = /* @__PURE__ */ new WeakMap();
27
112
  const JWKS_CACHE_TTL_MS = 300 * 1e3;
28
113
  const JWKS_CACHE_MAX_ENTRIES = 500;
29
114
  const JWKS_FETCH_TIMEOUT_MS = 5e3;
30
- function setJwksCache(uri, jwks, fetchedAt) {
31
- jwksCache.set(uri, {
115
+ const MAX_JWKS_RESPONSE_BYTES = 64 * 1024;
116
+ const JSON_CONTENT_TYPE = /^application\/(?:[-\w.]+\+)?json\s*(?:;|$)/i;
117
+ function setJwksCache(jwksCache, cacheKey, jwks, fetchedAt) {
118
+ jwksCache.set(cacheKey, {
32
119
  jwks,
33
120
  fetchedAt
34
121
  });
@@ -37,6 +124,16 @@ function setJwksCache(uri, jwks, fetchedAt) {
37
124
  if (oldest !== void 0) jwksCache.delete(oldest);
38
125
  }
39
126
  }
127
+ function getJwksCache(opts) {
128
+ const existingCache = jwksCaches.get(opts);
129
+ if (existingCache) return existingCache;
130
+ const cache = /* @__PURE__ */ new Map();
131
+ jwksCaches.set(opts, cache);
132
+ return cache;
133
+ }
134
+ function getJwksCacheKey(client) {
135
+ return `${client.clientDiscoveryId ?? "managed"}:${client.jwksUri ?? ""}`;
136
+ }
40
137
  const ALGORITHMS_LIST = [...PRIVATE_KEY_JWT_SIGNING_ALGORITHMS];
41
138
  /**
42
139
  * SSRF gate for user-supplied server-side fetch targets (`jwks_uri`,
@@ -60,6 +157,14 @@ function validateJwksUri(ctx, jwksUri, clientIdUrlOrigin) {
60
157
  error_description: "jwks_uri must use HTTPS",
61
158
  error: "invalid_client"
62
159
  });
160
+ if (parsed.username || parsed.password) throw new APIError("BAD_REQUEST", {
161
+ error_description: "jwks_uri must not contain credentials",
162
+ error: "invalid_client"
163
+ });
164
+ if (jwksUri.includes("#")) throw new APIError("BAD_REQUEST", {
165
+ error_description: "jwks_uri must not include a fragment component",
166
+ error: "invalid_client"
167
+ });
63
168
  if (isPrivateHostname(parsed.hostname)) throw new APIError("BAD_REQUEST", {
64
169
  error_description: "jwks_uri must not point to a private or reserved address",
65
170
  error: "invalid_client"
@@ -71,63 +176,120 @@ function validateJwksUri(ctx, jwksUri, clientIdUrlOrigin) {
71
176
  });
72
177
  }
73
178
  function urlClientIdOrigin(clientId) {
74
- if (!clientId.startsWith("https://") && !clientId.startsWith("http://")) return;
75
179
  try {
76
- return new URL(clientId).origin;
180
+ const parsed = new URL(clientId);
181
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return;
182
+ return parsed.origin;
77
183
  } catch {
78
184
  return;
79
185
  }
80
186
  }
81
- async function fetchJwksFromUri(jwksUri) {
187
+ async function readBoundedResponseBody(response) {
188
+ const contentLength = response.headers.get("content-length");
189
+ if (contentLength !== null && Number.isFinite(Number(contentLength)) && Number(contentLength) > MAX_JWKS_RESPONSE_BYTES) {
190
+ await response.body?.cancel();
191
+ throw new Error("JWKS response exceeds 64 KiB");
192
+ }
193
+ if (!response.body) return "";
194
+ const reader = response.body.getReader();
195
+ const chunks = [];
196
+ let totalBytes = 0;
197
+ while (true) {
198
+ const { done, value } = await reader.read();
199
+ if (done) break;
200
+ totalBytes += value.byteLength;
201
+ if (totalBytes > MAX_JWKS_RESPONSE_BYTES) {
202
+ await reader.cancel();
203
+ throw new Error("JWKS response exceeds 64 KiB");
204
+ }
205
+ chunks.push(value);
206
+ }
207
+ const bytes = new Uint8Array(totalBytes);
208
+ let offset = 0;
209
+ for (const chunk of chunks) {
210
+ bytes.set(chunk, offset);
211
+ offset += chunk.byteLength;
212
+ }
213
+ return new TextDecoder().decode(bytes);
214
+ }
215
+ async function fetchJwksFromUri(jwksUri, fetchClientMetadataResource = globalThis.fetch) {
82
216
  const controller = new AbortController();
83
217
  const timeout = setTimeout(() => controller.abort(), JWKS_FETCH_TIMEOUT_MS);
84
218
  try {
85
- const response = await fetch(jwksUri, {
219
+ const response = await fetchClientMetadataResource(jwksUri, {
86
220
  signal: controller.signal,
87
221
  headers: { accept: "application/json" },
88
222
  redirect: "error"
89
223
  });
90
- if (!response.ok) throw new Error(`JWKS fetch returned ${response.status}`);
91
- const jwks = await response.json();
92
- if (!jwks.keys || !Array.isArray(jwks.keys)) throw new Error("JWKS response missing keys array");
93
- return jwks;
224
+ if (response.redirected) throw new Error("JWKS fetch redirected");
225
+ if (response.status !== 200) throw new Error(`JWKS fetch returned ${response.status}`);
226
+ const contentType = response.headers.get("content-type");
227
+ if (!contentType || !JSON_CONTENT_TYPE.test(contentType)) throw new Error("JWKS response must use a JSON media type");
228
+ const responseBody = await readBoundedResponseBody(response);
229
+ let parsedBody;
230
+ try {
231
+ parsedBody = JSON.parse(responseBody);
232
+ } catch {
233
+ return { valid: false };
234
+ }
235
+ const result = validatePublicClientJwks(parsedBody);
236
+ if (!result.valid) return { valid: false };
237
+ return {
238
+ valid: true,
239
+ jwks: result.jwks
240
+ };
94
241
  } finally {
95
242
  clearTimeout(timeout);
96
243
  }
97
244
  }
98
- async function fetchClientJwks(ctx, client) {
245
+ function createClientJwksFetchError() {
246
+ return new APIError("BAD_REQUEST", {
247
+ error_description: "failed to fetch client JWKS",
248
+ error: "invalid_client"
249
+ });
250
+ }
251
+ async function fetchClientJwks(ctx, opts, client) {
99
252
  if (client.jwks) return JSON.parse(client.jwks);
100
253
  if (!client.jwksUri) throw new APIError("BAD_REQUEST", {
101
254
  error_description: "client has no JWKS configured",
102
255
  error: "invalid_client"
103
256
  });
104
- validateJwksUri(ctx, client.jwksUri, urlClientIdOrigin(client.clientId));
257
+ const discovery = client.clientDiscoveryId ? getClientDiscoveries(opts).find((candidate) => candidate.id === client.clientDiscoveryId) : void 0;
258
+ if (client.clientDiscoveryId && !discovery?.fetchClientMetadataResource) throw new APIError("BAD_REQUEST", {
259
+ error_description: "client discovery does not provide a metadata resource transport",
260
+ error: "invalid_client"
261
+ });
262
+ validateJwksUri(ctx, client.jwksUri, client.clientDiscoveryId ? urlClientIdOrigin(client.clientId) : void 0);
105
263
  const now = Date.now();
106
- const cached = jwksCache.get(client.jwksUri);
264
+ const cacheKey = getJwksCacheKey(client);
265
+ const jwksCache = getJwksCache(opts);
266
+ const cached = jwksCache.get(cacheKey);
107
267
  if (cached && now - cached.fetchedAt < JWKS_CACHE_TTL_MS) return cached.jwks;
268
+ let result;
108
269
  try {
109
- const jwks = await fetchJwksFromUri(client.jwksUri);
110
- setJwksCache(client.jwksUri, jwks, now);
111
- return jwks;
270
+ result = await fetchJwksFromUri(client.jwksUri, discovery?.fetchClientMetadataResource);
112
271
  } catch {
113
272
  const staleLimitMs = JWKS_CACHE_TTL_MS * 2;
114
273
  if (cached && now - cached.fetchedAt < staleLimitMs) return cached.jwks;
115
- throw new APIError("BAD_REQUEST", {
116
- error_description: "failed to fetch client JWKS",
117
- error: "invalid_client"
118
- });
274
+ throw createClientJwksFetchError();
119
275
  }
276
+ if (!result.valid) throw createClientJwksFetchError();
277
+ setJwksCache(jwksCache, cacheKey, result.jwks, now);
278
+ return result.jwks;
120
279
  }
121
280
  /**
122
281
  * Refetch JWKS from jwks_uri when signature verification fails with cached keys.
123
282
  * Handles key rotation: the client may have published a new key that isn't in our cache yet.
124
283
  */
125
- async function refetchClientJwks(client) {
284
+ async function refetchClientJwks(opts, client) {
126
285
  if (!client.jwksUri) return null;
286
+ const discovery = client.clientDiscoveryId ? getClientDiscoveries(opts).find((candidate) => candidate.id === client.clientDiscoveryId) : void 0;
287
+ if (client.clientDiscoveryId && !discovery?.fetchClientMetadataResource) return null;
127
288
  try {
128
- const jwks = await fetchJwksFromUri(client.jwksUri);
129
- setJwksCache(client.jwksUri, jwks, Date.now());
130
- return jwks;
289
+ const result = await fetchJwksFromUri(client.jwksUri, discovery?.fetchClientMetadataResource);
290
+ if (!result.valid) return null;
291
+ setJwksCache(getJwksCache(opts), getJwksCacheKey(client), result.jwks, Date.now());
292
+ return result.jwks;
131
293
  } catch {
132
294
  return null;
133
295
  }
@@ -265,7 +427,7 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
265
427
  error_description: "client is not registered for private_key_jwt authentication",
266
428
  error: "invalid_client"
267
429
  });
268
- const jwks = await fetchClientJwks(ctx, client);
430
+ const jwks = await fetchClientJwks(ctx, opts, client);
269
431
  const audience = expectedAudience ?? `${ctx.context.baseURL}/oauth2/token`;
270
432
  const verifyOpts = {
271
433
  issuer: clientId,
@@ -278,7 +440,7 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
278
440
  ({payload} = await jwtVerify(clientAssertion, createLocalJWKSet(jwks), verifyOpts));
279
441
  } catch (verifyErr) {
280
442
  if (verifyErr instanceof Error && /no matching key|no applicable key/i.test(verifyErr.message)) {
281
- const refreshed = await refetchClientJwks(client);
443
+ const refreshed = await refetchClientJwks(opts, client);
282
444
  if (refreshed) try {
283
445
  ({payload} = await jwtVerify(clientAssertion, createLocalJWKSet(refreshed), verifyOpts));
284
446
  } catch {
@@ -304,4 +466,4 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
304
466
  return { clientId };
305
467
  }
306
468
  //#endregion
307
- export { __exportAll as i, consumeClientAssertion as n, isPrivateHostname as r, client_assertion_exports as t };
469
+ export { __exportAll as a, validatePublicClientJwks as i, consumeClientAssertion as n, isPrivateHostname as r, client_assertion_exports as t };
@@ -1,4 +1,4 @@
1
- import { c as ResourceServerMetadata } from "./oauth-CSzjH7fO.mjs";
1
+ import { c as ResourceServerMetadata } from "./oauth-Bi2PA_d1.mjs";
2
2
  import { ResourceRequestInput, VerifyAccessTokenRequestOptions } from "better-auth/oauth2";
3
3
  import { JWTPayload, JWTVerifyOptions } from "jose";
4
4
  import { BetterAuthOptions } from "better-auth/types";
@@ -72,7 +72,8 @@ type VerifyAccessTokenOutput<T> = T extends undefined ? (token: string | undefin
72
72
  type VerifyAccessTokenRequestOutput<T> = T extends undefined ? (request: Request | ResourceRequestInput, opts: VerifyAccessTokenRequestNoAuthOpts) => Promise<JWTPayload> : (request: Request | ResourceRequestInput, opts?: VerifyAccessTokenRequestAuthOpts) => Promise<JWTPayload>;
73
73
  type VerifyAccessTokenAuthOpts = {
74
74
  verifyOptions?: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience">>;
75
- scopes?: string[];
75
+ requiredScopes?: readonly string[];
76
+ isScopeSatisfied?: VerifyAccessTokenRequestOptions["isScopeSatisfied"];
76
77
  jwksUrl?: string;
77
78
  remoteVerify?: VerifyAccessTokenRemote;
78
79
  /** Maps non-url (ie urn, client) resources to resource_metadata */
@@ -83,14 +84,16 @@ type VerifyAccessTokenRequestAuthOpts = VerifyAccessTokenAuthOpts & {
83
84
  };
84
85
  type VerifyAccessTokenNoAuthOpts = {
85
86
  verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
86
- scopes?: string[];
87
+ requiredScopes?: readonly string[];
88
+ isScopeSatisfied?: VerifyAccessTokenRequestOptions["isScopeSatisfied"];
87
89
  jwksUrl: string;
88
90
  remoteVerify?: VerifyAccessTokenRemote;
89
91
  /** Maps non-url (ie urn, client) resources to resource_metadata */
90
92
  resourceMetadataMappings?: Record<string, string>;
91
93
  } | {
92
94
  verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
93
- scopes?: string[];
95
+ requiredScopes?: readonly string[];
96
+ isScopeSatisfied?: VerifyAccessTokenRequestOptions["isScopeSatisfied"];
94
97
  jwksUrl?: string;
95
98
  remoteVerify: VerifyAccessTokenRemote;
96
99
  /** Maps non-url (ie urn, client) resources to resource_metadata */
@@ -1,5 +1,6 @@
1
- import { c as getOAuthProviderPlugin, s as getJwtPlugin, t as raiseResourceServerChallenge } from "./resource-challenge-2qgK1B-a.mjs";
2
- import { t as PACKAGE_VERSION } from "./version-C0y8aPv-.mjs";
1
+ import { o as getJwtPlugin, s as getOAuthProviderPlugin } from "./utils-GbnW6qPl.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-xoFrTW0m.mjs";
3
+ import { t as createResourceServerChallenge } from "./resource-challenge-CiJTlsEh.mjs";
3
4
  import { APIError } from "better-call";
4
5
  import { logger } from "@better-auth/core/env";
5
6
  import { BetterAuthError } from "@better-auth/core/error";
@@ -63,10 +64,12 @@ const oauthProviderResourceClient = (auth) => {
63
64
  if (!token?.length) throw new APIError("UNAUTHORIZED", { message: "missing authorization header" });
64
65
  return await verifyBearerToken(token, verifyOptions);
65
66
  } catch (error) {
66
- raiseResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
67
+ const challenge = createResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
67
68
  resourceMetadataMappings: opts?.resourceMetadataMappings,
68
69
  dpopSigningAlgorithms: DPOP_SIGNING_ALGORITHMS
69
70
  });
71
+ if (challenge) throw challenge;
72
+ throw error;
70
73
  }
71
74
  }),
72
75
  /**
@@ -80,10 +83,12 @@ const oauthProviderResourceClient = (auth) => {
80
83
  try {
81
84
  return await verifyAccessTokenRequest(toResourceRequestInput(request), verifyOptions);
82
85
  } catch (error) {
83
- raiseResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
86
+ const challenge = createResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
84
87
  resourceMetadataMappings: opts?.resourceMetadataMappings,
85
88
  dpopSigningAlgorithms: opts?.dpop?.signingAlgorithms ?? DPOP_SIGNING_ALGORITHMS
86
89
  });
90
+ if (challenge) throw challenge;
91
+ throw error;
87
92
  }
88
93
  }),
89
94
  /**
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { r as oauthProvider } from "./oauth-DdSU0qF6.mjs";
1
+ import { r as oauthProvider } from "./oauth-CgWbnA8o.mjs";
2
2
  //#region src/client.d.ts
3
3
  declare const oauthProviderClient: () => {
4
4
  id: "oauth-provider-client";
package/dist/client.mjs CHANGED
@@ -1,4 +1,5 @@
1
- import { n as buildSignedOAuthQuery, t as PACKAGE_VERSION } from "./version-C0y8aPv-.mjs";
1
+ import { t as buildSignedOAuthQuery } from "./signed-query-BQAwsV_w.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-xoFrTW0m.mjs";
2
3
  import { safeJSONParse } from "@better-auth/core/utils/json";
3
4
  //#region src/client.ts
4
5
  const oauthProviderClient = () => {
package/dist/index.d.mts CHANGED
@@ -1,8 +1,45 @@
1
- import { A as OAuthProviderExtension, B as StoreTokenType, C as OAuthConsent, D as OAuthOpaqueAccessToken, E as OAuthMetadataExtensionInput, F as OAuthTokenResponse, H as VerificationValue, I as OAuthUserInfoExtensionInput, L as Prompt, M as OAuthResource, N as OAuthResourceInput, O as OAuthOptions, P as OAuthTokenIssueParams, R as SchemaClient, S as OAuthClientResource, T as OAuthExtensionGrantHandlerInput, U as ClientRegistrationRequest, V as StoredAuthorizationQuery, W as ResourceUriSchema, _ as OAuthClaimExtensionInput, a as GrantType, b as OAuthClientAuthenticationResult, c as ResourceServerMetadata, d as ActiveAccessTokenPayload, f as AuthorizePrompt, g as OAuthAuthorizationQuery, h as OAuthAuthenticatedClient, i as Confirmation, j as OAuthRefreshToken, k as OAuthProviderApi, l as TokenEndpointAuthMethod, m as InitialAccessTokenAuthorization, n as AuthServerMetadata, o as OAuthClient, p as ClientDiscovery, r as BearerMethodsSupported, s as OIDCMetadata, t as AuthMethod, u as TokenType, v as OAuthClientAuthenticationInput, w as OAuthExtensionGrantHandler, x as OAuthClientAuthenticationStrategy, y as OAuthClientAuthenticationRequest, z as Scope } from "./oauth-CSzjH7fO.mjs";
2
- import { a as OAuthEndpointErrorResult, c as OAuthFieldErrorCode, i as getIssuer, l as OAuthFieldErrorCodeMap, n as getOAuthProviderState, o as OAuthEndpointRedirectContext, r as oauthProvider, s as OAuthErrorCode, t as DEFAULT_OAUTH_SCOPES, u as OAuthRedirectOnError } from "./oauth-DdSU0qF6.mjs";
3
- import { getSessionFromCtx } from "better-auth/api";
1
+ import { A as OAuthOpaqueAccessToken, B as Prompt, C as OAuthClientAuthenticationStrategy, D as OAuthExtensionGrantHandler, E as OAuthConsent, F as OAuthResource, G as VerificationValue, H as Scope, I as OAuthResourceInput, J as ResourceUriSchema, K as ClientRegistrationRequest, L as OAuthTokenIssueParams, M as OAuthProviderApi, N as OAuthProviderExtension, O as OAuthExtensionGrantHandlerInput, P as OAuthRefreshToken, R as OAuthTokenResponse, S as OAuthClientAuthenticationResult, T as OAuthClientResource, U as StoreTokenType, V as SchemaClient, W as StoredAuthorizationQuery, Y as oauthClientMetadataSchema, _ as OAuthAuthorizationQuery, a as GrantType, b as OAuthClientAuthenticationInput, c as ResourceServerMetadata, d as ActiveAccessTokenPayload, f as AuthorizePrompt, g as OAuthAuthenticatedClient, h as InitialAccessTokenAuthorization, i as Confirmation, j as OAuthOptions, k as OAuthMetadataExtensionInput, l as TokenEndpointAuthMethod, m as ClientMetadataResourceFetch, n as AuthServerMetadata, o as OAuthClient, p as ClientDiscovery, q as OAuthClientMetadata, r as BearerMethodsSupported, s as OIDCMetadata, t as AuthMethod, u as TokenType, v as OAuthClaimExtensionInput, w as OAuthClientRegistrationResponse, x as OAuthClientAuthenticationRequest, y as OAuthClientAdministrativeResponse, z as OAuthUserInfoExtensionInput } from "./oauth-Bi2PA_d1.mjs";
2
+ import { a as OAuthEndpointErrorResult, c as OAuthFieldErrorCode, i as getIssuer, l as OAuthFieldErrorCodeMap, n as getOAuthProviderState, o as OAuthEndpointRedirectContext, r as oauthProvider, s as OAuthErrorCode, t as DEFAULT_OAUTH_SCOPES, u as OAuthRedirectOnError } from "./oauth-CgWbnA8o.mjs";
3
+ import { APIError } from "better-call";
4
4
  import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
5
+ import { BetterAuthPlugin } from "better-auth/types";
5
6
  import { AuthContext, GenericEndpointContext } from "@better-auth/core";
7
+ //#region src/device-code.d.ts
8
+ /**
9
+ * RFC 8628 device authorization grant type. A registered OAuth client polls the
10
+ * token endpoint with this `grant_type` to exchange an approved device code for
11
+ * a first-class OAuth token set.
12
+ */
13
+ declare const DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
14
+ /**
15
+ * Bridges the {@link https://datatracker.ietf.org/doc/html/rfc8628 RFC 8628}
16
+ * device authorization grant into the OAuth Provider. Pair it with the
17
+ * `device-authorization` plugin (which owns the `/device/code` request endpoint,
18
+ * the user verification flow, and the `deviceCode` table) and the
19
+ * `oauthProvider` plugin: this registers a `device_code` token grant on
20
+ * `/oauth2/token` that issues real OAuth tokens for a registered OAuth client,
21
+ * and advertises `device_authorization_endpoint` in discovery metadata.
22
+ *
23
+ * First-party device login (the device-authorization plugin's own
24
+ * `/device/token`, which mints a Better Auth session token) keeps working
25
+ * unchanged. To stop a registered OAuth client's device code from being redeemed
26
+ * there for a session token, a `before` hook rejects `/device/token` requests
27
+ * whose `client_id` resolves to a registered OAuth client, directing them to
28
+ * `/oauth2/token`.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const auth = betterAuth({
33
+ * plugins: [
34
+ * deviceAuthorization(),
35
+ * oauthProvider({ ... }),
36
+ * deviceCodeGrant(),
37
+ * ],
38
+ * });
39
+ * ```
40
+ */
41
+ declare function deviceCodeGrant(): BetterAuthPlugin;
42
+ //#endregion
6
43
  //#region src/extensions.d.ts
7
44
  /**
8
45
  * Registers an {@link OAuthProviderExtension} with the OAuth Provider plugin
@@ -17,8 +54,8 @@ import { AuthContext, GenericEndpointContext } from "@better-auth/core";
17
54
  * twice. It throws if the oauth-provider plugin is not installed, if a grant
18
55
  * type or assertion type is not an absolute URI, if a client authentication
19
56
  * method reuses a built-in name, or if the extension registers a grant type,
20
- * auth method, or assertion type that another extension already registered
21
- * (contributions must be disjoint).
57
+ * auth method, assertion type, or client discovery identifier that another
58
+ * extension already registered (contributions must be disjoint).
22
59
  *
23
60
  * @example
24
61
  * ```ts
@@ -116,39 +153,37 @@ declare const oauthProviderOpenIdConfigMetadata: <Auth extends {
116
153
  headers?: HeadersInit;
117
154
  }) => (request: Request) => Promise<Response>;
118
155
  //#endregion
119
- //#region src/register.d.ts
120
- declare function checkOAuthClient(client: OAuthClient, opts: OAuthOptions<Scope[]>, settings?: {
121
- isRegister?: boolean;
122
- ctx?: GenericEndpointContext;
123
- }): Promise<void>;
124
- /**
125
- * Converts an OAuth 2.0 Dynamic Client Schema to a Database Schema
126
- *
127
- * @param input
128
- * @returns
129
- */
130
- declare function oauthToSchema(input: OAuthClient): SchemaClient<Scope[]>;
131
- //#endregion
132
156
  //#region src/resource-challenge.d.ts
133
157
  /**
134
- * Raise an OAuth resource-server challenge for a failed access-token request.
158
+ * Create an OAuth resource-server challenge for a failed access-token request.
135
159
  *
136
160
  * Missing/invalid bearer credentials are reported with RFC 6750 plus the RFC
137
- * 9728 `resource_metadata` pointer. DPoP-bound-token failures are reported with
138
- * RFC 9449's `DPoP` challenge so clients know which proof algorithms to use.
139
- * Non-URL resources (for example a `urn:` or a client id) resolve their
140
- * metadata URL through `resourceMetadataMappings`.
161
+ * 9728 `resource_metadata` pointer. Insufficient-scope failures (built with
162
+ * `createInsufficientScopeError`) are reported with RFC 6750 §3.1's
163
+ * `insufficient_scope` challenge on a 403 naming the scopes the token lacks, so
164
+ * clients can step up their authorization. DPoP-bound-token failures are
165
+ * reported with RFC 9449's `DPoP` challenge so clients know which proof
166
+ * algorithms to use. Non-URL resources (for example a `urn:` or a client id)
167
+ * resolve their metadata URL through `resourceMetadataMappings`.
141
168
  *
142
- * @internal
169
+ * Every other error returns `undefined`, including a plain `FORBIDDEN`: a
170
+ * permission denial that re-authorizing cannot fix must not be answered with a
171
+ * challenge that sends the user through consent for scopes they already hold.
172
+ *
173
+ * @external
143
174
  */
144
- declare function raiseResourceServerChallenge(error: unknown, resource: string | string[], opts?: {
175
+ declare function createResourceServerChallenge(error: unknown, resource: string | string[], opts?: {
145
176
  /** Maps non-URL (urn, client) resources to their resource_metadata URL. */
146
177
  resourceMetadataMappings?: Record<string, string>;
147
178
  /** DPoP JWS algorithms to advertise in RFC 9449 challenges. */
148
179
  dpopSigningAlgorithms?: readonly string[];
149
- /** Space-delimited scopes to advertise in RFC 6750 bearer challenges. */
150
- scope?: string;
151
- }): never;
180
+ /**
181
+ * Scopes to advertise in RFC 6750 bearer challenges,
182
+ * hinting what an unauthenticated client should request. An
183
+ * insufficient-scope failure advertises the scopes it names instead.
184
+ */
185
+ challengeScopes?: readonly string[];
186
+ }): APIError | undefined;
152
187
  //#endregion
153
188
  //#region src/token.d.ts
154
189
  /**
@@ -195,4 +230,4 @@ declare function consumeClientAssertion(ctx: GenericEndpointContext, opts: OAuth
195
230
  expectedAudience: string;
196
231
  }): Promise<void>;
197
232
  //#endregion
198
- export { type ActiveAccessTokenPayload, type AuthMethod, type AuthServerMetadata, type AuthorizePrompt, type BearerMethodsSupported, type ClientDiscovery, type ClientRegistrationRequest, type Confirmation, DEFAULT_OAUTH_SCOPES, type GrantType, type InitialAccessTokenAuthorization, type OAuthAuthenticatedClient, type OAuthAuthorizationQuery, type OAuthClaimExtensionInput, type OAuthClient, type OAuthClientAuthenticationInput, type OAuthClientAuthenticationRequest, type OAuthClientAuthenticationResult, type OAuthClientAuthenticationStrategy, type OAuthClientResource, type OAuthConsent, type OAuthEndpointErrorResult, type OAuthEndpointRedirectContext, type OAuthErrorCode, type OAuthExtensionGrantHandler, type OAuthExtensionGrantHandlerInput, type OAuthFieldErrorCode, type OAuthFieldErrorCodeMap, type OAuthMetadataExtensionInput, type OAuthOpaqueAccessToken, type OAuthOptions, type OAuthProviderApi, type OAuthProviderExtension, type OAuthRedirectOnError, type OAuthRefreshToken, type OAuthResource, type OAuthResourceInput, type OAuthTokenIssueParams, type OAuthTokenResponse, type OAuthUserInfoExtensionInput, type OIDCMetadata, type Prompt, type ResourceServerMetadata, ResourceUriSchema, type SchemaClient, type Scope, type StoreTokenType, type StoredAuthorizationQuery, type TokenEndpointAuthMethod, type TokenType, type VerificationValue, authServerMetadata, checkOAuthClient, consumeClientAssertion, extendOAuthProvider, getIssuer, getOAuthProviderApi, getOAuthProviderState, metadataResponse, oauthAuthorizationServerMetadata, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oauthToSchema, oidcServerMetadata, raiseResourceServerChallenge };
233
+ export { type ActiveAccessTokenPayload, type AuthMethod, type AuthServerMetadata, type AuthorizePrompt, type BearerMethodsSupported, type ClientDiscovery, type ClientMetadataResourceFetch, type ClientRegistrationRequest, type Confirmation, DEFAULT_OAUTH_SCOPES, DEVICE_CODE_GRANT_TYPE, type GrantType, type InitialAccessTokenAuthorization, type OAuthAuthenticatedClient, type OAuthAuthorizationQuery, type OAuthClaimExtensionInput, type OAuthClient, type OAuthClientAdministrativeResponse, type OAuthClientAuthenticationInput, type OAuthClientAuthenticationRequest, type OAuthClientAuthenticationResult, type OAuthClientAuthenticationStrategy, type OAuthClientMetadata, type OAuthClientRegistrationResponse, type OAuthClientResource, type OAuthConsent, type OAuthEndpointErrorResult, type OAuthEndpointRedirectContext, type OAuthErrorCode, type OAuthExtensionGrantHandler, type OAuthExtensionGrantHandlerInput, type OAuthFieldErrorCode, type OAuthFieldErrorCodeMap, type OAuthMetadataExtensionInput, type OAuthOpaqueAccessToken, type OAuthOptions, type OAuthProviderApi, type OAuthProviderExtension, type OAuthRedirectOnError, type OAuthRefreshToken, type OAuthResource, type OAuthResourceInput, type OAuthTokenIssueParams, type OAuthTokenResponse, type OAuthUserInfoExtensionInput, type OIDCMetadata, type Prompt, type ResourceServerMetadata, ResourceUriSchema, type SchemaClient, type Scope, type StoreTokenType, type StoredAuthorizationQuery, type TokenEndpointAuthMethod, type TokenType, type VerificationValue, authServerMetadata, consumeClientAssertion, createResourceServerChallenge, deviceCodeGrant, extendOAuthProvider, getIssuer, getOAuthProviderApi, getOAuthProviderState, metadataResponse, oauthAuthorizationServerMetadata, oauthClientMetadataSchema, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oidcServerMetadata };