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

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,21 +1,14 @@
1
- import { a as getClient } from "./utils-CIbcUsZ5.mjs";
1
+ import { t as __exportAll } from "./rolldown-runtime-wcPFST8Q.mjs";
2
+ import { a as getClient } from "./utils-DO8lmoDw.mjs";
3
+ import { isPublicRoutableHost } from "@better-auth/core/utils/host";
2
4
  import { APIError } from "better-call";
3
- import { ASSERTION_SIGNING_ALGORITHMS, CLIENT_ASSERTION_TYPE } from "@better-auth/core/oauth2";
5
+ import { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS } from "@better-auth/core/oauth2";
6
+ import { base64Url } from "@better-auth/utils/base64";
7
+ import { createHash } from "@better-auth/utils/hash";
4
8
  import { createLocalJWKSet, decodeJwt, decodeProtectedHeader, jwtVerify } from "jose";
5
- //#region \0rolldown/runtime.js
6
- var __defProp = Object.defineProperty;
7
- var __exportAll = (all, no_symbols) => {
8
- let target = {};
9
- for (var name in all) __defProp(target, name, {
10
- get: all[name],
11
- enumerable: true
12
- });
13
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
14
- return target;
15
- };
16
- //#endregion
17
9
  //#region src/utils/client-assertion.ts
18
10
  var client_assertion_exports = /* @__PURE__ */ __exportAll({
11
+ consumeClientAssertion: () => consumeClientAssertion,
19
12
  isPrivateHostname: () => isPrivateHostname,
20
13
  verifyClientAssertion: () => verifyClientAssertion
21
14
  });
@@ -33,36 +26,24 @@ function setJwksCache(uri, jwks, fetchedAt) {
33
26
  if (oldest !== void 0) jwksCache.delete(oldest);
34
27
  }
35
28
  }
36
- const ALGORITHMS_LIST = [...ASSERTION_SIGNING_ALGORITHMS];
37
- const pendingAssertionIds = /* @__PURE__ */ new Set();
29
+ const ALGORITHMS_LIST = [...PRIVATE_KEY_JWT_SIGNING_ALGORITHMS];
38
30
  /**
39
- * Block SSRF: reject jwks_uri pointing at private/reserved IP ranges.
40
- * Only HTTPS with public hostnames is allowed.
31
+ * SSRF gate for user-supplied server-side fetch targets (`jwks_uri`,
32
+ * `backchannel_logout_uri`): returns true when the host is NOT publicly
33
+ * routable. That covers loopback, RFC 1918 private, link-local (including AWS
34
+ * IMDS `169.254.169.254`), shared-address-space (carrier-grade NAT),
35
+ * IPv4-mapped IPv6, 6to4/NAT64/Teredo tunnels, every other RFC 6890
36
+ * special-purpose range, and cloud-metadata FQDNs.
37
+ *
38
+ * Delegates to the audited single source of truth so this check cannot drift
39
+ * into the kind of encoding bypass that bespoke regexes invite. This is a
40
+ * syntactic check only: it does not resolve DNS, so a public name that
41
+ * resolves to a private address at fetch time is not caught here.
41
42
  */
42
- function isPrivateIpv4(hostname) {
43
- const parts = hostname.split(".");
44
- if (parts.length !== 4 || parts.some((p) => !/^\d{1,3}$/.test(p))) return false;
45
- const octets = parts.map(Number);
46
- const a = octets[0];
47
- const b = octets[1];
48
- return a === 10 || a === 0 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254 || a === 127;
49
- }
50
43
  function isPrivateHostname(hostname) {
51
- const lower = hostname.toLowerCase();
52
- const host = lower.startsWith("[") && lower.endsWith("]") ? lower.slice(1, -1) : lower;
53
- if (host === "localhost" || host === "::1") return true;
54
- if (isPrivateIpv4(host)) return true;
55
- if (host.includes(":")) {
56
- const v4MappedMatch = host.match(/^(?:0{0,4}:){0,4}:?(?:0{0,4}:)?ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/);
57
- if (v4MappedMatch && isPrivateIpv4(v4MappedMatch[1])) return true;
58
- const isLinkLocal = host.startsWith("fe8") || host.startsWith("fe9") || host.startsWith("fea") || host.startsWith("feb");
59
- const isUniqueLocal = host.startsWith("fc") || host.startsWith("fd");
60
- if (isLinkLocal || isUniqueLocal) return true;
61
- }
62
- if (host === "metadata.google.internal") return true;
63
- return false;
44
+ return !isPublicRoutableHost(hostname);
64
45
  }
65
- function validateJwksUri(ctx, jwksUri) {
46
+ function validateJwksUri(ctx, jwksUri, clientIdUrlOrigin) {
66
47
  const parsed = new URL(jwksUri);
67
48
  if (parsed.protocol !== "https:") throw new APIError("BAD_REQUEST", {
68
49
  error_description: "jwks_uri must use HTTPS",
@@ -72,11 +53,20 @@ function validateJwksUri(ctx, jwksUri) {
72
53
  error_description: "jwks_uri must not point to a private or reserved address",
73
54
  error: "invalid_client"
74
55
  });
56
+ if (clientIdUrlOrigin && parsed.origin === clientIdUrlOrigin) return;
75
57
  if (!ctx.context.isTrustedOrigin(parsed.href)) throw new APIError("BAD_REQUEST", {
76
58
  error_description: "client jwks_uri is not trusted",
77
59
  error: "invalid_client"
78
60
  });
79
61
  }
62
+ function urlClientIdOrigin(clientId) {
63
+ if (!clientId.startsWith("https://") && !clientId.startsWith("http://")) return;
64
+ try {
65
+ return new URL(clientId).origin;
66
+ } catch {
67
+ return;
68
+ }
69
+ }
80
70
  async function fetchJwksFromUri(jwksUri) {
81
71
  const controller = new AbortController();
82
72
  const timeout = setTimeout(() => controller.abort(), JWKS_FETCH_TIMEOUT_MS);
@@ -100,7 +90,7 @@ async function fetchClientJwks(ctx, client) {
100
90
  error_description: "client has no JWKS configured",
101
91
  error: "invalid_client"
102
92
  });
103
- validateJwksUri(ctx, client.jwksUri);
93
+ validateJwksUri(ctx, client.jwksUri, urlClientIdOrigin(client.clientId));
104
94
  const now = Date.now();
105
95
  const cached = jwksCache.get(client.jwksUri);
106
96
  if (cached && now - cached.fetchedAt < JWKS_CACHE_TTL_MS) return cached.jwks;
@@ -132,6 +122,84 @@ async function refetchClientJwks(client) {
132
122
  }
133
123
  }
134
124
  /**
125
+ * Enforces the assertion-hygiene claims every client-assertion authentication
126
+ * method must check, independent of how the signature is verified or where the
127
+ * verification keys come from:
128
+ * - `aud` MUST include `expectedAudience` (RFC 7523 §3 rule 3),
129
+ * - `exp` MUST be present, unexpired, and at most `assertionMaxLifetime`
130
+ * seconds away (RFC 7523 §3 rule 4),
131
+ * - `iat`, when present, MUST be within `assertionMaxLifetime`,
132
+ * - `jti` MUST be present and single-use; this consumes a replay tombstone keyed
133
+ * by `` `${namespace}:${jti}` ``, inserted under the adapter's primary key so a
134
+ * replay across workers fails atomically.
135
+ *
136
+ * A custom {@link OAuthClientAuthenticationStrategy} should call this after
137
+ * verifying the assertion signature, so an extension method inherits the same
138
+ * replay, lifetime, and audience guarantees as the built-in `private_key_jwt`
139
+ * path, which calls it too.
140
+ *
141
+ * @param params.namespace Scopes the replay tombstone to the method and client,
142
+ * e.g. `` `${method}:${clientId}` ``, so the same `jti` can recur across
143
+ * distinct methods or clients but never within one.
144
+ */
145
+ async function consumeClientAssertion(ctx, opts, params) {
146
+ const { namespace, payload, expectedAudience } = params;
147
+ if (!(Array.isArray(payload.aud) ? payload.aud : payload.aud != null ? [payload.aud] : []).includes(expectedAudience)) throw new APIError("BAD_REQUEST", {
148
+ error_description: "client assertion aud does not match the endpoint",
149
+ error: "invalid_client"
150
+ });
151
+ const maxLifetime = opts.assertionMaxLifetime ?? 300;
152
+ const now = Math.floor(Date.now() / 1e3);
153
+ if (typeof payload.exp !== "number") throw new APIError("BAD_REQUEST", {
154
+ error_description: "client assertion must include exp claim",
155
+ error: "invalid_client"
156
+ });
157
+ if (payload.exp <= now) throw new APIError("BAD_REQUEST", {
158
+ error_description: "client assertion has expired",
159
+ error: "invalid_client"
160
+ });
161
+ if (payload.exp - now > maxLifetime) throw new APIError("BAD_REQUEST", {
162
+ error_description: `client assertion exp is too far in the future (max ${maxLifetime}s)`,
163
+ error: "invalid_client"
164
+ });
165
+ if (typeof payload.iat === "number" && now - payload.iat > maxLifetime) throw new APIError("BAD_REQUEST", {
166
+ error_description: `client assertion iat is too far in the past (max ${maxLifetime}s)`,
167
+ error: "invalid_client"
168
+ });
169
+ if (typeof payload.jti !== "string" || payload.jti.length === 0) throw new APIError("BAD_REQUEST", {
170
+ error_description: "client assertion must include jti claim",
171
+ error: "invalid_client"
172
+ });
173
+ const jtiDigest = await createHash("SHA-256").digest(new TextEncoder().encode(`${namespace}:${payload.jti}`));
174
+ const jtiId = base64Url.encode(new Uint8Array(jtiDigest).slice(0, 24), { padding: false });
175
+ try {
176
+ await ctx.context.adapter.create({
177
+ model: "oauthClientAssertion",
178
+ data: {
179
+ id: jtiId,
180
+ expiresAt: /* @__PURE__ */ new Date(payload.exp * 1e3)
181
+ },
182
+ forceAllowId: true
183
+ });
184
+ } catch (createErr) {
185
+ let alreadyUsed = false;
186
+ try {
187
+ alreadyUsed = Boolean(await ctx.context.adapter.findOne({
188
+ model: "oauthClientAssertion",
189
+ where: [{
190
+ field: "id",
191
+ value: jtiId
192
+ }]
193
+ }));
194
+ } catch {}
195
+ if (alreadyUsed) throw new APIError("BAD_REQUEST", {
196
+ error_description: "client assertion jti has already been used",
197
+ error: "invalid_client"
198
+ });
199
+ throw createErr;
200
+ }
201
+ }
202
+ /**
135
203
  * Verifies a client assertion JWT for `private_key_jwt` authentication.
136
204
  *
137
205
  * Validates: signature, iss=client_id, sub=client_id, aud=token_endpoint,
@@ -151,7 +219,7 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
151
219
  error: "invalid_client"
152
220
  });
153
221
  }
154
- if (!header.alg || !ASSERTION_SIGNING_ALGORITHMS.includes(header.alg)) throw new APIError("BAD_REQUEST", {
222
+ if (!header.alg || !ALGORITHMS_LIST.includes(header.alg)) throw new APIError("BAD_REQUEST", {
155
223
  error_description: `unsupported assertion signing algorithm: ${header.alg}`,
156
224
  error: "invalid_client"
157
225
  });
@@ -188,7 +256,6 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
188
256
  });
189
257
  const jwks = await fetchClientJwks(ctx, client);
190
258
  const audience = expectedAudience ?? `${ctx.context.baseURL}/oauth2/token`;
191
- const maxLifetime = opts.assertionMaxLifetime ?? 300;
192
259
  const verifyOpts = {
193
260
  issuer: clientId,
194
261
  subject: clientId,
@@ -218,55 +285,12 @@ async function verifyClientAssertion(ctx, opts, clientAssertion, clientAssertion
218
285
  error: "invalid_client"
219
286
  });
220
287
  }
221
- const now = Math.floor(Date.now() / 1e3);
222
- if (typeof payload.exp !== "number") throw new APIError("BAD_REQUEST", {
223
- error_description: "client assertion must include exp claim",
224
- error: "invalid_client"
288
+ await consumeClientAssertion(ctx, opts, {
289
+ namespace: `private_key_jwt:${clientId}`,
290
+ payload,
291
+ expectedAudience: audience
225
292
  });
226
- if (payload.exp - now > maxLifetime) throw new APIError("BAD_REQUEST", {
227
- error_description: `client assertion exp is too far in the future (max ${maxLifetime}s)`,
228
- error: "invalid_client"
229
- });
230
- if (typeof payload.iat === "number" && now - payload.iat > maxLifetime) throw new APIError("BAD_REQUEST", {
231
- error_description: `client assertion iat is too far in the past (max ${maxLifetime}s)`,
232
- error: "invalid_client"
233
- });
234
- if (!payload.jti) throw new APIError("BAD_REQUEST", {
235
- error_description: "client assertion must include jti claim",
236
- error: "invalid_client"
237
- });
238
- const jtiIdentifier = `private_key_jwt:${clientId}:${payload.jti}`;
239
- if (pendingAssertionIds.has(jtiIdentifier)) throw new APIError("BAD_REQUEST", {
240
- error_description: "client assertion jti has already been used",
241
- error: "invalid_client"
242
- });
243
- pendingAssertionIds.add(jtiIdentifier);
244
- try {
245
- if (await ctx.context.internalAdapter.findVerificationValue(jtiIdentifier)) throw new APIError("BAD_REQUEST", {
246
- error_description: "client assertion jti has already been used",
247
- error: "invalid_client"
248
- });
249
- const jtiExpiry = /* @__PURE__ */ new Date(payload.exp * 1e3);
250
- try {
251
- await ctx.context.internalAdapter.createVerificationValue({
252
- identifier: jtiIdentifier,
253
- value: clientId,
254
- expiresAt: jtiExpiry
255
- });
256
- } catch (createErr) {
257
- if (await ctx.context.internalAdapter.findVerificationValue(jtiIdentifier)) throw new APIError("BAD_REQUEST", {
258
- error_description: "client assertion jti has already been used",
259
- error: "invalid_client"
260
- });
261
- throw createErr;
262
- }
263
- } finally {
264
- pendingAssertionIds.delete(jtiIdentifier);
265
- }
266
- return {
267
- clientId,
268
- client
269
- };
293
+ return { clientId };
270
294
  }
271
295
  //#endregion
272
- export { isPrivateHostname as n, client_assertion_exports as t };
296
+ export { consumeClientAssertion as n, isPrivateHostname as r, client_assertion_exports as t };
@@ -1,9 +1,17 @@
1
- import { a as ResourceServerMetadata } from "./oauth-C8aTlaAC.mjs";
1
+ import { c as ResourceServerMetadata } from "./oauth-ScTJEcFV.mjs";
2
+ import { ResourceRequestInput, VerifyAccessTokenRequestOptions } from "better-auth/oauth2";
2
3
  import { JWTPayload, JWTVerifyOptions } from "jose";
3
- import { Auth } from "better-auth/types";
4
+ import { BetterAuthOptions } from "better-auth/types";
4
5
 
5
6
  //#region src/client-resource.d.ts
6
- declare const oauthProviderResourceClient: <T extends Auth | undefined>(auth?: T) => {
7
+ type ResourceClientAuth = {
8
+ options: {
9
+ baseURL?: BetterAuthOptions["baseURL"];
10
+ basePath?: BetterAuthOptions["basePath"];
11
+ };
12
+ $context: Promise<unknown>;
13
+ };
14
+ declare const oauthProviderResourceClient: <T extends ResourceClientAuth | undefined = undefined>(auth?: T) => {
7
15
  id: "oauth-provider-resource-client";
8
16
  version: string;
9
17
  getActions(): {
@@ -15,7 +23,14 @@ declare const oauthProviderResourceClient: <T extends Auth | undefined>(auth?: T
15
23
  *
16
24
  * The optional auth parameter can fill known values automatically.
17
25
  */
18
- verifyAccessToken: VerifyAccessTokenOutput<T>;
26
+ verifyBearerToken: VerifyAccessTokenOutput<T>;
27
+ /**
28
+ * Performs verification of a protected-resource request. Use this for
29
+ * new resource-server integrations so sender-constrained DPoP access
30
+ * tokens are enforced with the request method, URL, Authorization
31
+ * scheme, DPoP proof, `ath`, and `cnf.jkt` binding.
32
+ */
33
+ verifyAccessTokenRequest: VerifyAccessTokenRequestOutput<T>;
19
34
  /**
20
35
  * An authorization server does not typically publish
21
36
  * the `/.well-known/oauth-protected-resource` themselves.
@@ -42,8 +57,23 @@ interface VerifyAccessTokenRemote {
42
57
  * is also still active.
43
58
  */
44
59
  force?: boolean;
60
+ /**
61
+ * Accept introspection responses that omit the `aud` claim even when a
62
+ * required `audience` is configured in `verifyOptions`.
63
+ *
64
+ * By default verification fails closed: if you configure an `audience` and
65
+ * the introspection response has no `aud` (or a mismatching one), the token
66
+ * is rejected. Some authorization servers legitimately omit `aud` from
67
+ * introspection responses (it is OPTIONAL per RFC 7662 §2.2); only enable
68
+ * this if you trust the issuer to bind the token to this resource through
69
+ * another mechanism, as it skips the audience check in that case.
70
+ *
71
+ * @default false
72
+ */
73
+ allowMissingAudience?: boolean;
45
74
  }
46
- type VerifyAccessTokenOutput<T> = T extends Auth ? (token: string | undefined, opts?: VerifyAccessTokenAuthOpts) => Promise<JWTPayload> : (token: string | undefined, opts: VerifyAccessTokenNoAuthOpts) => Promise<JWTPayload>;
75
+ type VerifyAccessTokenOutput<T> = T extends undefined ? (token: string | undefined, opts: VerifyAccessTokenNoAuthOpts) => Promise<JWTPayload> : (token: string | undefined, opts?: VerifyAccessTokenAuthOpts) => Promise<JWTPayload>;
76
+ type VerifyAccessTokenRequestOutput<T> = T extends undefined ? (request: Request | ResourceRequestInput, opts: VerifyAccessTokenRequestNoAuthOpts) => Promise<JWTPayload> : (request: Request | ResourceRequestInput, opts?: VerifyAccessTokenRequestAuthOpts) => Promise<JWTPayload>;
47
77
  type VerifyAccessTokenAuthOpts = {
48
78
  verifyOptions?: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience">>;
49
79
  scopes?: string[];
@@ -51,6 +81,9 @@ type VerifyAccessTokenAuthOpts = {
51
81
  remoteVerify?: VerifyAccessTokenRemote; /** Maps non-url (ie urn, client) resources to resource_metadata */
52
82
  resourceMetadataMappings?: Record<string, string>;
53
83
  };
84
+ type VerifyAccessTokenRequestAuthOpts = VerifyAccessTokenAuthOpts & {
85
+ dpop?: VerifyAccessTokenRequestOptions["dpop"];
86
+ };
54
87
  type VerifyAccessTokenNoAuthOpts = {
55
88
  verifyOptions: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience" | "issuer">>;
56
89
  scopes?: string[];
@@ -64,12 +97,15 @@ type VerifyAccessTokenNoAuthOpts = {
64
97
  remoteVerify: VerifyAccessTokenRemote; /** Maps non-url (ie urn, client) resources to resource_metadata */
65
98
  resourceMetadataMappings?: Record<string, string>;
66
99
  };
67
- type ProtectedResourceMetadataOutput<T> = T extends Auth ? (overrides?: Partial<ResourceServerMetadata>, opts?: {
100
+ type VerifyAccessTokenRequestNoAuthOpts = VerifyAccessTokenNoAuthOpts & {
101
+ dpop?: VerifyAccessTokenRequestOptions["dpop"];
102
+ };
103
+ type ProtectedResourceMetadataOutput<T> = T extends undefined ? (overrides: ResourceServerMetadata, opts?: {
68
104
  silenceWarnings?: {
69
105
  oidcScopes?: boolean;
70
106
  };
71
107
  externalScopes?: string[];
72
- }) => Promise<ResourceServerMetadata> : (overrides: ResourceServerMetadata, opts?: {
108
+ }) => Promise<ResourceServerMetadata> : (overrides?: Partial<ResourceServerMetadata>, opts?: {
73
109
  silenceWarnings?: {
74
110
  oidcScopes?: boolean;
75
111
  };
@@ -1,10 +1,10 @@
1
- import { t as handleMcpErrors } from "./mcp-CYnz-MXn.mjs";
2
- import { o as getJwtPlugin, s as getOAuthProviderPlugin } from "./utils-CIbcUsZ5.mjs";
3
- import { t as PACKAGE_VERSION } from "./version-BGWhjYBb.mjs";
4
- import { verifyAccessToken } from "better-auth/oauth2";
1
+ import { o as getJwtPlugin, s as getOAuthProviderPlugin } from "./utils-DO8lmoDw.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-a5HhJg8A.mjs";
3
+ import { t as raiseResourceServerChallenge } from "./resource-challenge-B-cqv4ur.mjs";
5
4
  import { APIError } from "better-call";
6
5
  import { logger } from "@better-auth/core/env";
7
6
  import { BetterAuthError } from "@better-auth/core/error";
7
+ import { DPOP_SIGNING_ALGORITHMS, requestToResourceInput, verifyAccessTokenRequest, verifyBearerToken } from "better-auth/oauth2";
8
8
  //#region src/client-resource.ts
9
9
  const oauthProviderResourceClient = (auth) => {
10
10
  let oauthProviderPlugin;
@@ -22,36 +22,55 @@ const oauthProviderResourceClient = (auth) => {
22
22
  return (await getJwtPluginOptions())?.jwt?.issuer ?? authServerBaseUrl;
23
23
  };
24
24
  const authServerBasePath = auth?.options.basePath;
25
+ const resolveVerifyAccessTokenOptions = async (opts) => {
26
+ const jwtPluginOptions = await getJwtPluginOptions();
27
+ const audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;
28
+ const issuer = opts?.verifyOptions?.issuer ?? jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
29
+ if (!audience) throw Error("please define opts.verifyOptions.audience");
30
+ if (!issuer) throw Error("please define opts.verifyOptions.issuer");
31
+ const jwksUrl = opts?.jwksUrl ?? jwtPluginOptions?.jwks?.remoteUrl ?? (authServerBaseUrl ? `${authServerBaseUrl + (authServerBasePath ?? "")}${jwtPluginOptions?.jwks?.jwksPath ?? "/jwks"}` : void 0);
32
+ const introspectUrl = opts?.remoteVerify?.introspectUrl ?? (authServerBaseUrl ? `${authServerBaseUrl}${authServerBasePath ?? ""}/oauth2/introspect` : void 0);
33
+ return {
34
+ ...opts,
35
+ jwksUrl,
36
+ verifyOptions: {
37
+ ...opts?.verifyOptions,
38
+ audience,
39
+ issuer
40
+ },
41
+ remoteVerify: opts?.remoteVerify && introspectUrl ? {
42
+ ...opts.remoteVerify,
43
+ introspectUrl
44
+ } : void 0
45
+ };
46
+ };
47
+ const toResourceRequestInput = (request) => typeof request.headers?.get === "function" ? requestToResourceInput(request) : request;
25
48
  return {
26
49
  id: "oauth-provider-resource-client",
27
50
  version: PACKAGE_VERSION,
28
51
  getActions() {
29
52
  return {
30
- verifyAccessToken: (async (token, opts) => {
31
- const jwtPluginOptions = await getJwtPluginOptions();
32
- const audience = opts?.verifyOptions?.audience ?? authServerBaseUrl;
33
- const issuer = opts?.verifyOptions?.issuer ?? jwtPluginOptions?.jwt?.issuer ?? authServerBaseUrl;
34
- if (!audience) throw Error("please define opts.verifyOptions.audience");
35
- if (!issuer) throw Error("please define opts.verifyOptions.issuer");
36
- const jwksUrl = opts?.jwksUrl ?? jwtPluginOptions?.jwks?.remoteUrl ?? (authServerBaseUrl ? `${authServerBaseUrl + (authServerBasePath ?? "")}${jwtPluginOptions?.jwks?.jwksPath ?? "/jwks"}` : void 0);
37
- const introspectUrl = opts?.remoteVerify?.introspectUrl ?? (authServerBaseUrl ? `${authServerBaseUrl}${authServerBasePath ?? ""}/oauth2/introspect` : void 0);
53
+ verifyBearerToken: (async (token, opts) => {
54
+ const verifyOptions = await resolveVerifyAccessTokenOptions(opts);
38
55
  try {
39
56
  if (!token?.length) throw new APIError("UNAUTHORIZED", { message: "missing authorization header" });
40
- return await verifyAccessToken(token, {
41
- ...opts,
42
- jwksUrl,
43
- verifyOptions: {
44
- ...opts?.verifyOptions,
45
- audience,
46
- issuer
47
- },
48
- remoteVerify: opts?.remoteVerify && introspectUrl ? {
49
- ...opts.remoteVerify,
50
- introspectUrl
51
- } : void 0
57
+ return await verifyBearerToken(token, verifyOptions);
58
+ } catch (error) {
59
+ raiseResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
60
+ resourceMetadataMappings: opts?.resourceMetadataMappings,
61
+ dpopSigningAlgorithms: DPOP_SIGNING_ALGORITHMS
52
62
  });
63
+ }
64
+ }),
65
+ verifyAccessTokenRequest: (async (request, opts) => {
66
+ const verifyOptions = await resolveVerifyAccessTokenOptions(opts);
67
+ try {
68
+ return await verifyAccessTokenRequest(toResourceRequestInput(request), verifyOptions);
53
69
  } catch (error) {
54
- throw handleMcpErrors(error, audience, { resourceMetadataMappings: opts?.resourceMetadataMappings });
70
+ raiseResourceServerChallenge(error, verifyOptions.verifyOptions.audience, {
71
+ resourceMetadataMappings: opts?.resourceMetadataMappings,
72
+ dpopSigningAlgorithms: opts?.dpop?.signingAlgorithms ?? DPOP_SIGNING_ALGORITHMS
73
+ });
55
74
  }
56
75
  }),
57
76
  getProtectedResourceMetadata: (async (overrides, opts) => {
@@ -78,6 +97,7 @@ const oauthProviderResourceClient = (auth) => {
78
97
  return {
79
98
  resource,
80
99
  authorization_servers: authorizationServer ? [authorizationServer] : void 0,
100
+ dpop_signing_alg_values_supported: [...oauthProviderOptions?.dpop?.signingAlgorithms ?? DPOP_SIGNING_ALGORITHMS],
81
101
  ...overrides
82
102
  };
83
103
  })
package/dist/client.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { n as oauthProvider } from "./oauth-Dh4YXCXY.mjs";
1
+ import { r as oauthProvider } from "./oauth-BrNRbP2A.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,17 +1,7 @@
1
- import { t as PACKAGE_VERSION } from "./version-BGWhjYBb.mjs";
1
+ import { t as buildSignedOAuthQuery } from "./signed-query-Df1MNiSH.mjs";
2
+ import { t as PACKAGE_VERSION } from "./version-a5HhJg8A.mjs";
2
3
  import { safeJSONParse } from "@better-auth/core/utils/json";
3
4
  //#region src/client.ts
4
- function parseSignedQuery(search) {
5
- const params = new URLSearchParams(search);
6
- if (params.has("sig")) {
7
- const signedParams = new URLSearchParams();
8
- for (const [key, value] of params.entries()) {
9
- signedParams.append(key, value);
10
- if (key === "sig") break;
11
- }
12
- return signedParams.toString();
13
- }
14
- }
15
5
  const oauthProviderClient = () => {
16
6
  return {
17
7
  id: "oauth-provider-client",
@@ -26,7 +16,7 @@ const oauthProviderClient = () => {
26
16
  if (body?.oauth_query) return;
27
17
  if (typeof window !== "undefined" && window?.location?.search && !(ctx.method === "GET" || ctx.method === "DELETE")) ctx.body = JSON.stringify({
28
18
  ...body,
29
- oauth_query: parseSignedQuery(window.location.search)
19
+ oauth_query: buildSignedOAuthQuery(window.location.search)
30
20
  });
31
21
  } }
32
22
  }],