@better-auth/oauth-provider 1.6.15 → 1.6.16
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.
|
@@ -49,6 +49,20 @@ interface VerifyAccessTokenRemote {
|
|
|
49
49
|
* is also still active.
|
|
50
50
|
*/
|
|
51
51
|
force?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Accept introspection responses that omit the `aud` claim even when a
|
|
54
|
+
* required `audience` is configured in `verifyOptions`.
|
|
55
|
+
*
|
|
56
|
+
* By default verification fails closed: if you configure an `audience` and
|
|
57
|
+
* the introspection response has no `aud` (or a mismatching one), the token
|
|
58
|
+
* is rejected. Some authorization servers legitimately omit `aud` from
|
|
59
|
+
* introspection responses (it is OPTIONAL per RFC 7662 §2.2); only enable
|
|
60
|
+
* this if you trust the issuer to bind the token to this resource through
|
|
61
|
+
* another mechanism, as it skips the audience check in that case.
|
|
62
|
+
*
|
|
63
|
+
* @default false
|
|
64
|
+
*/
|
|
65
|
+
allowMissingAudience?: boolean;
|
|
52
66
|
}
|
|
53
67
|
type VerifyAccessTokenOutput<T> = T extends undefined ? (token: string | undefined, opts: VerifyAccessTokenNoAuthOpts) => Promise<JWTPayload> : (token: string | undefined, opts?: VerifyAccessTokenAuthOpts) => Promise<JWTPayload>;
|
|
54
68
|
type VerifyAccessTokenAuthOpts = {
|
package/dist/client-resource.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { C as handleMcpErrors, a as getJwtPlugin, o as getOAuthProviderPlugin } from "./utils-BKGiA6QL.mjs";
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-BJguNh6q.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.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { S as verifyOAuthQueryParams, _ as searchParamsToQuery, a as getJwtPlugin, b as storeToken, c as getStoredToken, d as parseClientMetadata, f as parsePrompt, g as resolveSubjectIdentifier, h as resolveSessionAuthTime, i as getClient, l as isPKCERequired, m as removePromptFromQuery, n as clientAllowsGrant, p as postLoginClearedParam, r as decryptStoredClientSecret, s as getSignedQueryIssuedAt, t as basicToClientCredentials, u as normalizeTimestampValue, v as signedQueryIssuedAtParam, w as mcpHandler, x as validateClientCredentials, y as storeClientSecret } from "./utils-BKGiA6QL.mjs";
|
|
2
|
+
import { t as PACKAGE_VERSION } from "./version-BJguNh6q.mjs";
|
|
3
3
|
import { APIError, createAuthEndpoint, createAuthMiddleware, dispatchAuthEndpoint, 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";
|
|
@@ -143,7 +143,8 @@ async function created(ctx, authorize) {
|
|
|
143
143
|
return await authorize(ctx);
|
|
144
144
|
}
|
|
145
145
|
async function postLogin(ctx, authorize) {
|
|
146
|
-
const
|
|
146
|
+
const state = await oAuthState.get();
|
|
147
|
+
const _query = state?.query;
|
|
147
148
|
if (!_query) throw new APIError("BAD_REQUEST", {
|
|
148
149
|
error_description: "missing oauth query",
|
|
149
150
|
error: "invalid_request"
|
|
@@ -151,7 +152,8 @@ async function postLogin(ctx, authorize) {
|
|
|
151
152
|
const query = new URLSearchParams(_query);
|
|
152
153
|
ctx.headers?.set("accept", "application/json");
|
|
153
154
|
ctx.query = searchParamsToQuery(query);
|
|
154
|
-
|
|
155
|
+
const session = await getSessionFromCtx(ctx);
|
|
156
|
+
return await authorize(ctx, { postLogin: state?.postLoginClearedForSession !== void 0 && state.postLoginClearedForSession === session?.session.id });
|
|
155
157
|
}
|
|
156
158
|
//#endregion
|
|
157
159
|
//#region src/types/zod.ts
|
|
@@ -503,7 +505,7 @@ async function createUserTokens(ctx, opts, params) {
|
|
|
503
505
|
return prev < curr ? prev : curr;
|
|
504
506
|
}, defaultExp) : defaultExp;
|
|
505
507
|
const audience = await checkResource(ctx, opts, scopes);
|
|
506
|
-
const isRefreshToken = user && (existingRefreshToken?.scopes?.includes("offline_access") || scopes.includes("offline_access"));
|
|
508
|
+
const isRefreshToken = user && clientAllowsGrant(client, "refresh_token") && (existingRefreshToken?.scopes?.includes("offline_access") || scopes.includes("offline_access"));
|
|
507
509
|
const isJwtAccessToken = audience && !opts.disableJwtPlugin;
|
|
508
510
|
const isIdToken = user && scopes.includes("openid");
|
|
509
511
|
const customFields = opts.customTokenResponseFields ? await opts.customTokenResponseFields({
|
|
@@ -618,7 +620,7 @@ async function handleAuthorizationCodeGrant(ctx, opts) {
|
|
|
618
620
|
error: "invalid_scope"
|
|
619
621
|
});
|
|
620
622
|
/** Verify Client */
|
|
621
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, scopes);
|
|
623
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, scopes, "authorization_code");
|
|
622
624
|
if (isPKCERequired(client, (verificationValue.query?.scope)?.split(" ") || [])) {
|
|
623
625
|
if (!isAuthCodeWithPkce) throw new APIError("BAD_REQUEST", {
|
|
624
626
|
error_description: "PKCE is required for this client",
|
|
@@ -701,7 +703,7 @@ async function handleClientCredentialsGrant(ctx, opts) {
|
|
|
701
703
|
error_description: "Missing a required client_secret",
|
|
702
704
|
error: "invalid_grant"
|
|
703
705
|
});
|
|
704
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret);
|
|
706
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, void 0, "client_credentials");
|
|
705
707
|
let requestedScopes = scope?.split(" ");
|
|
706
708
|
if (requestedScopes) {
|
|
707
709
|
const validScopes = new Set(client.scopes ?? opts.scopes);
|
|
@@ -784,7 +786,7 @@ async function handleRefreshTokenGrant(ctx, opts) {
|
|
|
784
786
|
error: "invalid_scope"
|
|
785
787
|
});
|
|
786
788
|
}
|
|
787
|
-
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, requestedScopes ?? scopes);
|
|
789
|
+
const client = await validateClientCredentials(ctx, opts, client_id, client_secret, requestedScopes ?? scopes, "refresh_token");
|
|
788
790
|
const user = await ctx.context.internalAdapter.findUserById(refreshToken.userId);
|
|
789
791
|
if (!user) throw new APIError("BAD_REQUEST", {
|
|
790
792
|
error_description: "user not found",
|
|
@@ -842,12 +844,10 @@ async function validateJwtAccessToken(ctx, opts, token, clientId) {
|
|
|
842
844
|
}
|
|
843
845
|
throw new Error(error);
|
|
844
846
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
if (clientId && jwtPayload.azp !== clientId) return { active: false };
|
|
850
|
-
}
|
|
847
|
+
if (!jwtPayload.azp) return { active: false };
|
|
848
|
+
const client = await getClient(ctx, opts, jwtPayload.azp);
|
|
849
|
+
if (!client || client?.disabled) return { active: false };
|
|
850
|
+
if (clientId && jwtPayload.azp !== clientId) return { active: false };
|
|
851
851
|
const sessionId = jwtPayload.sid;
|
|
852
852
|
if (sessionId) {
|
|
853
853
|
const session = await ctx.context.adapter.findOne({
|
|
@@ -859,7 +859,7 @@ async function validateJwtAccessToken(ctx, opts, token, clientId) {
|
|
|
859
859
|
});
|
|
860
860
|
if (!session || session.expiresAt < /* @__PURE__ */ new Date()) jwtPayload.sid = void 0;
|
|
861
861
|
}
|
|
862
|
-
|
|
862
|
+
jwtPayload.client_id = jwtPayload.azp;
|
|
863
863
|
jwtPayload.active = true;
|
|
864
864
|
return jwtPayload;
|
|
865
865
|
}
|
|
@@ -3856,6 +3856,7 @@ async function authorizeEndpoint(ctx, opts, settings) {
|
|
|
3856
3856
|
const client = await getClient(ctx, opts, query.client_id);
|
|
3857
3857
|
if (!client) return handleRedirect(ctx, getErrorURL(ctx, "invalid_client", "client_id is required"));
|
|
3858
3858
|
if (client.disabled) return handleRedirect(ctx, getErrorURL(ctx, "client_disabled", "client is disabled"));
|
|
3859
|
+
if (!clientAllowsGrant(client, "authorization_code")) return handleRedirect(ctx, getErrorURL(ctx, "unauthorized_client", "client is not authorized to use the authorization_code grant"));
|
|
3859
3860
|
if (!client.redirectUris?.find((url) => {
|
|
3860
3861
|
if (url === query.redirect_uri) return true;
|
|
3861
3862
|
try {
|
|
@@ -272,12 +272,27 @@ function basicToClientCredentials(authorization) {
|
|
|
272
272
|
}
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
|
+
* Whether a client is allowed to use a given grant type.
|
|
276
|
+
*
|
|
277
|
+
* A client's registered `grantTypes` defaults to the documented default
|
|
278
|
+
* `["authorization_code"]` when unset (see client registration). Refresh tokens
|
|
279
|
+
* are only ever issued through the authorization_code flow, so a client allowed
|
|
280
|
+
* to use `authorization_code` is implicitly allowed to use `refresh_token`.
|
|
281
|
+
*
|
|
282
|
+
* @internal
|
|
283
|
+
*/
|
|
284
|
+
function clientAllowsGrant(client, grantType) {
|
|
285
|
+
const allowedGrants = client.grantTypes && client.grantTypes.length > 0 ? client.grantTypes : ["authorization_code"];
|
|
286
|
+
if (grantType === "refresh_token" && allowedGrants.includes("authorization_code")) return true;
|
|
287
|
+
return allowedGrants.includes(grantType);
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
275
290
|
* Validates client credentials failing on mismatches
|
|
276
291
|
* and incorrectly provided information
|
|
277
292
|
*
|
|
278
293
|
* @internal
|
|
279
294
|
*/
|
|
280
|
-
async function validateClientCredentials(ctx, options, clientId, clientSecret, scopes) {
|
|
295
|
+
async function validateClientCredentials(ctx, options, clientId, clientSecret, scopes, grantType) {
|
|
281
296
|
const client = await getClient(ctx, options, clientId);
|
|
282
297
|
if (!client) throw new APIError$1("BAD_REQUEST", {
|
|
283
298
|
error_description: "missing client",
|
|
@@ -306,6 +321,10 @@ async function validateClientCredentials(ctx, options, clientId, clientSecret, s
|
|
|
306
321
|
error: "invalid_scope"
|
|
307
322
|
});
|
|
308
323
|
}
|
|
324
|
+
if (grantType && !clientAllowsGrant(client, grantType)) throw new APIError$1("BAD_REQUEST", {
|
|
325
|
+
error_description: `client is not authorized to use grant type ${grantType}`,
|
|
326
|
+
error: "unauthorized_client"
|
|
327
|
+
});
|
|
309
328
|
return client;
|
|
310
329
|
}
|
|
311
330
|
/**
|
|
@@ -417,4 +436,4 @@ function isPKCERequired(client, requestedScopes) {
|
|
|
417
436
|
return false;
|
|
418
437
|
}
|
|
419
438
|
//#endregion
|
|
420
|
-
export {
|
|
439
|
+
export { handleMcpErrors as C, verifyOAuthQueryParams as S, searchParamsToQuery as _, getJwtPlugin as a, storeToken as b, getStoredToken as c, parseClientMetadata as d, parsePrompt as f, resolveSubjectIdentifier as g, resolveSessionAuthTime as h, getClient as i, isPKCERequired as l, removePromptFromQuery as m, clientAllowsGrant as n, getOAuthProviderPlugin as o, postLoginClearedParam as p, decryptStoredClientSecret as r, getSignedQueryIssuedAt as s, basicToClientCredentials as t, normalizeTimestampValue as u, signedQueryIssuedAtParam as v, mcpHandler as w, validateClientCredentials as x, storeClientSecret as y };
|
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.16",
|
|
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.16",
|
|
68
|
+
"better-auth": "1.6.16"
|
|
69
69
|
},
|
|
70
70
|
"peerDependencies": {
|
|
71
71
|
"@better-auth/utils": "0.4.1",
|
|
72
|
-
"@better-fetch/fetch": "1.
|
|
73
|
-
"better-call": "1.3.
|
|
74
|
-
"@better-auth/core": "^1.6.
|
|
75
|
-
"better-auth": "^1.6.
|
|
72
|
+
"@better-fetch/fetch": "1.2.2",
|
|
73
|
+
"better-call": "1.3.6",
|
|
74
|
+
"@better-auth/core": "^1.6.16",
|
|
75
|
+
"better-auth": "^1.6.16"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
78
|
"build": "tsdown",
|