@better-auth/oauth-provider 1.6.10 → 1.6.12
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-resource.d.mts +13 -6
- package/dist/client-resource.mjs +2 -2
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +152 -57
- package/dist/{oauth-DBCeGXT3.d.mts → oauth-CUqnBdrR.d.mts} +7 -1
- package/dist/{oauth-CqgT-XaR.d.mts → oauth-D74mBkw6.d.mts} +4 -1
- package/dist/{utils-LAthGy-x.mjs → utils-DoYEeMrg.mjs} +4 -2
- package/dist/{version-Cg-c01N0.mjs → version-DZ0lABPc.mjs} +1 -1
- package/package.json +6 -6
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { s as ResourceServerMetadata } from "./oauth-
|
|
1
|
+
import { s as ResourceServerMetadata } from "./oauth-D74mBkw6.mjs";
|
|
2
2
|
import { JWTPayload, JWTVerifyOptions } from "jose";
|
|
3
|
-
import {
|
|
3
|
+
import { BetterAuthOptions } from "better-auth/types";
|
|
4
4
|
|
|
5
5
|
//#region src/client-resource.d.ts
|
|
6
|
-
|
|
6
|
+
type ResourceClientAuth = {
|
|
7
|
+
options: {
|
|
8
|
+
baseURL?: BetterAuthOptions["baseURL"];
|
|
9
|
+
basePath?: BetterAuthOptions["basePath"];
|
|
10
|
+
};
|
|
11
|
+
$context: Promise<unknown>;
|
|
12
|
+
};
|
|
13
|
+
declare const oauthProviderResourceClient: <T extends ResourceClientAuth | undefined = undefined>(auth?: T) => {
|
|
7
14
|
id: "oauth-provider-resource-client";
|
|
8
15
|
version: string;
|
|
9
16
|
getActions(): {
|
|
@@ -43,7 +50,7 @@ interface VerifyAccessTokenRemote {
|
|
|
43
50
|
*/
|
|
44
51
|
force?: boolean;
|
|
45
52
|
}
|
|
46
|
-
type VerifyAccessTokenOutput<T> = T extends
|
|
53
|
+
type VerifyAccessTokenOutput<T> = T extends undefined ? (token: string | undefined, opts: VerifyAccessTokenNoAuthOpts) => Promise<JWTPayload> : (token: string | undefined, opts?: VerifyAccessTokenAuthOpts) => Promise<JWTPayload>;
|
|
47
54
|
type VerifyAccessTokenAuthOpts = {
|
|
48
55
|
verifyOptions?: JWTVerifyOptions & Required<Pick<JWTVerifyOptions, "audience">>;
|
|
49
56
|
scopes?: string[];
|
|
@@ -64,12 +71,12 @@ type VerifyAccessTokenNoAuthOpts = {
|
|
|
64
71
|
remoteVerify: VerifyAccessTokenRemote; /** Maps non-url (ie urn, client) resources to resource_metadata */
|
|
65
72
|
resourceMetadataMappings?: Record<string, string>;
|
|
66
73
|
};
|
|
67
|
-
type ProtectedResourceMetadataOutput<T> = T extends
|
|
74
|
+
type ProtectedResourceMetadataOutput<T> = T extends undefined ? (overrides: ResourceServerMetadata, opts?: {
|
|
68
75
|
silenceWarnings?: {
|
|
69
76
|
oidcScopes?: boolean;
|
|
70
77
|
};
|
|
71
78
|
externalScopes?: string[];
|
|
72
|
-
}) => Promise<ResourceServerMetadata> : (overrides
|
|
79
|
+
}) => Promise<ResourceServerMetadata> : (overrides?: Partial<ResourceServerMetadata>, opts?: {
|
|
73
80
|
silenceWarnings?: {
|
|
74
81
|
oidcScopes?: boolean;
|
|
75
82
|
};
|
package/dist/client-resource.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as handleMcpErrors, a as getOAuthProviderPlugin, i as getJwtPlugin } from "./utils-
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { S as handleMcpErrors, a as getOAuthProviderPlugin, i as getJwtPlugin } from "./utils-DoYEeMrg.mjs";
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-DZ0lABPc.mjs";
|
|
3
3
|
import { verifyAccessToken } from "better-auth/oauth2";
|
|
4
4
|
import { APIError } from "better-call";
|
|
5
5
|
import { logger } from "@better-auth/core/env";
|
package/dist/client.d.mts
CHANGED
package/dist/client.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { _ as Scope, a as OAuthClient, b as Awaitable, c as TokenEndpointAuthMethod, d as OAuthConsent, f as OAuthOpaqueAccessToken, g as SchemaClient, h as Prompt, i as GrantType, l as AuthorizePrompt, m as OAuthRefreshToken, n as AuthServerMetadata, o as OIDCMetadata, p as OAuthOptions, r as BearerMethodsSupported, s as ResourceServerMetadata, t as AuthMethod, u as OAuthAuthorizationQuery, v as StoreTokenType, y as VerificationValue } from "./oauth-
|
|
2
|
-
import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-
|
|
1
|
+
import { _ as Scope, a as OAuthClient, b as Awaitable, c as TokenEndpointAuthMethod, d as OAuthConsent, f as OAuthOpaqueAccessToken, g as SchemaClient, h as Prompt, i as GrantType, l as AuthorizePrompt, m as OAuthRefreshToken, n as AuthServerMetadata, o as OIDCMetadata, p as OAuthOptions, r as BearerMethodsSupported, s as ResourceServerMetadata, t as AuthMethod, u as OAuthAuthorizationQuery, v as StoreTokenType, y as VerificationValue } from "./oauth-D74mBkw6.mjs";
|
|
2
|
+
import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-CUqnBdrR.mjs";
|
|
3
3
|
import { verifyAccessToken } from "better-auth/oauth2";
|
|
4
4
|
import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
|
|
5
5
|
import { JWTPayload } from "jose";
|
|
@@ -21,6 +21,7 @@ verifyOptions: Parameters<typeof verifyAccessToken>[1], handler: (req: Request,
|
|
|
21
21
|
//#region src/metadata.d.ts
|
|
22
22
|
declare function authServerMetadata(ctx: GenericEndpointContext, opts?: JwtOptions, overrides?: {
|
|
23
23
|
scopes_supported?: AuthServerMetadata["scopes_supported"];
|
|
24
|
+
dynamic_client_registration_supported?: boolean;
|
|
24
25
|
public_client_supported?: boolean;
|
|
25
26
|
grant_types_supported?: GrantType[];
|
|
26
27
|
jwt_disabled?: boolean;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as mcpHandler, _ as signedQueryIssuedAtParam, b as validateClientCredentials, c as isPKCERequired, d as parsePrompt, f as postLoginClearedParam, g as searchParamsToQuery, h as resolveSubjectIdentifier, i as getJwtPlugin, l as normalizeTimestampValue, m as resolveSessionAuthTime, n as decryptStoredClientSecret, o as getSignedQueryIssuedAt, p as removePromptFromQuery, r as getClient, s as getStoredToken, t as basicToClientCredentials, u as parseClientMetadata, v as storeClientSecret, x as verifyOAuthQueryParams, y as storeToken } from "./utils-
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { C as mcpHandler, _ as signedQueryIssuedAtParam, b as validateClientCredentials, c as isPKCERequired, d as parsePrompt, f as postLoginClearedParam, g as searchParamsToQuery, h as resolveSubjectIdentifier, i as getJwtPlugin, l as normalizeTimestampValue, m as resolveSessionAuthTime, n as decryptStoredClientSecret, o as getSignedQueryIssuedAt, p as removePromptFromQuery, r as getClient, s as getStoredToken, t as basicToClientCredentials, u as parseClientMetadata, v as storeClientSecret, x as verifyOAuthQueryParams, y as storeToken } from "./utils-DoYEeMrg.mjs";
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-DZ0lABPc.mjs";
|
|
3
3
|
import { APIError, createAuthEndpoint, createAuthMiddleware, getOAuthState, getSessionFromCtx, sessionMiddleware } from "better-auth/api";
|
|
4
4
|
import { generateCodeChallenge, getJwks, verifyJwsAccessToken } from "better-auth/oauth2";
|
|
5
5
|
import { APIError as APIError$1 } from "better-call";
|
|
@@ -437,33 +437,95 @@ async function createOpaqueAccessToken(ctx, opts, user, client, scopes, payload,
|
|
|
437
437
|
});
|
|
438
438
|
return (opts.prefix?.opaqueAccessToken ?? "") + token;
|
|
439
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Tear down the entire refresh-token family for a (client, user) pair, plus
|
|
442
|
+
* any access tokens that reference those refresh rows, per RFC 9700 §4.14.
|
|
443
|
+
* Access tokens are deleted first so the parent rows' foreign-key children
|
|
444
|
+
* do not block the refresh-row delete.
|
|
445
|
+
*
|
|
446
|
+
* TODO(invalidate-family-race): the two `deleteMany` calls are not atomic
|
|
447
|
+
* with respect to each other. Between them, a concurrent rotation in a
|
|
448
|
+
* different worker can `create` a fresh refresh row (and, immediately after,
|
|
449
|
+
* an access-token row referencing it) for the same (client, user) pair,
|
|
450
|
+
* leaving the family partially rebuilt and the new refresh row orphaned of
|
|
451
|
+
* any deletion. Closing this window requires the same transactional adapter
|
|
452
|
+
* contract tracked under FIXME(strict-family-invalidation) in
|
|
453
|
+
* `createRefreshToken`.
|
|
454
|
+
*
|
|
455
|
+
* @internal
|
|
456
|
+
*/
|
|
457
|
+
async function invalidateRefreshFamily(ctx, clientId, userId) {
|
|
458
|
+
const refreshTokens = await ctx.context.adapter.findMany({
|
|
459
|
+
model: "oauthRefreshToken",
|
|
460
|
+
where: [{
|
|
461
|
+
field: "clientId",
|
|
462
|
+
value: clientId
|
|
463
|
+
}, {
|
|
464
|
+
field: "userId",
|
|
465
|
+
value: userId
|
|
466
|
+
}]
|
|
467
|
+
});
|
|
468
|
+
if (refreshTokens.length) await ctx.context.adapter.deleteMany({
|
|
469
|
+
model: "oauthAccessToken",
|
|
470
|
+
where: [{
|
|
471
|
+
field: "refreshId",
|
|
472
|
+
operator: "in",
|
|
473
|
+
value: refreshTokens.map((r) => r.id)
|
|
474
|
+
}]
|
|
475
|
+
});
|
|
476
|
+
await ctx.context.adapter.deleteMany({
|
|
477
|
+
model: "oauthRefreshToken",
|
|
478
|
+
where: [{
|
|
479
|
+
field: "clientId",
|
|
480
|
+
value: clientId
|
|
481
|
+
}, {
|
|
482
|
+
field: "userId",
|
|
483
|
+
value: userId
|
|
484
|
+
}]
|
|
485
|
+
});
|
|
486
|
+
}
|
|
440
487
|
async function createRefreshToken(ctx, opts, user, referenceId, client, scopes, payload, originalRefresh, authTime) {
|
|
441
488
|
const iat = payload.iat ?? Math.floor(Date.now() / 1e3);
|
|
442
489
|
const exp = payload?.exp ?? iat + (opts.refreshTokenExpiresIn ?? 2592e3);
|
|
443
490
|
const token = opts.generateRefreshToken ? await opts.generateRefreshToken() : generateRandomString(32, "A-Z", "a-z");
|
|
444
491
|
const sessionId = payload?.sid;
|
|
445
|
-
|
|
492
|
+
const newRow = {
|
|
493
|
+
token: await storeToken(opts.storeTokens, token, "refresh_token"),
|
|
494
|
+
clientId: client.clientId,
|
|
495
|
+
sessionId,
|
|
496
|
+
userId: user.id,
|
|
497
|
+
referenceId,
|
|
498
|
+
authTime,
|
|
499
|
+
scopes,
|
|
500
|
+
createdAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
501
|
+
expiresAt: /* @__PURE__ */ new Date(exp * 1e3)
|
|
502
|
+
};
|
|
503
|
+
if (!originalRefresh?.id) return {
|
|
504
|
+
id: (await ctx.context.adapter.create({
|
|
505
|
+
model: "oauthRefreshToken",
|
|
506
|
+
data: newRow
|
|
507
|
+
})).id,
|
|
508
|
+
token: await encodeRefreshToken(opts, token, sessionId)
|
|
509
|
+
};
|
|
510
|
+
if (!await ctx.context.adapter.update({
|
|
446
511
|
model: "oauthRefreshToken",
|
|
447
512
|
where: [{
|
|
448
513
|
field: "id",
|
|
449
514
|
value: originalRefresh.id
|
|
515
|
+
}, {
|
|
516
|
+
field: "revoked",
|
|
517
|
+
operator: "eq",
|
|
518
|
+
value: null
|
|
450
519
|
}],
|
|
451
520
|
update: { revoked: /* @__PURE__ */ new Date(iat * 1e3) }
|
|
521
|
+
})) throw new APIError("BAD_REQUEST", {
|
|
522
|
+
error_description: "invalid refresh token",
|
|
523
|
+
error: "invalid_grant"
|
|
452
524
|
});
|
|
453
525
|
return {
|
|
454
526
|
id: (await ctx.context.adapter.create({
|
|
455
527
|
model: "oauthRefreshToken",
|
|
456
|
-
data:
|
|
457
|
-
token: await storeToken(opts.storeTokens, token, "refresh_token"),
|
|
458
|
-
clientId: client.clientId,
|
|
459
|
-
sessionId,
|
|
460
|
-
userId: user.id,
|
|
461
|
-
referenceId,
|
|
462
|
-
authTime,
|
|
463
|
-
scopes,
|
|
464
|
-
createdAt: /* @__PURE__ */ new Date(iat * 1e3),
|
|
465
|
-
expiresAt: /* @__PURE__ */ new Date(exp * 1e3)
|
|
466
|
-
}
|
|
528
|
+
data: newRow
|
|
467
529
|
})).id,
|
|
468
530
|
token: await encodeRefreshToken(opts, token, sessionId)
|
|
469
531
|
};
|
|
@@ -541,15 +603,10 @@ async function createUserTokens(ctx, opts, params) {
|
|
|
541
603
|
}
|
|
542
604
|
/** Checks verification value */
|
|
543
605
|
async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri) {
|
|
544
|
-
const verification = await ctx.context.internalAdapter.
|
|
606
|
+
const verification = await ctx.context.internalAdapter.consumeVerificationValue(await storeToken(opts.storeTokens, code, "authorization_code"));
|
|
545
607
|
if (!verification) throw new APIError("UNAUTHORIZED", {
|
|
546
|
-
error_description: "
|
|
547
|
-
error: "
|
|
548
|
-
});
|
|
549
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(await storeToken(opts.storeTokens, code, "authorization_code"));
|
|
550
|
-
if (!verification.expiresAt || verification.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
551
|
-
error_description: "code expired",
|
|
552
|
-
error: "invalid_verification"
|
|
608
|
+
error_description: "invalid code",
|
|
609
|
+
error: "invalid_grant"
|
|
553
610
|
});
|
|
554
611
|
let rawValue;
|
|
555
612
|
try {
|
|
@@ -557,13 +614,13 @@ async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri)
|
|
|
557
614
|
} catch {
|
|
558
615
|
throw new APIError("UNAUTHORIZED", {
|
|
559
616
|
error_description: "malformed verification value",
|
|
560
|
-
error: "
|
|
617
|
+
error: "invalid_grant"
|
|
561
618
|
});
|
|
562
619
|
}
|
|
563
620
|
const parsed = verificationValueSchema.safeParse(rawValue);
|
|
564
621
|
if (!parsed.success) throw new APIError("UNAUTHORIZED", {
|
|
565
622
|
error_description: "malformed verification value",
|
|
566
|
-
error: "
|
|
623
|
+
error: "invalid_grant"
|
|
567
624
|
});
|
|
568
625
|
const verificationValue = parsed.data;
|
|
569
626
|
if (verificationValue.query.client_id !== client_id) throw new APIError("UNAUTHORIZED", {
|
|
@@ -764,16 +821,7 @@ async function handleRefreshTokenGrant(ctx, opts) {
|
|
|
764
821
|
error: "invalid_grant"
|
|
765
822
|
});
|
|
766
823
|
if (refreshToken.revoked) {
|
|
767
|
-
await ctx.
|
|
768
|
-
model: "oauthRefreshToken",
|
|
769
|
-
where: [{
|
|
770
|
-
field: "clientId",
|
|
771
|
-
value: client_id
|
|
772
|
-
}, {
|
|
773
|
-
field: "userId",
|
|
774
|
-
value: refreshToken.userId
|
|
775
|
-
}]
|
|
776
|
-
});
|
|
824
|
+
await invalidateRefreshFamily(ctx, client_id, refreshToken.userId);
|
|
777
825
|
throw new APIError("BAD_REQUEST", {
|
|
778
826
|
error_description: "invalid refresh token",
|
|
779
827
|
error: "invalid_grant"
|
|
@@ -2176,12 +2224,12 @@ async function updateConsentEndpoint(ctx, opts) {
|
|
|
2176
2224
|
error_description: "no consent",
|
|
2177
2225
|
error: "not_found"
|
|
2178
2226
|
});
|
|
2227
|
+
if (consent.userId !== session.user.id) throw new APIError("UNAUTHORIZED");
|
|
2179
2228
|
const client = await getClient(ctx, opts, consent.clientId);
|
|
2180
|
-
if (!
|
|
2181
|
-
error_description: "
|
|
2229
|
+
if (!client) throw new APIError("NOT_FOUND", {
|
|
2230
|
+
error_description: "client not found",
|
|
2182
2231
|
error: "not_found"
|
|
2183
2232
|
});
|
|
2184
|
-
if (consent.userId !== session.user.id) throw new APIError("UNAUTHORIZED");
|
|
2185
2233
|
const allowedScopes = client?.scopes ?? opts.scopes ?? [];
|
|
2186
2234
|
const updates = ctx.body.update;
|
|
2187
2235
|
const scopes = updates.scopes;
|
|
@@ -2329,16 +2377,7 @@ async function revokeRefreshToken(ctx, opts, token, clientId) {
|
|
|
2329
2377
|
error: "invalid_request"
|
|
2330
2378
|
});
|
|
2331
2379
|
if (refreshToken.revoked) {
|
|
2332
|
-
await ctx.
|
|
2333
|
-
model: "oauthRefreshToken",
|
|
2334
|
-
where: [{
|
|
2335
|
-
field: "clientId",
|
|
2336
|
-
value: clientId
|
|
2337
|
-
}, {
|
|
2338
|
-
field: "userId",
|
|
2339
|
-
value: refreshToken.userId
|
|
2340
|
-
}]
|
|
2341
|
-
});
|
|
2380
|
+
await invalidateRefreshFamily(ctx, clientId, refreshToken.userId);
|
|
2342
2381
|
throw new APIError$1("BAD_REQUEST", {
|
|
2343
2382
|
error_description: "refresh token revoked",
|
|
2344
2383
|
error: "invalid_request"
|
|
@@ -2346,20 +2385,31 @@ async function revokeRefreshToken(ctx, opts, token, clientId) {
|
|
|
2346
2385
|
}
|
|
2347
2386
|
if (!refreshToken.clientId || refreshToken.clientId !== clientId) return null;
|
|
2348
2387
|
const iat = Math.floor(Date.now() / 1e3);
|
|
2349
|
-
await
|
|
2350
|
-
model: "oauthAccessToken",
|
|
2351
|
-
where: [{
|
|
2352
|
-
field: "refreshId",
|
|
2353
|
-
value: refreshToken.id
|
|
2354
|
-
}]
|
|
2355
|
-
}), ctx.context.adapter.update({
|
|
2388
|
+
if (!await ctx.context.adapter.update({
|
|
2356
2389
|
model: "oauthRefreshToken",
|
|
2357
2390
|
where: [{
|
|
2358
2391
|
field: "id",
|
|
2359
2392
|
value: refreshToken.id
|
|
2393
|
+
}, {
|
|
2394
|
+
field: "revoked",
|
|
2395
|
+
operator: "eq",
|
|
2396
|
+
value: null
|
|
2360
2397
|
}],
|
|
2361
2398
|
update: { revoked: /* @__PURE__ */ new Date(iat * 1e3) }
|
|
2362
|
-
})
|
|
2399
|
+
})) {
|
|
2400
|
+
await invalidateRefreshFamily(ctx, clientId, refreshToken.userId);
|
|
2401
|
+
throw new APIError$1("BAD_REQUEST", {
|
|
2402
|
+
error_description: "refresh token revoked",
|
|
2403
|
+
error: "invalid_request"
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
await ctx.context.adapter.deleteMany({
|
|
2407
|
+
model: "oauthAccessToken",
|
|
2408
|
+
where: [{
|
|
2409
|
+
field: "refreshId",
|
|
2410
|
+
value: refreshToken.id
|
|
2411
|
+
}]
|
|
2412
|
+
});
|
|
2363
2413
|
}
|
|
2364
2414
|
/**
|
|
2365
2415
|
* We don't know the access token format so we try to validate it
|
|
@@ -2569,7 +2619,8 @@ const schema = {
|
|
|
2569
2619
|
oauthRefreshToken: { fields: {
|
|
2570
2620
|
token: {
|
|
2571
2621
|
type: "string",
|
|
2572
|
-
required: true
|
|
2622
|
+
required: true,
|
|
2623
|
+
unique: true
|
|
2573
2624
|
},
|
|
2574
2625
|
clientId: {
|
|
2575
2626
|
type: "string",
|
|
@@ -2776,6 +2827,47 @@ const oauthProvider = (options) => {
|
|
|
2776
2827
|
if (opts.grantTypes && opts.grantTypes.includes("refresh_token") && !opts.grantTypes.includes("authorization_code")) throw new BetterAuthError("refresh_token grant requires authorization_code grant");
|
|
2777
2828
|
if (opts.disableJwtPlugin && (opts.storeClientSecret === "hashed" || typeof opts.storeClientSecret === "object" && "hash" in opts.storeClientSecret)) throw new BetterAuthError("unable to store hashed secrets because id tokens will be signed with secret");
|
|
2778
2829
|
if (!opts.disableJwtPlugin && (opts.storeClientSecret === "encrypted" || typeof opts.storeClientSecret === "object" && ("encrypt" in opts.storeClientSecret || "decrypt" in opts.storeClientSecret))) throw new BetterAuthError("encryption method not recommended, please use 'hashed' or the 'hash' function");
|
|
2830
|
+
const handleIssuerMetadataRequest = async (request, ctx) => {
|
|
2831
|
+
const requestPathname = new URL(request.url).pathname;
|
|
2832
|
+
const requestPath = ctx.options.advanced?.skipTrailingSlashes ? requestPathname.replace(/\/+$/, "") || "/" : requestPathname;
|
|
2833
|
+
const issuer = opts.disableJwtPlugin ? ctx.baseURL : getJwtPlugin(ctx)?.options?.jwt?.issuer ?? ctx.baseURL;
|
|
2834
|
+
let issuerPath = "/";
|
|
2835
|
+
try {
|
|
2836
|
+
issuerPath = new URL(issuer).pathname.replace(/\/$/, "") || "";
|
|
2837
|
+
} catch {
|
|
2838
|
+
issuerPath = new URL(ctx.baseURL).pathname.replace(/\/$/, "") || "";
|
|
2839
|
+
}
|
|
2840
|
+
const endpointCtx = { context: ctx };
|
|
2841
|
+
const authServerMetadataPaths = new Set([`/.well-known/oauth-authorization-server${issuerPath}`, `${issuerPath}/.well-known/oauth-authorization-server`]);
|
|
2842
|
+
const openIdConfigPath = `${issuerPath}/.well-known/openid-configuration`;
|
|
2843
|
+
const isAuthServerMetadataRequest = authServerMetadataPaths.has(requestPath);
|
|
2844
|
+
const isOpenIdConfigRequest = opts.scopes?.includes("openid") && requestPath === openIdConfigPath;
|
|
2845
|
+
const createMetadataResponse = (metadata) => {
|
|
2846
|
+
const response = metadataResponse(metadata);
|
|
2847
|
+
if (request.method === "HEAD") return new Response(null, {
|
|
2848
|
+
status: response.status,
|
|
2849
|
+
headers: response.headers
|
|
2850
|
+
});
|
|
2851
|
+
return response;
|
|
2852
|
+
};
|
|
2853
|
+
if (isAuthServerMetadataRequest || isOpenIdConfigRequest) {
|
|
2854
|
+
if (request.method !== "GET" && request.method !== "HEAD") return { response: new Response(null, {
|
|
2855
|
+
status: 405,
|
|
2856
|
+
headers: { Allow: "GET, HEAD" }
|
|
2857
|
+
}) };
|
|
2858
|
+
}
|
|
2859
|
+
if (isAuthServerMetadataRequest) {
|
|
2860
|
+
if (opts.scopes?.includes("openid")) return { response: createMetadataResponse(oidcServerMetadata(endpointCtx, opts)) };
|
|
2861
|
+
return { response: createMetadataResponse(authServerMetadata(endpointCtx, opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx)?.options, {
|
|
2862
|
+
scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes,
|
|
2863
|
+
dynamic_client_registration_supported: opts.allowDynamicClientRegistration,
|
|
2864
|
+
public_client_supported: opts.allowUnauthenticatedClientRegistration,
|
|
2865
|
+
grant_types_supported: opts.grantTypes,
|
|
2866
|
+
jwt_disabled: opts.disableJwtPlugin
|
|
2867
|
+
})) };
|
|
2868
|
+
}
|
|
2869
|
+
if (isOpenIdConfigRequest) return { response: createMetadataResponse(oidcServerMetadata(endpointCtx, opts)) };
|
|
2870
|
+
};
|
|
2779
2871
|
return {
|
|
2780
2872
|
id: "oauth-provider",
|
|
2781
2873
|
version: PACKAGE_VERSION,
|
|
@@ -2797,6 +2889,7 @@ const oauthProvider = (options) => {
|
|
|
2797
2889
|
if (!opts.silenceWarnings?.openidConfig && ctx.options.basePath !== issuerPath && opts.scopes?.includes("openid")) logger.warn(`Please ensure '${issuerPath}${issuerPath.endsWith("/") ? "" : "/"}.well-known/openid-configuration' exists. Upon completion, clear with silenceWarnings.openidConfig.`);
|
|
2798
2890
|
}
|
|
2799
2891
|
},
|
|
2892
|
+
onRequest: handleIssuerMetadataRequest,
|
|
2800
2893
|
hooks: {
|
|
2801
2894
|
before: [{
|
|
2802
2895
|
matcher(ctx) {
|
|
@@ -2853,6 +2946,7 @@ const oauthProvider = (options) => {
|
|
|
2853
2946
|
if (opts.scopes && opts.scopes.includes("openid")) return oidcServerMetadata(ctx, opts);
|
|
2854
2947
|
else return authServerMetadata(ctx, opts.disableJwtPlugin ? void 0 : getJwtPlugin(ctx.context)?.options, {
|
|
2855
2948
|
scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes,
|
|
2949
|
+
dynamic_client_registration_supported: opts.allowDynamicClientRegistration,
|
|
2856
2950
|
public_client_supported: opts.allowUnauthenticatedClientRegistration,
|
|
2857
2951
|
grant_types_supported: opts.grantTypes,
|
|
2858
2952
|
jwt_disabled: opts.disableJwtPlugin
|
|
@@ -3975,7 +4069,7 @@ function authServerMetadata(ctx, opts, overrides) {
|
|
|
3975
4069
|
authorization_endpoint: `${baseURL}/oauth2/authorize`,
|
|
3976
4070
|
token_endpoint: `${baseURL}/oauth2/token`,
|
|
3977
4071
|
jwks_uri: overrides?.jwt_disabled ? void 0 : opts?.jwks?.remoteUrl ?? `${baseURL}${opts?.jwks?.jwksPath ?? "/jwks"}`,
|
|
3978
|
-
registration_endpoint: `${baseURL}/oauth2/register
|
|
4072
|
+
registration_endpoint: overrides?.dynamic_client_registration_supported ? `${baseURL}/oauth2/register` : void 0,
|
|
3979
4073
|
introspection_endpoint: `${baseURL}/oauth2/introspect`,
|
|
3980
4074
|
revocation_endpoint: `${baseURL}/oauth2/revoke`,
|
|
3981
4075
|
response_types_supported: overrides?.grant_types_supported && !overrides.grant_types_supported.includes("authorization_code") ? [] : ["code"],
|
|
@@ -4002,6 +4096,7 @@ function oidcServerMetadata(ctx, opts) {
|
|
|
4002
4096
|
return {
|
|
4003
4097
|
...authServerMetadata(ctx, jwtPluginOptions, {
|
|
4004
4098
|
scopes_supported: opts.advertisedMetadata?.scopes_supported ?? opts.scopes,
|
|
4099
|
+
dynamic_client_registration_supported: opts.allowDynamicClientRegistration,
|
|
4005
4100
|
public_client_supported: opts.allowUnauthenticatedClientRegistration,
|
|
4006
4101
|
grant_types_supported: opts.grantTypes,
|
|
4007
4102
|
jwt_disabled: opts.disableJwtPlugin
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as Scope, a as OAuthClient, d as OAuthConsent, n as AuthServerMetadata, o as OIDCMetadata, p as OAuthOptions } from "./oauth-
|
|
1
|
+
import { _ as Scope, a as OAuthClient, d as OAuthConsent, n as AuthServerMetadata, o as OIDCMetadata, p as OAuthOptions } from "./oauth-D74mBkw6.mjs";
|
|
2
2
|
import * as better_call0 from "better-call";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
import * as better_auth_plugins0 from "better-auth/plugins";
|
|
@@ -30,6 +30,11 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
30
30
|
version: string;
|
|
31
31
|
options: NoInfer<O>;
|
|
32
32
|
init: (ctx: better_auth0.AuthContext) => void;
|
|
33
|
+
onRequest: (request: Request, ctx: better_auth0.AuthContext) => Promise<{
|
|
34
|
+
response: Response;
|
|
35
|
+
} | {
|
|
36
|
+
request: Request;
|
|
37
|
+
} | void>;
|
|
33
38
|
hooks: {
|
|
34
39
|
before: {
|
|
35
40
|
matcher(ctx: better_auth0.HookEndpointContext): any;
|
|
@@ -1914,6 +1919,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
1914
1919
|
token: {
|
|
1915
1920
|
type: "string";
|
|
1916
1921
|
required: true;
|
|
1922
|
+
unique: true;
|
|
1917
1923
|
};
|
|
1918
1924
|
clientId: {
|
|
1919
1925
|
type: "string";
|
|
@@ -143,6 +143,7 @@ declare const schema: {
|
|
|
143
143
|
token: {
|
|
144
144
|
type: "string";
|
|
145
145
|
required: true;
|
|
146
|
+
unique: true;
|
|
146
147
|
};
|
|
147
148
|
clientId: {
|
|
148
149
|
type: "string";
|
|
@@ -1379,9 +1380,11 @@ interface AuthServerMetadata {
|
|
|
1379
1380
|
/**
|
|
1380
1381
|
* The URL of the dynamic client registration endpoint.
|
|
1381
1382
|
*
|
|
1383
|
+
* This field is only present when `allowDynamicClientRegistration` is enabled.
|
|
1384
|
+
*
|
|
1382
1385
|
* @default `/oauth2/register`
|
|
1383
1386
|
*/
|
|
1384
|
-
registration_endpoint
|
|
1387
|
+
registration_endpoint?: string;
|
|
1385
1388
|
/**
|
|
1386
1389
|
* Supported scopes.
|
|
1387
1390
|
*/
|
|
@@ -254,11 +254,13 @@ function basicToClientCredentials(authorization) {
|
|
|
254
254
|
if (authorization.startsWith("Basic ")) {
|
|
255
255
|
const encoded = authorization.replace("Basic ", "");
|
|
256
256
|
const decoded = new TextDecoder().decode(base64.decode(encoded));
|
|
257
|
-
|
|
257
|
+
const separatorIndex = decoded.indexOf(":");
|
|
258
|
+
if (separatorIndex === -1) throw new APIError$1("BAD_REQUEST", {
|
|
258
259
|
error_description: "invalid authorization header format",
|
|
259
260
|
error: "invalid_client"
|
|
260
261
|
});
|
|
261
|
-
const
|
|
262
|
+
const id = decoded.slice(0, separatorIndex);
|
|
263
|
+
const secret = decoded.slice(separatorIndex + 1);
|
|
262
264
|
if (!id || !secret) throw new APIError$1("BAD_REQUEST", {
|
|
263
265
|
error_description: "invalid authorization header format",
|
|
264
266
|
error: "invalid_client"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/oauth-provider",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.12",
|
|
4
4
|
"description": "An oauth provider plugin for Better Auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -64,15 +64,15 @@
|
|
|
64
64
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
65
65
|
"listhen": "^1.9.0",
|
|
66
66
|
"tsdown": "0.21.1",
|
|
67
|
-
"@better-auth/core": "1.6.
|
|
68
|
-
"better-auth": "1.6.
|
|
67
|
+
"@better-auth/core": "1.6.12",
|
|
68
|
+
"better-auth": "1.6.12"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
|
-
"@better-auth/utils": "0.4.
|
|
71
|
+
"@better-auth/utils": "0.4.1",
|
|
72
72
|
"@better-fetch/fetch": "1.1.21",
|
|
73
73
|
"better-call": "1.3.5",
|
|
74
|
-
"@better-auth/core": "^1.6.
|
|
75
|
-
"better-auth": "^1.6.
|
|
74
|
+
"@better-auth/core": "^1.6.12",
|
|
75
|
+
"better-auth": "^1.6.12"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"build": "tsdown",
|