@better-auth/oauth-provider 1.6.10 → 1.6.11
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 +1 -1
- package/dist/client-resource.mjs +1 -1
- package/dist/client.d.mts +1 -1
- package/dist/client.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +103 -48
- package/dist/{oauth-CqgT-XaR.d.mts → oauth-BqWgUea8.d.mts} +1 -0
- package/dist/{oauth-DBCeGXT3.d.mts → oauth-C4GaGx2I.d.mts} +2 -1
- package/dist/{version-Cg-c01N0.mjs → version-CRjaDWWg.mjs} +1 -1
- package/package.json +5 -5
package/dist/client-resource.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { S as handleMcpErrors, a as getOAuthProviderPlugin, i as getJwtPlugin } from "./utils-LAthGy-x.mjs";
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-CRjaDWWg.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-BqWgUea8.mjs";
|
|
2
|
+
import { n as oauthProvider, t as getOAuthProviderState } from "./oauth-C4GaGx2I.mjs";
|
|
3
3
|
import { verifyAccessToken } from "better-auth/oauth2";
|
|
4
4
|
import { JWSAlgorithms, JwtOptions } from "better-auth/plugins";
|
|
5
5
|
import { JWTPayload } from "jose";
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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-LAthGy-x.mjs";
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-CRjaDWWg.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,14 @@ 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
608
|
error_description: "Invalid code",
|
|
547
|
-
error: "
|
|
609
|
+
error: "invalid_grant"
|
|
548
610
|
});
|
|
549
|
-
await ctx.context.internalAdapter.deleteVerificationByIdentifier(await storeToken(opts.storeTokens, code, "authorization_code"));
|
|
550
611
|
if (!verification.expiresAt || verification.expiresAt < /* @__PURE__ */ new Date()) throw new APIError("UNAUTHORIZED", {
|
|
551
612
|
error_description: "code expired",
|
|
552
|
-
error: "
|
|
613
|
+
error: "invalid_grant"
|
|
553
614
|
});
|
|
554
615
|
let rawValue;
|
|
555
616
|
try {
|
|
@@ -557,13 +618,13 @@ async function checkVerificationValue(ctx, opts, code, client_id, redirect_uri)
|
|
|
557
618
|
} catch {
|
|
558
619
|
throw new APIError("UNAUTHORIZED", {
|
|
559
620
|
error_description: "malformed verification value",
|
|
560
|
-
error: "
|
|
621
|
+
error: "invalid_grant"
|
|
561
622
|
});
|
|
562
623
|
}
|
|
563
624
|
const parsed = verificationValueSchema.safeParse(rawValue);
|
|
564
625
|
if (!parsed.success) throw new APIError("UNAUTHORIZED", {
|
|
565
626
|
error_description: "malformed verification value",
|
|
566
|
-
error: "
|
|
627
|
+
error: "invalid_grant"
|
|
567
628
|
});
|
|
568
629
|
const verificationValue = parsed.data;
|
|
569
630
|
if (verificationValue.query.client_id !== client_id) throw new APIError("UNAUTHORIZED", {
|
|
@@ -764,16 +825,7 @@ async function handleRefreshTokenGrant(ctx, opts) {
|
|
|
764
825
|
error: "invalid_grant"
|
|
765
826
|
});
|
|
766
827
|
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
|
-
});
|
|
828
|
+
await invalidateRefreshFamily(ctx, client_id, refreshToken.userId);
|
|
777
829
|
throw new APIError("BAD_REQUEST", {
|
|
778
830
|
error_description: "invalid refresh token",
|
|
779
831
|
error: "invalid_grant"
|
|
@@ -2329,16 +2381,7 @@ async function revokeRefreshToken(ctx, opts, token, clientId) {
|
|
|
2329
2381
|
error: "invalid_request"
|
|
2330
2382
|
});
|
|
2331
2383
|
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
|
-
});
|
|
2384
|
+
await invalidateRefreshFamily(ctx, clientId, refreshToken.userId);
|
|
2342
2385
|
throw new APIError$1("BAD_REQUEST", {
|
|
2343
2386
|
error_description: "refresh token revoked",
|
|
2344
2387
|
error: "invalid_request"
|
|
@@ -2346,20 +2389,31 @@ async function revokeRefreshToken(ctx, opts, token, clientId) {
|
|
|
2346
2389
|
}
|
|
2347
2390
|
if (!refreshToken.clientId || refreshToken.clientId !== clientId) return null;
|
|
2348
2391
|
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({
|
|
2392
|
+
if (!await ctx.context.adapter.update({
|
|
2356
2393
|
model: "oauthRefreshToken",
|
|
2357
2394
|
where: [{
|
|
2358
2395
|
field: "id",
|
|
2359
2396
|
value: refreshToken.id
|
|
2397
|
+
}, {
|
|
2398
|
+
field: "revoked",
|
|
2399
|
+
operator: "eq",
|
|
2400
|
+
value: null
|
|
2360
2401
|
}],
|
|
2361
2402
|
update: { revoked: /* @__PURE__ */ new Date(iat * 1e3) }
|
|
2362
|
-
})
|
|
2403
|
+
})) {
|
|
2404
|
+
await invalidateRefreshFamily(ctx, clientId, refreshToken.userId);
|
|
2405
|
+
throw new APIError$1("BAD_REQUEST", {
|
|
2406
|
+
error_description: "refresh token revoked",
|
|
2407
|
+
error: "invalid_request"
|
|
2408
|
+
});
|
|
2409
|
+
}
|
|
2410
|
+
await ctx.context.adapter.deleteMany({
|
|
2411
|
+
model: "oauthAccessToken",
|
|
2412
|
+
where: [{
|
|
2413
|
+
field: "refreshId",
|
|
2414
|
+
value: refreshToken.id
|
|
2415
|
+
}]
|
|
2416
|
+
});
|
|
2363
2417
|
}
|
|
2364
2418
|
/**
|
|
2365
2419
|
* We don't know the access token format so we try to validate it
|
|
@@ -2569,7 +2623,8 @@ const schema = {
|
|
|
2569
2623
|
oauthRefreshToken: { fields: {
|
|
2570
2624
|
token: {
|
|
2571
2625
|
type: "string",
|
|
2572
|
-
required: true
|
|
2626
|
+
required: true,
|
|
2627
|
+
unique: true
|
|
2573
2628
|
},
|
|
2574
2629
|
clientId: {
|
|
2575
2630
|
type: "string",
|
|
@@ -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-BqWgUea8.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";
|
|
@@ -1914,6 +1914,7 @@ declare const oauthProvider: <O extends OAuthOptions<Scope[]>>(options: O) => {
|
|
|
1914
1914
|
token: {
|
|
1915
1915
|
type: "string";
|
|
1916
1916
|
required: true;
|
|
1917
|
+
unique: true;
|
|
1917
1918
|
};
|
|
1918
1919
|
clientId: {
|
|
1919
1920
|
type: "string";
|
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.11",
|
|
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.11",
|
|
68
|
+
"better-auth": "1.6.11"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@better-auth/utils": "0.4.0",
|
|
72
72
|
"@better-fetch/fetch": "1.1.21",
|
|
73
73
|
"better-call": "1.3.5",
|
|
74
|
-
"@better-auth/core": "^1.6.
|
|
75
|
-
"better-auth": "^1.6.
|
|
74
|
+
"@better-auth/core": "^1.6.11",
|
|
75
|
+
"better-auth": "^1.6.11"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"build": "tsdown",
|