@better-auth/oauth-provider 1.6.3 → 1.7.0-beta.1
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.
- package/dist/client-assertion-CderPEmR.mjs +281 -0
- package/dist/client-resource.d.mts +1 -1
- package/dist/client-resource.mjs +3 -2
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +48 -4
- package/dist/index.mjs +193 -97
- package/dist/mcp-CYnz-MXn.mjs +56 -0
- package/dist/{oauth-DH61OMT6.d.mts → oauth-B_qonG53.d.mts} +119 -3
- package/dist/{oauth-IpUqx-_N.d.mts → oauth-CU79t-eG.d.mts} +104 -12
- package/dist/{utils-B9Pj9EPf.mjs → utils-Cx_XnD9i.mjs} +110 -75
- package/dist/{version-BlcZ64XB.mjs → version-DIwdpXrQ.mjs} +1 -1
- package/package.json +5 -5
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { n as isPrivateHostname } from "./client-assertion-CderPEmR.mjs";
|
|
2
|
+
import { n as mcpHandler } from "./mcp-CYnz-MXn.mjs";
|
|
3
|
+
import { _ as storeClientSecret, a as getClient, b as validateClientCredentials, c as getStoredToken, d as normalizeTimestampValue, f as parseClientMetadata, g as searchParamsToQuery, h as resolveSubjectIdentifier, i as extractClientCredentials, l as isPKCERequired, m as resolveSessionAuthTime, n as deleteFromPrompt, o as getJwtPlugin, p as parsePrompt, r as destructureCredentials, t as decryptStoredClientSecret, u as mergeDiscoveryMetadata, v as storeToken, x as verifyOAuthQueryParams, y as toClientDiscoveryArray } from "./utils-Cx_XnD9i.mjs";
|
|
4
|
+
import { t as PACKAGE_VERSION } from "./version-DIwdpXrQ.mjs";
|
|
3
5
|
import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
|
|
4
6
|
import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
|
|
5
7
|
import { APIError as APIError$1 } from "better-call";
|
|
8
|
+
import { ASSERTION_SIGNING_ALGORITHMS } from "@better-auth/core/oauth2";
|
|
6
9
|
import { isBrowserFetchRequest } from "@better-auth/core/utils/fetch-metadata";
|
|
7
10
|
import { generateRandomString, makeSignature } from "better-auth/crypto";
|
|
8
11
|
import { defineRequestState } from "@better-auth/core/context";
|
|
@@ -11,8 +14,8 @@ import { BetterAuthError } from "@better-auth/core/error";
|
|
|
11
14
|
import { parseSetCookieHeader } from "better-auth/cookies";
|
|
12
15
|
import { mergeSchema } from "better-auth/db";
|
|
13
16
|
import * as z from "zod";
|
|
14
|
-
import { signJWT, toExpJWT } from "better-auth/plugins";
|
|
15
|
-
import { SignJWT, compactVerify, createLocalJWKSet, decodeJwt } from "jose";
|
|
17
|
+
import { resolveSigningKey, signJWT, toExpJWT } from "better-auth/plugins";
|
|
18
|
+
import { SignJWT, base64url, compactVerify, createLocalJWKSet, decodeJwt, decodeProtectedHeader } from "jose";
|
|
16
19
|
//#region src/consent.ts
|
|
17
20
|
async function consentEndpoint(ctx, opts) {
|
|
18
21
|
const _query = (await oAuthState.get())?.query;
|
|
@@ -171,7 +174,7 @@ const oauthAuthorizationQuerySchema = z.object({
|
|
|
171
174
|
request_uri: z.string().optional(),
|
|
172
175
|
redirect_uri: z.string(),
|
|
173
176
|
scope: z.string().optional(),
|
|
174
|
-
state: z.string(),
|
|
177
|
+
state: z.string().optional(),
|
|
175
178
|
client_id: z.string(),
|
|
176
179
|
prompt: z.string().optional(),
|
|
177
180
|
display: z.string().optional(),
|
|
@@ -352,10 +355,23 @@ async function createJwtAccessToken(ctx, opts, user, client, audience, scopes, r
|
|
|
352
355
|
});
|
|
353
356
|
}
|
|
354
357
|
/**
|
|
358
|
+
* Computes an OIDC hash (at_hash, c_hash) per OIDC Core §3.1.3.6.
|
|
359
|
+
* Hashes the token, takes the left half, and base64url-encodes it.
|
|
360
|
+
*/
|
|
361
|
+
async function computeOidcHash(token, signingAlg) {
|
|
362
|
+
let hashAlg;
|
|
363
|
+
if (signingAlg === "EdDSA") hashAlg = "SHA-512";
|
|
364
|
+
else if (signingAlg.endsWith("384")) hashAlg = "SHA-384";
|
|
365
|
+
else if (signingAlg.endsWith("512")) hashAlg = "SHA-512";
|
|
366
|
+
else hashAlg = "SHA-256";
|
|
367
|
+
const digest = new Uint8Array(await crypto.subtle.digest(hashAlg, new TextEncoder().encode(token)));
|
|
368
|
+
return base64url.encode(digest.slice(0, digest.length / 2));
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
355
371
|
* Creates a user id token in code_authorization with scope of 'openid'
|
|
356
372
|
* and hybrid/implicit (not yet implemented) flows
|
|
357
373
|
*/
|
|
358
|
-
async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime) {
|
|
374
|
+
async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime, accessToken) {
|
|
359
375
|
const iat = Math.floor(Date.now() / 1e3);
|
|
360
376
|
const exp = iat + (opts.idTokenExpiresIn ?? 36e3);
|
|
361
377
|
const userClaims = userNormalClaims(user, scopes);
|
|
@@ -368,11 +384,15 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId,
|
|
|
368
384
|
metadata: parseClientMetadata(client.metadata)
|
|
369
385
|
}) : {};
|
|
370
386
|
const jwtPluginOptions = opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context).options;
|
|
387
|
+
const resolvedKey = !opts.disableJwtPlugin && !jwtPluginOptions?.jwt?.sign ? await resolveSigningKey(ctx, jwtPluginOptions) : void 0;
|
|
388
|
+
const signingAlg = opts.disableJwtPlugin ? "HS256" : resolvedKey?.alg ?? jwtPluginOptions?.jwks?.keyPairConfig?.alg;
|
|
389
|
+
const atHash = accessToken ? await computeOidcHash(accessToken, signingAlg) : void 0;
|
|
371
390
|
const payload = {
|
|
372
391
|
...userClaims,
|
|
373
392
|
auth_time: authTimeSec,
|
|
374
393
|
acr,
|
|
375
394
|
...customClaims,
|
|
395
|
+
at_hash: atHash,
|
|
376
396
|
iss: jwtPluginOptions?.jwt?.issuer ?? ctx.context.baseURL,
|
|
377
397
|
sub: resolvedSub,
|
|
378
398
|
aud: client.clientId,
|
|
@@ -382,10 +402,19 @@ async function createIdToken(ctx, opts, user, client, scopes, nonce, sessionId,
|
|
|
382
402
|
sid: client.enableEndSession ? sessionId : void 0
|
|
383
403
|
};
|
|
384
404
|
if (opts.disableJwtPlugin && !client.clientSecret) return;
|
|
385
|
-
|
|
405
|
+
const idToken = opts.disableJwtPlugin ? await new SignJWT(payload).setProtectedHeader({ alg: "HS256" }).sign(new TextEncoder().encode(await decryptStoredClientSecret(ctx, opts.storeClientSecret, client.clientSecret))) : await signJWT(ctx, {
|
|
386
406
|
options: jwtPluginOptions,
|
|
387
|
-
payload
|
|
407
|
+
payload,
|
|
408
|
+
resolvedKey: resolvedKey ?? void 0
|
|
388
409
|
});
|
|
410
|
+
if (idToken && atHash && jwtPluginOptions?.jwt?.sign) {
|
|
411
|
+
const header = decodeProtectedHeader(idToken);
|
|
412
|
+
if (header.alg !== signingAlg) throw new APIError("INTERNAL_SERVER_ERROR", {
|
|
413
|
+
error_description: `ID token signed with "${header.alg}" but at_hash was computed for "${signingAlg}". Ensure jwt.sign uses the algorithm declared in keyPairConfig.alg.`,
|
|
414
|
+
error: "server_error"
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
return idToken;
|
|
389
418
|
}
|
|
390
419
|
/**
|
|
391
420
|
* Encodes a refresh token for a client
|
|
@@ -497,23 +526,20 @@ async function createUserTokens(ctx, opts, params) {
|
|
|
497
526
|
exp: iat + (opts.refreshTokenExpiresIn ?? 2592e3),
|
|
498
527
|
sid: sessionId
|
|
499
528
|
}, existingRefreshToken, authTime) : void 0;
|
|
500
|
-
const [accessToken, refreshToken
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}, existingRefreshToken, authTime) : void 0,
|
|
515
|
-
isIdToken && user ? createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime) : void 0
|
|
516
|
-
]);
|
|
529
|
+
const [accessToken, refreshToken] = await Promise.all([isJwtAccessToken ? createJwtAccessToken(ctx, opts, user, client, audience, scopes, referenceId, {
|
|
530
|
+
iat,
|
|
531
|
+
exp,
|
|
532
|
+
sid: sessionId
|
|
533
|
+
}) : createOpaqueAccessToken(ctx, opts, user, client, scopes, {
|
|
534
|
+
iat,
|
|
535
|
+
exp,
|
|
536
|
+
sid: sessionId
|
|
537
|
+
}, referenceId, earlyRefreshToken?.id), earlyRefreshToken ? earlyRefreshToken : isRefreshToken && user ? createRefreshToken(ctx, opts, user, referenceId, client, scopes, {
|
|
538
|
+
iat,
|
|
539
|
+
exp: iat + (opts.refreshTokenExpiresIn ?? 2592e3),
|
|
540
|
+
sid: sessionId
|
|
541
|
+
}, existingRefreshToken, authTime) : void 0]);
|
|
542
|
+
const idToken = isIdToken ? await createIdToken(ctx, opts, user, client, scopes, nonce, sessionId, authTime, accessToken) : void 0;
|
|
517
543
|
return ctx.json({
|
|
518
544
|
...customFields,
|
|
519
545
|
access_token: accessToken,
|
|
@@ -569,13 +595,8 @@ async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri)
|
|
|
569
595
|
* Obtains new Session Jwt and Refresh Tokens using a code
|
|
570
596
|
*/
|
|
571
597
|
async function handleAuthorizationCodeGrant(ctx, opts) {
|
|
572
|
-
|
|
573
|
-
const
|
|
574
|
-
if (authorization?.startsWith("Basic ")) {
|
|
575
|
-
const res = basicToClientCredentials(authorization);
|
|
576
|
-
client_id = res?.client_id;
|
|
577
|
-
client_secret = res?.client_secret;
|
|
578
|
-
}
|
|
598
|
+
const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/token`));
|
|
599
|
+
const { code, code_verifier, redirect_uri } = ctx.body;
|
|
579
600
|
if (!client_id) throw new APIError("BAD_REQUEST", {
|
|
580
601
|
error_description: "client_id is required",
|
|
581
602
|
error: "invalid_request"
|
|
@@ -590,7 +611,7 @@ async function handleAuthorizationCodeGrant(ctx, opts) {
|
|
|
590
611
|
});
|
|
591
612
|
const isAuthCodeWithSecret = client_id && client_secret;
|
|
592
613
|
const isAuthCodeWithPkce = client_id && code && code_verifier;
|
|
593
|
-
if (!isAuthCodeWithSecret && !isAuthCodeWithPkce) throw new APIError("BAD_REQUEST", {
|
|
614
|
+
if (!isAuthCodeWithSecret && !isAuthCodeWithPkce && !preVerifiedClient) throw new APIError("BAD_REQUEST", {
|
|
594
615
|
error_description: "Either code_verifier or client_secret is required",
|
|
595
616
|
error: "invalid_request"
|
|
596
617
|
});
|
|
@@ -602,14 +623,14 @@ async function handleAuthorizationCodeGrant(ctx, opts) {
|
|
|
602
623
|
error: "invalid_scope"
|
|
603
624
|
});
|
|
604
625
|
/** Verify Client */
|
|
605
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, scopes);
|
|
626
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, scopes, preVerifiedClient);
|
|
606
627
|
if (isPKCERequired(client, (verificationValue.query?.scope)?.split(" ") || [])) {
|
|
607
628
|
if (!isAuthCodeWithPkce) throw new APIError("BAD_REQUEST", {
|
|
608
629
|
error_description: "PKCE is required for this client",
|
|
609
630
|
error: "invalid_request"
|
|
610
631
|
});
|
|
611
|
-
} else if (!(isAuthCodeWithPkce || isAuthCodeWithSecret)) throw new APIError("BAD_REQUEST", {
|
|
612
|
-
error_description: "Either PKCE (code_verifier) or client authentication (client_secret) is required",
|
|
632
|
+
} else if (!(isAuthCodeWithPkce || isAuthCodeWithSecret || preVerifiedClient)) throw new APIError("BAD_REQUEST", {
|
|
633
|
+
error_description: "Either PKCE (code_verifier) or client authentication (client_secret or client_assertion) is required",
|
|
613
634
|
error: "invalid_request"
|
|
614
635
|
});
|
|
615
636
|
/** Check PKCE challenge if verifier is provided */
|
|
@@ -670,22 +691,17 @@ async function handleAuthorizationCodeGrant(ctx, opts) {
|
|
|
670
691
|
* MUST follow https://datatracker.ietf.org/doc/html/rfc6749#section-4.4
|
|
671
692
|
*/
|
|
672
693
|
async function handleClientCredentialsGrant(ctx, opts) {
|
|
673
|
-
|
|
674
|
-
const
|
|
675
|
-
if (authorization?.startsWith("Basic ")) {
|
|
676
|
-
const res = basicToClientCredentials(authorization);
|
|
677
|
-
client_id = res?.client_id;
|
|
678
|
-
client_secret = res?.client_secret;
|
|
679
|
-
}
|
|
694
|
+
const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/token`));
|
|
695
|
+
const { scope } = ctx.body;
|
|
680
696
|
if (!client_id) throw new APIError("BAD_REQUEST", {
|
|
681
697
|
error_description: "Missing required client_id",
|
|
682
698
|
error: "invalid_grant"
|
|
683
699
|
});
|
|
684
|
-
if (!client_secret) throw new APIError("BAD_REQUEST", {
|
|
700
|
+
if (!client_secret && !preVerifiedClient) throw new APIError("BAD_REQUEST", {
|
|
685
701
|
error_description: "Missing a required client_secret",
|
|
686
702
|
error: "invalid_grant"
|
|
687
703
|
});
|
|
688
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret);
|
|
704
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, void 0, preVerifiedClient);
|
|
689
705
|
let requestedScopes = scope?.split(" ");
|
|
690
706
|
if (requestedScopes) {
|
|
691
707
|
const validScopes = new Set(client.scopes ?? opts.scopes);
|
|
@@ -717,13 +733,8 @@ async function handleClientCredentialsGrant(ctx, opts) {
|
|
|
717
733
|
* To add scopes, you must restart the authorize process again.
|
|
718
734
|
*/
|
|
719
735
|
async function handleRefreshTokenGrant(ctx, opts) {
|
|
720
|
-
|
|
721
|
-
const
|
|
722
|
-
if (authorization?.startsWith("Basic ")) {
|
|
723
|
-
const res = basicToClientCredentials(authorization);
|
|
724
|
-
client_id = res?.client_id;
|
|
725
|
-
client_secret = res?.client_secret;
|
|
726
|
-
}
|
|
736
|
+
const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/token`));
|
|
737
|
+
const { refresh_token, scope } = ctx.body;
|
|
727
738
|
if (!client_id) throw new APIError("BAD_REQUEST", {
|
|
728
739
|
error_description: "Missing required client_id",
|
|
729
740
|
error: "invalid_grant"
|
|
@@ -777,7 +788,7 @@ async function handleRefreshTokenGrant(ctx, opts) {
|
|
|
777
788
|
error: "invalid_scope"
|
|
778
789
|
});
|
|
779
790
|
}
|
|
780
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, requestedScopes ?? scopes);
|
|
791
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, requestedScopes ?? scopes, preVerifiedClient);
|
|
781
792
|
const user = await ctx.context.internalAdapter.findUserById(refreshToken.userId);
|
|
782
793
|
if (!user) throw new APIError("BAD_REQUEST", {
|
|
783
794
|
error_description: "user not found",
|
|
@@ -1004,14 +1015,9 @@ async function resolveIntrospectionSub(opts, payload, client) {
|
|
|
1004
1015
|
return payload;
|
|
1005
1016
|
}
|
|
1006
1017
|
async function introspectEndpoint(ctx, opts) {
|
|
1007
|
-
let {
|
|
1008
|
-
const
|
|
1009
|
-
if (
|
|
1010
|
-
const res = basicToClientCredentials(authorization);
|
|
1011
|
-
client_id = res?.client_id;
|
|
1012
|
-
client_secret = res?.client_secret;
|
|
1013
|
-
}
|
|
1014
|
-
if (!client_id || !client_secret) throw new APIError$1("UNAUTHORIZED", {
|
|
1018
|
+
let { token, token_type_hint } = ctx.body;
|
|
1019
|
+
const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/introspect`));
|
|
1020
|
+
if (!client_id || !client_secret && !preVerifiedClient) throw new APIError$1("UNAUTHORIZED", {
|
|
1015
1021
|
error_description: "missing required credentials",
|
|
1016
1022
|
error: "invalid_client"
|
|
1017
1023
|
});
|
|
@@ -1020,7 +1026,7 @@ async function introspectEndpoint(ctx, opts) {
|
|
|
1020
1026
|
error_description: "missing a required token for introspection",
|
|
1021
1027
|
error: "invalid_request"
|
|
1022
1028
|
});
|
|
1023
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret);
|
|
1029
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, void 0, preVerifiedClient);
|
|
1024
1030
|
try {
|
|
1025
1031
|
if (token_type_hint === void 0 || token_type_hint === "access_token") try {
|
|
1026
1032
|
return resolveIntrospectionSub(opts, await validateAccessToken(ctx, opts, token, client.clientId), client);
|
|
@@ -1264,14 +1270,59 @@ async function checkOAuthClient(client, opts, settings) {
|
|
|
1264
1270
|
error: "invalid_client_metadata",
|
|
1265
1271
|
error_description: `pkce is required for registered clients.`
|
|
1266
1272
|
});
|
|
1273
|
+
if (client.token_endpoint_auth_method === "private_key_jwt") {
|
|
1274
|
+
if (client.jwks && client.jwks_uri) throw new APIError("BAD_REQUEST", {
|
|
1275
|
+
error: "invalid_client_metadata",
|
|
1276
|
+
error_description: "jwks and jwks_uri are mutually exclusive"
|
|
1277
|
+
});
|
|
1278
|
+
if (!client.jwks && !client.jwks_uri) throw new APIError("BAD_REQUEST", {
|
|
1279
|
+
error: "invalid_client_metadata",
|
|
1280
|
+
error_description: "private_key_jwt requires either jwks or jwks_uri"
|
|
1281
|
+
});
|
|
1282
|
+
if (client.jwks_uri) try {
|
|
1283
|
+
const uri = new URL(client.jwks_uri);
|
|
1284
|
+
if (uri.protocol !== "https:") throw new APIError("BAD_REQUEST", {
|
|
1285
|
+
error: "invalid_client_metadata",
|
|
1286
|
+
error_description: "jwks_uri must use HTTPS"
|
|
1287
|
+
});
|
|
1288
|
+
if (isPrivateHostname(uri.hostname)) throw new APIError("BAD_REQUEST", {
|
|
1289
|
+
error: "invalid_client_metadata",
|
|
1290
|
+
error_description: "jwks_uri must not point to a private or reserved address"
|
|
1291
|
+
});
|
|
1292
|
+
if (settings?.ctx && !settings.ctx.context.isTrustedOrigin(uri.href)) throw new APIError("BAD_REQUEST", {
|
|
1293
|
+
error: "invalid_client_metadata",
|
|
1294
|
+
error_description: "jwks_uri must belong to a trusted origin"
|
|
1295
|
+
});
|
|
1296
|
+
} catch (e) {
|
|
1297
|
+
if (e instanceof APIError) throw e;
|
|
1298
|
+
throw new APIError("BAD_REQUEST", {
|
|
1299
|
+
error: "invalid_client_metadata",
|
|
1300
|
+
error_description: "jwks_uri must be a valid URL"
|
|
1301
|
+
});
|
|
1302
|
+
}
|
|
1303
|
+
if (client.jwks) {
|
|
1304
|
+
const keys = Array.isArray(client.jwks) ? client.jwks : client.jwks.keys;
|
|
1305
|
+
if (!Array.isArray(keys) || keys.length === 0) throw new APIError("BAD_REQUEST", {
|
|
1306
|
+
error: "invalid_client_metadata",
|
|
1307
|
+
error_description: "jwks must be a non-empty array of JWK objects or a JWKS document {keys:[...]}"
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
} else if (client.jwks || client.jwks_uri) throw new APIError("BAD_REQUEST", {
|
|
1311
|
+
error: "invalid_client_metadata",
|
|
1312
|
+
error_description: "jwks and jwks_uri are only allowed with private_key_jwt authentication"
|
|
1313
|
+
});
|
|
1267
1314
|
}
|
|
1268
1315
|
async function createOAuthClientEndpoint(ctx, opts, settings) {
|
|
1269
1316
|
const body = ctx.body;
|
|
1270
1317
|
const session = await getSessionFromCtx(ctx);
|
|
1271
1318
|
const isPublic = body.token_endpoint_auth_method === "none";
|
|
1272
|
-
|
|
1319
|
+
const isPrivateKeyJwt = body.token_endpoint_auth_method === "private_key_jwt";
|
|
1320
|
+
await checkOAuthClient(ctx.body, opts, {
|
|
1321
|
+
...settings,
|
|
1322
|
+
ctx
|
|
1323
|
+
});
|
|
1273
1324
|
const clientId = opts.generateClientId?.() || generateRandomString(32, "a-z", "A-Z");
|
|
1274
|
-
const clientSecret = isPublic ? void 0 : opts.generateClientSecret?.() || generateRandomString(32, "a-z", "A-Z");
|
|
1325
|
+
const clientSecret = isPublic || isPrivateKeyJwt ? void 0 : opts.generateClientSecret?.() || generateRandomString(32, "a-z", "A-Z");
|
|
1275
1326
|
const storedClientSecret = clientSecret ? await storeClientSecret(ctx, opts, clientSecret) : void 0;
|
|
1276
1327
|
const iat = Math.floor(Date.now() / 1e3);
|
|
1277
1328
|
const referenceId = opts.clientReference ? await opts.clientReference({
|
|
@@ -1281,8 +1332,6 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
|
|
|
1281
1332
|
const schema = oauthToSchema({
|
|
1282
1333
|
...body ?? {},
|
|
1283
1334
|
disabled: void 0,
|
|
1284
|
-
jwks: void 0,
|
|
1285
|
-
jwks_uri: void 0,
|
|
1286
1335
|
client_secret_expires_at: storedClientSecret ? settings.isRegister && opts?.clientRegistrationClientSecretExpiration ? toExpJWT(opts.clientRegistrationClientSecretExpiration, iat) : 0 : void 0,
|
|
1287
1336
|
client_id: clientId,
|
|
1288
1337
|
client_secret: storedClientSecret,
|
|
@@ -1317,7 +1366,7 @@ async function createOAuthClientEndpoint(ctx, opts, settings) {
|
|
|
1317
1366
|
* @returns
|
|
1318
1367
|
*/
|
|
1319
1368
|
function oauthToSchema(input) {
|
|
1320
|
-
const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks:
|
|
1369
|
+
const { client_id: clientId, client_secret: clientSecret, client_secret_expires_at: _expiresAt, scope: _scope, user_id: userId, client_id_issued_at: _createdAt, client_name: name, client_uri: uri, logo_uri: icon, contacts, tos_uri: tos, policy_uri: policy, jwks: inputJwks, jwks_uri: jwksUri, software_id: softwareId, software_version: softwareVersion, software_statement: softwareStatement, redirect_uris: redirectUris, post_logout_redirect_uris: postLogoutRedirectUris, token_endpoint_auth_method: tokenEndpointAuthMethod, grant_types: grantTypes, response_types: responseTypes, public: _public, type, disabled, skip_consent: skipConsent, enable_end_session: enableEndSession, require_pkce: requirePKCE, subject_type: subjectType, reference_id: referenceId, metadata: inputMetadata, ...rest } = input;
|
|
1321
1370
|
const expiresAt = _expiresAt ? /* @__PURE__ */ new Date(_expiresAt * 1e3) : void 0;
|
|
1322
1371
|
const createdAt = _createdAt ? /* @__PURE__ */ new Date(_createdAt * 1e3) : void 0;
|
|
1323
1372
|
const scopes = _scope?.split(" ");
|
|
@@ -1325,6 +1374,7 @@ function oauthToSchema(input) {
|
|
|
1325
1374
|
...rest && Object.keys(rest).length ? rest : {},
|
|
1326
1375
|
...inputMetadata && typeof inputMetadata === "object" ? inputMetadata : {}
|
|
1327
1376
|
};
|
|
1377
|
+
const metadata = Object.keys(metadataObj).length ? JSON.stringify(metadataObj) : void 0;
|
|
1328
1378
|
return {
|
|
1329
1379
|
clientId,
|
|
1330
1380
|
clientSecret,
|
|
@@ -1347,6 +1397,8 @@ function oauthToSchema(input) {
|
|
|
1347
1397
|
tokenEndpointAuthMethod,
|
|
1348
1398
|
grantTypes,
|
|
1349
1399
|
responseTypes,
|
|
1400
|
+
jwks: inputJwks ? JSON.stringify({ keys: Array.isArray(inputJwks) ? inputJwks : inputJwks.keys }) : void 0,
|
|
1401
|
+
jwksUri,
|
|
1350
1402
|
public: _public,
|
|
1351
1403
|
type,
|
|
1352
1404
|
skipConsent,
|
|
@@ -1354,7 +1406,7 @@ function oauthToSchema(input) {
|
|
|
1354
1406
|
requirePKCE,
|
|
1355
1407
|
subjectType,
|
|
1356
1408
|
referenceId,
|
|
1357
|
-
metadata
|
|
1409
|
+
metadata
|
|
1358
1410
|
};
|
|
1359
1411
|
}
|
|
1360
1412
|
/**
|
|
@@ -1364,7 +1416,7 @@ function oauthToSchema(input) {
|
|
|
1364
1416
|
* @returns
|
|
1365
1417
|
*/
|
|
1366
1418
|
function schemaToOAuth(input) {
|
|
1367
|
-
const { clientId, clientSecret, disabled, scopes, userId, createdAt, updatedAt: _updatedAt, expiresAt, name, uri, icon, contacts, tos, policy, softwareId, softwareVersion, softwareStatement, redirectUris, postLogoutRedirectUris, tokenEndpointAuthMethod, grantTypes, responseTypes, public: _public, type, skipConsent, enableEndSession, requirePKCE, subjectType, referenceId, metadata } = input;
|
|
1419
|
+
const { clientId, clientSecret, disabled, scopes, userId, createdAt, updatedAt: _updatedAt, expiresAt, name, uri, icon, contacts, tos, policy, softwareId, softwareVersion, softwareStatement, redirectUris, postLogoutRedirectUris, tokenEndpointAuthMethod, grantTypes, responseTypes, public: _public, type, jwks, jwksUri, skipConsent, enableEndSession, requirePKCE, subjectType, referenceId, metadata } = input;
|
|
1368
1420
|
const _expiresAt = expiresAt ? Math.round(new Date(expiresAt).getTime() / 1e3) : void 0;
|
|
1369
1421
|
const _createdAt = createdAt ? Math.round(new Date(createdAt).getTime() / 1e3) : void 0;
|
|
1370
1422
|
const _scopes = scopes?.join(" ");
|
|
@@ -1382,6 +1434,8 @@ function schemaToOAuth(input) {
|
|
|
1382
1434
|
contacts: contacts ?? void 0,
|
|
1383
1435
|
tos_uri: tos ?? void 0,
|
|
1384
1436
|
policy_uri: policy ?? void 0,
|
|
1437
|
+
jwks: jwks ? JSON.parse(jwks).keys : void 0,
|
|
1438
|
+
jwks_uri: jwksUri ?? void 0,
|
|
1385
1439
|
software_id: softwareId ?? void 0,
|
|
1386
1440
|
software_version: softwareVersion ?? void 0,
|
|
1387
1441
|
software_statement: softwareStatement ?? void 0,
|
|
@@ -1558,7 +1612,14 @@ async function updateClientEndpoint(ctx, opts) {
|
|
|
1558
1612
|
await checkOAuthClient({
|
|
1559
1613
|
...schemaToOAuth(client),
|
|
1560
1614
|
...updates
|
|
1561
|
-
}, opts);
|
|
1615
|
+
}, opts, { ctx });
|
|
1616
|
+
const schemaUpdates = { ...oauthToSchema(updates) };
|
|
1617
|
+
if (updates.token_endpoint_auth_method) if (updates.token_endpoint_auth_method === "private_key_jwt") schemaUpdates.clientSecret = null;
|
|
1618
|
+
else {
|
|
1619
|
+
schemaUpdates.jwks = null;
|
|
1620
|
+
schemaUpdates.jwksUri = null;
|
|
1621
|
+
if (!schemaUpdates.clientSecret) schemaUpdates.clientSecret = await storeClientSecret(ctx, opts, opts.generateClientSecret?.() || generateRandomString(32, "a-z", "A-Z"));
|
|
1622
|
+
}
|
|
1562
1623
|
const updatedClient = await ctx.context.adapter.update({
|
|
1563
1624
|
model: "oauthClient",
|
|
1564
1625
|
where: [{
|
|
@@ -1566,7 +1627,7 @@ async function updateClientEndpoint(ctx, opts) {
|
|
|
1566
1627
|
value: clientId
|
|
1567
1628
|
}],
|
|
1568
1629
|
update: {
|
|
1569
|
-
...
|
|
1630
|
+
...schemaUpdates,
|
|
1570
1631
|
updatedAt: /* @__PURE__ */ new Date(Math.floor(Date.now() / 1e3) * 1e3)
|
|
1571
1632
|
}
|
|
1572
1633
|
});
|
|
@@ -1604,7 +1665,7 @@ async function rotateClientSecretEndpoint(ctx, opts) {
|
|
|
1604
1665
|
if (client.referenceId !== await opts.clientReference(session)) throw new APIError("UNAUTHORIZED");
|
|
1605
1666
|
} else throw new APIError("UNAUTHORIZED");
|
|
1606
1667
|
if (client.public || !client.clientSecret) throw new APIError("BAD_REQUEST", {
|
|
1607
|
-
error_description: "
|
|
1668
|
+
error_description: "secret rotation is only available for clients using client_secret authentication",
|
|
1608
1669
|
error: "invalid_client"
|
|
1609
1670
|
});
|
|
1610
1671
|
const clientSecret = opts.generateClientSecret?.() || generateRandomString(32, "a-z", "A-Z");
|
|
@@ -1649,8 +1710,11 @@ const adminCreateOAuthClient = (opts) => createAuthEndpoint("/admin/oauth2/creat
|
|
|
1649
1710
|
token_endpoint_auth_method: z.enum([
|
|
1650
1711
|
"none",
|
|
1651
1712
|
"client_secret_basic",
|
|
1652
|
-
"client_secret_post"
|
|
1713
|
+
"client_secret_post",
|
|
1714
|
+
"private_key_jwt"
|
|
1653
1715
|
]).default("client_secret_basic").optional(),
|
|
1716
|
+
jwks: z.union([z.array(z.record(z.string(), z.unknown())), z.object({ keys: z.array(z.record(z.string(), z.unknown())) })]).optional(),
|
|
1717
|
+
jwks_uri: z.string().optional(),
|
|
1654
1718
|
grant_types: z.array(z.enum([
|
|
1655
1719
|
"authorization_code",
|
|
1656
1720
|
"client_credentials",
|
|
@@ -1832,8 +1896,11 @@ const createOAuthClient = (opts) => createAuthEndpoint("/oauth2/create-client",
|
|
|
1832
1896
|
token_endpoint_auth_method: z.enum([
|
|
1833
1897
|
"none",
|
|
1834
1898
|
"client_secret_basic",
|
|
1835
|
-
"client_secret_post"
|
|
1899
|
+
"client_secret_post",
|
|
1900
|
+
"private_key_jwt"
|
|
1836
1901
|
]).default("client_secret_basic").optional(),
|
|
1902
|
+
jwks: z.union([z.array(z.record(z.string(), z.unknown())), z.object({ keys: z.array(z.record(z.string(), z.unknown())) })]).optional(),
|
|
1903
|
+
jwks_uri: z.string().optional(),
|
|
1837
1904
|
grant_types: z.array(z.enum([
|
|
1838
1905
|
"authorization_code",
|
|
1839
1906
|
"client_credentials",
|
|
@@ -2391,13 +2458,8 @@ async function revokeAccessToken(ctx, opts, clientId, token) {
|
|
|
2391
2458
|
});
|
|
2392
2459
|
}
|
|
2393
2460
|
async function revokeEndpoint(ctx, opts) {
|
|
2394
|
-
let {
|
|
2395
|
-
const
|
|
2396
|
-
if (authorization?.startsWith("Basic ")) {
|
|
2397
|
-
const res = basicToClientCredentials(authorization);
|
|
2398
|
-
client_id = res?.client_id;
|
|
2399
|
-
client_secret = res?.client_secret;
|
|
2400
|
-
}
|
|
2461
|
+
let { token, token_type_hint } = ctx.body;
|
|
2462
|
+
const { clientId: client_id, clientSecret: client_secret, preVerifiedClient } = destructureCredentials(await extractClientCredentials(ctx, opts, `${ctx.context.baseURL}/oauth2/revoke`));
|
|
2401
2463
|
if (!client_id) throw new APIError$1("UNAUTHORIZED", {
|
|
2402
2464
|
error_description: "missing required credentials",
|
|
2403
2465
|
error: "invalid_client"
|
|
@@ -2407,7 +2469,7 @@ async function revokeEndpoint(ctx, opts) {
|
|
|
2407
2469
|
error_description: "missing a required token for introspection",
|
|
2408
2470
|
error: "invalid_request"
|
|
2409
2471
|
});
|
|
2410
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret);
|
|
2472
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, void 0, preVerifiedClient);
|
|
2411
2473
|
try {
|
|
2412
2474
|
if (token_type_hint === void 0 || token_type_hint === "access_token") try {
|
|
2413
2475
|
return await revokeAccessToken(ctx, opts, client.clientId, token);
|
|
@@ -2542,6 +2604,14 @@ const schema = {
|
|
|
2542
2604
|
type: "string",
|
|
2543
2605
|
required: false
|
|
2544
2606
|
},
|
|
2607
|
+
jwks: {
|
|
2608
|
+
type: "string",
|
|
2609
|
+
required: false
|
|
2610
|
+
},
|
|
2611
|
+
jwksUri: {
|
|
2612
|
+
type: "string",
|
|
2613
|
+
required: false
|
|
2614
|
+
},
|
|
2545
2615
|
grantTypes: {
|
|
2546
2616
|
type: "string[]",
|
|
2547
2617
|
required: false
|
|
@@ -2806,7 +2876,7 @@ const oauthProvider = (options) => {
|
|
|
2806
2876
|
queryParams.delete("sig");
|
|
2807
2877
|
queryParams.delete("exp");
|
|
2808
2878
|
await oAuthState.set({ query: queryParams.toString() });
|
|
2809
|
-
if (ctx.path === "/sign-in/social"
|
|
2879
|
+
if (ctx.path === "/sign-in/social") {
|
|
2810
2880
|
if (ctx.body.additionalData?.query) return;
|
|
2811
2881
|
if (!ctx.body.additionalData) ctx.body.additionalData = {};
|
|
2812
2882
|
ctx.body.additionalData.query = queryParams.toString();
|
|
@@ -2840,12 +2910,15 @@ const oauthProvider = (options) => {
|
|
|
2840
2910
|
metadata: { SERVER_ONLY: true }
|
|
2841
2911
|
}, async (ctx) => {
|
|
2842
2912
|
if (opts.scopes && opts.scopes.includes("openid")) return oidcServerMetadata(ctx, opts);
|
|
2843
|
-
else return
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2913
|
+
else return {
|
|
2914
|
+
...authServerMetadata(ctx, opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context)?.options, {
|
|
2915
|
+
scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes,
|
|
2916
|
+
public_client_supported: opts.allowUnauthenticatedClientRegistration || toClientDiscoveryArray(opts.clientDiscovery).length > 0,
|
|
2917
|
+
grant_types_supported: opts.grantTypes,
|
|
2918
|
+
jwt_disabled: opts.disableJwtPlugin
|
|
2919
|
+
}),
|
|
2920
|
+
...mergeDiscoveryMetadata(opts.clientDiscovery)
|
|
2921
|
+
};
|
|
2849
2922
|
}),
|
|
2850
2923
|
getOpenIdConfig: createAuthEndpoint("/.well-known/openid-configuration", {
|
|
2851
2924
|
method: "GET",
|
|
@@ -3044,6 +3117,8 @@ const oauthProvider = (options) => {
|
|
|
3044
3117
|
]),
|
|
3045
3118
|
client_id: z.string().optional(),
|
|
3046
3119
|
client_secret: z.string().optional(),
|
|
3120
|
+
client_assertion: z.string().optional(),
|
|
3121
|
+
client_assertion_type: z.string().optional(),
|
|
3047
3122
|
code: z.string().optional(),
|
|
3048
3123
|
code_verifier: z.string().optional(),
|
|
3049
3124
|
redirect_uri: SafeUrlSchema.optional(),
|
|
@@ -3168,6 +3243,8 @@ const oauthProvider = (options) => {
|
|
|
3168
3243
|
body: z.object({
|
|
3169
3244
|
client_id: z.string().optional(),
|
|
3170
3245
|
client_secret: z.string().optional(),
|
|
3246
|
+
client_assertion: z.string().optional(),
|
|
3247
|
+
client_assertion_type: z.string().optional(),
|
|
3171
3248
|
token: z.string(),
|
|
3172
3249
|
token_type_hint: z.enum(["access_token", "refresh_token"]).optional()
|
|
3173
3250
|
}),
|
|
@@ -3286,6 +3363,8 @@ const oauthProvider = (options) => {
|
|
|
3286
3363
|
body: z.object({
|
|
3287
3364
|
client_id: z.string().optional(),
|
|
3288
3365
|
client_secret: z.string().optional(),
|
|
3366
|
+
client_assertion: z.string().optional(),
|
|
3367
|
+
client_assertion_type: z.string().optional(),
|
|
3289
3368
|
token: z.string(),
|
|
3290
3369
|
token_type_hint: z.enum(["access_token", "refresh_token"]).optional()
|
|
3291
3370
|
}),
|
|
@@ -3483,8 +3562,11 @@ const oauthProvider = (options) => {
|
|
|
3483
3562
|
token_endpoint_auth_method: z.enum([
|
|
3484
3563
|
"none",
|
|
3485
3564
|
"client_secret_basic",
|
|
3486
|
-
"client_secret_post"
|
|
3565
|
+
"client_secret_post",
|
|
3566
|
+
"private_key_jwt"
|
|
3487
3567
|
]).default("client_secret_basic").optional(),
|
|
3568
|
+
jwks: z.union([z.array(z.record(z.string(), z.unknown())), z.object({ keys: z.array(z.record(z.string(), z.unknown())) })]).optional(),
|
|
3569
|
+
jwks_uri: z.string().optional(),
|
|
3488
3570
|
grant_types: z.array(z.enum([
|
|
3489
3571
|
"authorization_code",
|
|
3490
3572
|
"client_credentials",
|
|
@@ -3589,7 +3671,8 @@ const oauthProvider = (options) => {
|
|
|
3589
3671
|
enum: [
|
|
3590
3672
|
"none",
|
|
3591
3673
|
"client_secret_basic",
|
|
3592
|
-
"client_secret_post"
|
|
3674
|
+
"client_secret_post",
|
|
3675
|
+
"private_key_jwt"
|
|
3593
3676
|
]
|
|
3594
3677
|
},
|
|
3595
3678
|
grant_types: {
|
|
@@ -3974,10 +4057,22 @@ function authServerMetadata(ctx, opts, overrides) {
|
|
|
3974
4057
|
token_endpoint_auth_methods_supported: [
|
|
3975
4058
|
...overrides?.public_client_supported ? ["none"] : [],
|
|
3976
4059
|
"client_secret_basic",
|
|
3977
|
-
"client_secret_post"
|
|
4060
|
+
"client_secret_post",
|
|
4061
|
+
"private_key_jwt"
|
|
3978
4062
|
],
|
|
3979
|
-
|
|
3980
|
-
|
|
4063
|
+
token_endpoint_auth_signing_alg_values_supported: [...ASSERTION_SIGNING_ALGORITHMS],
|
|
4064
|
+
introspection_endpoint_auth_methods_supported: [
|
|
4065
|
+
"client_secret_basic",
|
|
4066
|
+
"client_secret_post",
|
|
4067
|
+
"private_key_jwt"
|
|
4068
|
+
],
|
|
4069
|
+
introspection_endpoint_auth_signing_alg_values_supported: [...ASSERTION_SIGNING_ALGORITHMS],
|
|
4070
|
+
revocation_endpoint_auth_methods_supported: [
|
|
4071
|
+
"client_secret_basic",
|
|
4072
|
+
"client_secret_post",
|
|
4073
|
+
"private_key_jwt"
|
|
4074
|
+
],
|
|
4075
|
+
revocation_endpoint_auth_signing_alg_values_supported: [...ASSERTION_SIGNING_ALGORITHMS],
|
|
3981
4076
|
code_challenge_methods_supported: ["S256"],
|
|
3982
4077
|
authorization_response_iss_parameter_supported: true
|
|
3983
4078
|
};
|
|
@@ -3988,7 +4083,7 @@ function oidcServerMetadata(ctx, opts) {
|
|
|
3988
4083
|
return {
|
|
3989
4084
|
...authServerMetadata(ctx, jwtPluginOptions, {
|
|
3990
4085
|
scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes,
|
|
3991
|
-
public_client_supported: opts.allowUnauthenticatedClientRegistration,
|
|
4086
|
+
public_client_supported: opts.allowUnauthenticatedClientRegistration || toClientDiscoveryArray(opts.clientDiscovery).length > 0,
|
|
3992
4087
|
grant_types_supported: opts.grantTypes,
|
|
3993
4088
|
jwt_disabled: opts.disableJwtPlugin
|
|
3994
4089
|
}),
|
|
@@ -4004,7 +4099,8 @@ function oidcServerMetadata(ctx, opts) {
|
|
|
4004
4099
|
"create",
|
|
4005
4100
|
"select_account",
|
|
4006
4101
|
"none"
|
|
4007
|
-
]
|
|
4102
|
+
],
|
|
4103
|
+
...mergeDiscoveryMetadata(opts.clientDiscovery)
|
|
4008
4104
|
};
|
|
4009
4105
|
}
|
|
4010
4106
|
const METADATA_CACHE_CONTROL = "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400";
|
|
@@ -4050,4 +4146,4 @@ const oauthProviderOpenIdConfigMetadata = (auth, opts) => {
|
|
|
4050
4146
|
};
|
|
4051
4147
|
};
|
|
4052
4148
|
//#endregion
|
|
4053
|
-
export { authServerMetadata, getOAuthProviderState, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oidcServerMetadata };
|
|
4149
|
+
export { authServerMetadata, checkOAuthClient, getOAuthProviderState, mcpHandler, oauthProvider, oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata, oauthToSchema, oidcServerMetadata };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isAPIError } from "better-auth/api";
|
|
2
|
+
import { verifyAccessToken } from "better-auth/oauth2";
|
|
3
|
+
import { APIError as APIError$1 } from "better-call";
|
|
4
|
+
//#region src/mcp.ts
|
|
5
|
+
/**
|
|
6
|
+
* A request middleware handler that checks and responds with
|
|
7
|
+
* a WWW-Authenticate header for unauthenticated responses.
|
|
8
|
+
*
|
|
9
|
+
* @external
|
|
10
|
+
*/
|
|
11
|
+
const mcpHandler = (verifyOptions, handler, opts) => {
|
|
12
|
+
return async (req) => {
|
|
13
|
+
const authorization = req.headers?.get("authorization") ?? void 0;
|
|
14
|
+
const accessToken = authorization?.startsWith("Bearer ") ? authorization.replace("Bearer ", "") : authorization;
|
|
15
|
+
try {
|
|
16
|
+
if (!accessToken?.length) throw new APIError$1("UNAUTHORIZED", { message: "missing authorization header" });
|
|
17
|
+
return handler(req, await verifyAccessToken(accessToken, verifyOptions));
|
|
18
|
+
} catch (error) {
|
|
19
|
+
try {
|
|
20
|
+
handleMcpErrors(error, verifyOptions.verifyOptions.audience, opts);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
if (err instanceof APIError$1) return new Response(err.message, {
|
|
23
|
+
...err,
|
|
24
|
+
status: err.statusCode
|
|
25
|
+
});
|
|
26
|
+
throw new Error(String(err));
|
|
27
|
+
}
|
|
28
|
+
throw new Error(String(error));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The following handles all MCP errors and API errors
|
|
34
|
+
*
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function handleMcpErrors(error, resource, opts) {
|
|
38
|
+
if (isAPIError(error) && error.status === "UNAUTHORIZED") {
|
|
39
|
+
const wwwAuthenticateValue = (Array.isArray(resource) ? resource : [resource]).map((v) => {
|
|
40
|
+
let audiencePath;
|
|
41
|
+
if (URL.canParse?.(v)) {
|
|
42
|
+
const url = new URL(v);
|
|
43
|
+
audiencePath = url.pathname.endsWith("/") ? url.pathname.slice(0, -1) : url.pathname;
|
|
44
|
+
return `Bearer resource_metadata="${url.origin}/.well-known/oauth-protected-resource${audiencePath}"`;
|
|
45
|
+
} else {
|
|
46
|
+
const resourceMetadata = opts?.resourceMetadataMappings?.[v];
|
|
47
|
+
if (!resourceMetadata) throw new APIError$1("INTERNAL_SERVER_ERROR", { message: `missing resource_metadata mapping for ${v}` });
|
|
48
|
+
return `Bearer resource_metadata=${resourceMetadata}`;
|
|
49
|
+
}
|
|
50
|
+
}).join(", ");
|
|
51
|
+
throw new APIError$1("UNAUTHORIZED", { message: error.message }, { "WWW-Authenticate": wwwAuthenticateValue });
|
|
52
|
+
} else if (error instanceof Error) throw error;
|
|
53
|
+
else throw new Error(error);
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { mcpHandler as n, handleMcpErrors as t };
|