@better-auth/core 1.7.0-beta.3 → 1.7.0-beta.4
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/context/global.mjs +1 -1
- package/dist/db/adapter/factory.mjs +63 -1
- package/dist/db/adapter/index.d.mts +35 -1
- package/dist/db/adapter/types.d.mts +1 -1
- package/dist/db/type.d.mts +12 -0
- package/dist/error/codes.d.mts +1 -0
- package/dist/error/codes.mjs +1 -0
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/authorization-params.d.mts +12 -0
- package/dist/oauth2/authorization-params.mjs +12 -0
- package/dist/oauth2/basic-credentials.d.mts +30 -0
- package/dist/oauth2/basic-credentials.mjs +64 -0
- package/dist/oauth2/client-assertion.d.mts +38 -22
- package/dist/oauth2/client-assertion.mjs +63 -28
- package/dist/oauth2/client-credentials-token.d.mts +19 -40
- package/dist/oauth2/client-credentials-token.mjs +18 -29
- package/dist/oauth2/create-authorization-url.d.mts +9 -1
- package/dist/oauth2/create-authorization-url.mjs +23 -5
- package/dist/oauth2/index.d.mts +10 -7
- package/dist/oauth2/index.mjs +9 -7
- package/dist/oauth2/oauth-provider.d.mts +21 -2
- package/dist/oauth2/refresh-access-token.d.mts +20 -40
- package/dist/oauth2/refresh-access-token.mjs +19 -32
- package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
- package/dist/oauth2/token-endpoint-auth.mjs +89 -0
- package/dist/oauth2/utils.d.mts +9 -1
- package/dist/oauth2/utils.mjs +12 -1
- package/dist/oauth2/validate-authorization-code.d.mts +17 -52
- package/dist/oauth2/validate-authorization-code.mjs +17 -30
- package/dist/oauth2/verify.mjs +15 -5
- package/dist/social-providers/apple.d.mts +4 -18
- package/dist/social-providers/apple.mjs +14 -3
- package/dist/social-providers/atlassian.d.mts +3 -1
- package/dist/social-providers/atlassian.mjs +5 -2
- package/dist/social-providers/cognito.d.mts +16 -1
- package/dist/social-providers/cognito.mjs +6 -2
- package/dist/social-providers/discord.d.mts +4 -2
- package/dist/social-providers/discord.mjs +16 -3
- package/dist/social-providers/dropbox.d.mts +3 -1
- package/dist/social-providers/dropbox.mjs +5 -4
- package/dist/social-providers/facebook.d.mts +3 -1
- package/dist/social-providers/facebook.mjs +5 -2
- package/dist/social-providers/figma.d.mts +3 -1
- package/dist/social-providers/figma.mjs +3 -2
- package/dist/social-providers/github.d.mts +3 -1
- package/dist/social-providers/github.mjs +5 -4
- package/dist/social-providers/gitlab.d.mts +3 -1
- package/dist/social-providers/gitlab.mjs +3 -2
- package/dist/social-providers/google.d.mts +3 -1
- package/dist/social-providers/google.mjs +5 -2
- package/dist/social-providers/huggingface.d.mts +3 -1
- package/dist/social-providers/huggingface.mjs +3 -2
- package/dist/social-providers/index.d.mts +104 -36
- package/dist/social-providers/kakao.d.mts +3 -1
- package/dist/social-providers/kakao.mjs +3 -2
- package/dist/social-providers/kick.d.mts +3 -1
- package/dist/social-providers/kick.mjs +3 -2
- package/dist/social-providers/line.d.mts +3 -1
- package/dist/social-providers/line.mjs +3 -2
- package/dist/social-providers/linear.d.mts +3 -1
- package/dist/social-providers/linear.mjs +3 -2
- package/dist/social-providers/linkedin.d.mts +3 -1
- package/dist/social-providers/linkedin.mjs +3 -2
- package/dist/social-providers/microsoft-entra-id.d.mts +2 -1
- package/dist/social-providers/microsoft-entra-id.mjs +3 -2
- package/dist/social-providers/naver.d.mts +3 -1
- package/dist/social-providers/naver.mjs +3 -2
- package/dist/social-providers/notion.d.mts +3 -1
- package/dist/social-providers/notion.mjs +5 -2
- package/dist/social-providers/paybin.d.mts +3 -1
- package/dist/social-providers/paybin.mjs +3 -2
- package/dist/social-providers/paypal.d.mts +3 -1
- package/dist/social-providers/paypal.mjs +4 -3
- package/dist/social-providers/polar.d.mts +3 -1
- package/dist/social-providers/polar.mjs +3 -2
- package/dist/social-providers/railway.d.mts +3 -1
- package/dist/social-providers/railway.mjs +3 -2
- package/dist/social-providers/reddit.d.mts +3 -1
- package/dist/social-providers/reddit.mjs +3 -2
- package/dist/social-providers/roblox.d.mts +4 -2
- package/dist/social-providers/roblox.mjs +12 -2
- package/dist/social-providers/salesforce.d.mts +3 -1
- package/dist/social-providers/salesforce.mjs +3 -2
- package/dist/social-providers/slack.d.mts +4 -2
- package/dist/social-providers/slack.mjs +11 -8
- package/dist/social-providers/spotify.d.mts +3 -1
- package/dist/social-providers/spotify.mjs +3 -2
- package/dist/social-providers/tiktok.d.mts +3 -1
- package/dist/social-providers/tiktok.mjs +14 -2
- package/dist/social-providers/twitch.d.mts +3 -1
- package/dist/social-providers/twitch.mjs +3 -2
- package/dist/social-providers/twitter.d.mts +5 -2
- package/dist/social-providers/twitter.mjs +2 -1
- package/dist/social-providers/vercel.d.mts +3 -1
- package/dist/social-providers/vercel.mjs +3 -2
- package/dist/social-providers/vk.d.mts +3 -1
- package/dist/social-providers/vk.mjs +3 -2
- package/dist/social-providers/wechat.d.mts +3 -1
- package/dist/social-providers/wechat.mjs +7 -1
- package/dist/social-providers/zoom.d.mts +4 -2
- package/dist/social-providers/zoom.mjs +10 -17
- package/dist/types/context.d.mts +23 -3
- package/dist/types/init-options.d.mts +29 -5
- package/dist/utils/ip.d.mts +5 -4
- package/dist/utils/ip.mjs +3 -3
- package/dist/utils/redirect-uri.d.mts +20 -0
- package/dist/utils/redirect-uri.mjs +48 -0
- package/dist/utils/string.d.mts +5 -1
- package/dist/utils/string.mjs +20 -1
- package/dist/utils/url.d.mts +18 -1
- package/dist/utils/url.mjs +30 -1
- package/package.json +9 -8
- package/src/db/adapter/factory.ts +118 -0
- package/src/db/adapter/index.ts +32 -0
- package/src/db/adapter/types.ts +1 -0
- package/src/db/type.ts +12 -0
- package/src/error/codes.ts +1 -0
- package/src/oauth2/authorization-params.ts +28 -0
- package/src/oauth2/basic-credentials.ts +87 -0
- package/src/oauth2/client-assertion.ts +131 -58
- package/src/oauth2/client-credentials-token.ts +48 -72
- package/src/oauth2/create-authorization-url.ts +28 -6
- package/src/oauth2/index.ts +25 -9
- package/src/oauth2/oauth-provider.ts +21 -2
- package/src/oauth2/refresh-access-token.ts +50 -76
- package/src/oauth2/token-endpoint-auth.ts +221 -0
- package/src/oauth2/utils.ts +19 -0
- package/src/oauth2/validate-authorization-code.ts +55 -85
- package/src/oauth2/verify.ts +20 -4
- package/src/social-providers/apple.ts +26 -2
- package/src/social-providers/atlassian.ts +8 -1
- package/src/social-providers/cognito.ts +26 -1
- package/src/social-providers/discord.ts +21 -17
- package/src/social-providers/dropbox.ts +7 -5
- package/src/social-providers/facebook.ts +11 -6
- package/src/social-providers/figma.ts +8 -1
- package/src/social-providers/github.ts +4 -2
- package/src/social-providers/gitlab.ts +2 -0
- package/src/social-providers/google.ts +2 -0
- package/src/social-providers/huggingface.ts +8 -1
- package/src/social-providers/kakao.ts +2 -1
- package/src/social-providers/kick.ts +8 -1
- package/src/social-providers/line.ts +2 -0
- package/src/social-providers/linear.ts +8 -1
- package/src/social-providers/linkedin.ts +2 -0
- package/src/social-providers/microsoft-entra-id.ts +1 -0
- package/src/social-providers/naver.ts +2 -1
- package/src/social-providers/notion.ts +8 -1
- package/src/social-providers/paybin.ts +2 -0
- package/src/social-providers/paypal.ts +7 -1
- package/src/social-providers/polar.ts +8 -1
- package/src/social-providers/railway.ts +8 -1
- package/src/social-providers/reddit.ts +2 -1
- package/src/social-providers/roblox.ts +16 -11
- package/src/social-providers/salesforce.ts +8 -1
- package/src/social-providers/slack.ts +15 -9
- package/src/social-providers/spotify.ts +8 -1
- package/src/social-providers/tiktok.ts +22 -9
- package/src/social-providers/twitch.ts +2 -1
- package/src/social-providers/twitter.ts +1 -0
- package/src/social-providers/vercel.ts +8 -1
- package/src/social-providers/vk.ts +8 -1
- package/src/social-providers/wechat.ts +9 -1
- package/src/social-providers/zoom.ts +15 -19
- package/src/types/context.ts +25 -4
- package/src/types/init-options.ts +29 -5
- package/src/utils/ip.ts +12 -13
- package/src/utils/redirect-uri.ts +54 -0
- package/src/utils/string.ts +37 -0
- package/src/utils/url.ts +28 -0
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { base64 } from "@better-auth/utils/base64";
|
|
1
|
+
import { applyTokenEndpointAuth } from "./token-endpoint-auth.mjs";
|
|
3
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
4
3
|
//#region src/oauth2/client-credentials-token.ts
|
|
5
|
-
async function clientCredentialsTokenRequest({ options, scope, authentication,
|
|
4
|
+
async function clientCredentialsTokenRequest({ options, scope, authentication, tokenEndpointAuth, tokenEndpoint, resource }) {
|
|
6
5
|
options = typeof options === "function" ? await options() : options;
|
|
7
|
-
|
|
8
|
-
if (authentication === "private_key_jwt") {
|
|
9
|
-
if (!clientAssertion) throw new Error("private_key_jwt authentication requires a clientAssertion configuration");
|
|
10
|
-
extraParams = await resolveAssertionParams({
|
|
11
|
-
clientAssertion,
|
|
12
|
-
clientId: Array.isArray(options.clientId) ? options.clientId[0] : options.clientId,
|
|
13
|
-
tokenEndpoint
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
return createClientCredentialsTokenRequest({
|
|
6
|
+
const request = buildClientCredentialsTokenRequest({
|
|
17
7
|
options,
|
|
18
8
|
scope,
|
|
19
|
-
|
|
20
|
-
resource,
|
|
21
|
-
extraParams
|
|
9
|
+
resource
|
|
22
10
|
});
|
|
11
|
+
await applyTokenEndpointAuth({
|
|
12
|
+
body: request.body,
|
|
13
|
+
headers: request.headers,
|
|
14
|
+
options,
|
|
15
|
+
tokenEndpoint: tokenEndpoint ?? "",
|
|
16
|
+
grantType: "client_credentials",
|
|
17
|
+
tokenEndpointAuth,
|
|
18
|
+
authentication
|
|
19
|
+
});
|
|
20
|
+
return request;
|
|
23
21
|
}
|
|
24
|
-
|
|
25
|
-
* @deprecated use async'd clientCredentialsTokenRequest instead
|
|
26
|
-
*/
|
|
27
|
-
function createClientCredentialsTokenRequest({ options, scope, authentication, resource, extraParams }) {
|
|
22
|
+
function buildClientCredentialsTokenRequest({ options, scope, resource, extraParams }) {
|
|
28
23
|
const body = new URLSearchParams();
|
|
29
24
|
const headers = {
|
|
30
25
|
"content-type": "application/x-www-form-urlencoded",
|
|
@@ -34,12 +29,6 @@ function createClientCredentialsTokenRequest({ options, scope, authentication, r
|
|
|
34
29
|
scope && body.set("scope", scope);
|
|
35
30
|
if (resource) if (typeof resource === "string") body.append("resource", resource);
|
|
36
31
|
else for (const _resource of resource) body.append("resource", _resource);
|
|
37
|
-
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
38
|
-
if (authentication === "basic") headers["authorization"] = `Basic ${base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`)}`;
|
|
39
|
-
else {
|
|
40
|
-
body.set("client_id", primaryClientId);
|
|
41
|
-
if (authentication !== "private_key_jwt" && options.clientSecret) body.set("client_secret", options.clientSecret);
|
|
42
|
-
}
|
|
43
32
|
if (extraParams) {
|
|
44
33
|
for (const [key, value] of Object.entries(extraParams)) if (!body.has(key)) body.append(key, value);
|
|
45
34
|
}
|
|
@@ -48,12 +37,12 @@ function createClientCredentialsTokenRequest({ options, scope, authentication, r
|
|
|
48
37
|
headers
|
|
49
38
|
};
|
|
50
39
|
}
|
|
51
|
-
async function clientCredentialsToken({ options, tokenEndpoint, scope, authentication,
|
|
40
|
+
async function clientCredentialsToken({ options, tokenEndpoint, scope, authentication, tokenEndpointAuth, resource }) {
|
|
52
41
|
const { body, headers } = await clientCredentialsTokenRequest({
|
|
53
42
|
options,
|
|
54
43
|
scope,
|
|
55
44
|
authentication,
|
|
56
|
-
|
|
45
|
+
tokenEndpointAuth,
|
|
57
46
|
tokenEndpoint,
|
|
58
47
|
resource
|
|
59
48
|
});
|
|
@@ -75,4 +64,4 @@ async function clientCredentialsToken({ options, tokenEndpoint, scope, authentic
|
|
|
75
64
|
return tokens;
|
|
76
65
|
}
|
|
77
66
|
//#endregion
|
|
78
|
-
export { clientCredentialsToken, clientCredentialsTokenRequest
|
|
67
|
+
export { clientCredentialsToken, clientCredentialsTokenRequest };
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { AwaitableFunction } from "../types/helper.mjs";
|
|
2
2
|
import { ProviderOptions } from "./oauth-provider.mjs";
|
|
3
3
|
//#region src/oauth2/create-authorization-url.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Query-parameter names that are populated by the framework as part of the
|
|
6
|
+
* authorization request and must not be overridden by caller-supplied
|
|
7
|
+
* `additionalParams`. Overriding `state`, PKCE, or `redirect_uri` would
|
|
8
|
+
* break the callback correlation and session pinning guarantees.
|
|
9
|
+
*/
|
|
10
|
+
declare const RESERVED_AUTHORIZATION_PARAMS: readonly ["state", "client_id", "redirect_uri", "response_type", "code_challenge", "code_challenge_method", "scope"];
|
|
11
|
+
declare const RESERVED_AUTHORIZATION_PARAMS_SET: ReadonlySet<string>;
|
|
4
12
|
declare function createAuthorizationURL({
|
|
5
13
|
id,
|
|
6
14
|
options,
|
|
@@ -41,4 +49,4 @@ declare function createAuthorizationURL({
|
|
|
41
49
|
scopeJoiner?: string | undefined;
|
|
42
50
|
}): Promise<URL>;
|
|
43
51
|
//#endregion
|
|
44
|
-
export { createAuthorizationURL };
|
|
52
|
+
export { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL };
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
import { generateCodeChallenge } from "./utils.mjs";
|
|
1
|
+
import { generateCodeChallenge, getPrimaryClientId } from "./utils.mjs";
|
|
2
2
|
//#region src/oauth2/create-authorization-url.ts
|
|
3
|
+
/**
|
|
4
|
+
* Query-parameter names that are populated by the framework as part of the
|
|
5
|
+
* authorization request and must not be overridden by caller-supplied
|
|
6
|
+
* `additionalParams`. Overriding `state`, PKCE, or `redirect_uri` would
|
|
7
|
+
* break the callback correlation and session pinning guarantees.
|
|
8
|
+
*/
|
|
9
|
+
const RESERVED_AUTHORIZATION_PARAMS = [
|
|
10
|
+
"state",
|
|
11
|
+
"client_id",
|
|
12
|
+
"redirect_uri",
|
|
13
|
+
"response_type",
|
|
14
|
+
"code_challenge",
|
|
15
|
+
"code_challenge_method",
|
|
16
|
+
"scope"
|
|
17
|
+
];
|
|
18
|
+
const RESERVED_AUTHORIZATION_PARAMS_SET = new Set(RESERVED_AUTHORIZATION_PARAMS);
|
|
3
19
|
async function createAuthorizationURL({ id, options, authorizationEndpoint, state, codeVerifier, scopes, claims, redirectURI, duration, prompt, accessType, responseType, display, loginHint, hd, responseMode, additionalParams, scopeJoiner }) {
|
|
4
20
|
options = typeof options === "function" ? await options() : options;
|
|
5
21
|
const url = new URL(options.authorizationEndpoint || authorizationEndpoint);
|
|
6
22
|
url.searchParams.set("response_type", responseType || "code");
|
|
7
|
-
const primaryClientId =
|
|
23
|
+
const primaryClientId = getPrimaryClientId(options.clientId);
|
|
24
|
+
if (!primaryClientId) throw new Error("OAuth provider requires clientId");
|
|
8
25
|
url.searchParams.set("client_id", primaryClientId);
|
|
9
26
|
url.searchParams.set("state", state);
|
|
10
27
|
if (scopes) url.searchParams.set("scope", scopes.join(scopeJoiner || " "));
|
|
@@ -32,10 +49,11 @@ async function createAuthorizationURL({ id, options, authorizationEndpoint, stat
|
|
|
32
49
|
...claimsObj
|
|
33
50
|
} }));
|
|
34
51
|
}
|
|
35
|
-
if (additionalParams)
|
|
52
|
+
if (additionalParams) for (const [key, value] of Object.entries(additionalParams)) {
|
|
53
|
+
if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
|
|
36
54
|
url.searchParams.set(key, value);
|
|
37
|
-
}
|
|
55
|
+
}
|
|
38
56
|
return url;
|
|
39
57
|
}
|
|
40
58
|
//#endregion
|
|
41
|
-
export { createAuthorizationURL };
|
|
59
|
+
export { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL };
|
package/dist/oauth2/index.d.mts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { additionalAuthorizationParamsSchema } from "./authorization-params.mjs";
|
|
2
|
+
import { decodeBasicCredentials, encodeBasicCredentials } from "./basic-credentials.mjs";
|
|
3
|
+
import { CLIENT_ASSERTION_TYPE, ClientAssertionContext, ClientAssertionGetter, ClientAssertionGrantType, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, PrivateKeyJwtClientAssertionGetterOptions, PrivateKeyJwtSigningAlgorithm, createPrivateKeyJwtClientAssertionGetter, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion } from "./client-assertion.mjs";
|
|
2
4
|
import { OAuth2Tokens, OAuth2UserInfo, OAuthProvider, ProviderOptions } from "./oauth-provider.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
5
|
+
import { TokenEndpointAuth, TokenEndpointAuthMethod, TokenEndpointSecretAuthentication } from "./token-endpoint-auth.mjs";
|
|
6
|
+
import { clientCredentialsToken, clientCredentialsTokenRequest } from "./client-credentials-token.mjs";
|
|
7
|
+
import { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL } from "./create-authorization-url.mjs";
|
|
8
|
+
import { refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
|
|
9
|
+
import { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
|
|
10
|
+
import { authorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
|
|
8
11
|
import { getJwks, verifyAccessToken, verifyJwsAccessToken } from "./verify.mjs";
|
|
9
|
-
export {
|
|
12
|
+
export { CLIENT_ASSERTION_TYPE, type ClientAssertionContext, type ClientAssertionGetter, type ClientAssertionGrantType, type OAuth2Tokens, type OAuth2UserInfo, type OAuthProvider, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, type PrivateKeyJwtClientAssertionGetterOptions, type PrivateKeyJwtSigningAlgorithm, type ProviderOptions, RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, type TokenEndpointAuth, type TokenEndpointAuthMethod, type TokenEndpointSecretAuthentication, additionalAuthorizationParamsSchema, applyDefaultAccessTokenExpiry, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, decodeBasicCredentials, encodeBasicCredentials, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
|
package/dist/oauth2/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId } from "./utils.mjs";
|
|
2
|
+
import { RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, createAuthorizationURL } from "./create-authorization-url.mjs";
|
|
3
|
+
import { additionalAuthorizationParamsSchema } from "./authorization-params.mjs";
|
|
4
|
+
import { decodeBasicCredentials, encodeBasicCredentials } from "./basic-credentials.mjs";
|
|
5
|
+
import { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, createPrivateKeyJwtClientAssertionGetter, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion } from "./client-assertion.mjs";
|
|
6
|
+
import { clientCredentialsToken, clientCredentialsTokenRequest } from "./client-credentials-token.mjs";
|
|
7
|
+
import { refreshAccessToken, refreshAccessTokenRequest } from "./refresh-access-token.mjs";
|
|
8
|
+
import { authorizationCodeRequest, validateAuthorizationCode, validateToken } from "./validate-authorization-code.mjs";
|
|
7
9
|
import { getJwks, verifyAccessToken, verifyJwsAccessToken } from "./verify.mjs";
|
|
8
|
-
export {
|
|
10
|
+
export { CLIENT_ASSERTION_TYPE, PRIVATE_KEY_JWT_SIGNING_ALGORITHMS, RESERVED_AUTHORIZATION_PARAMS, RESERVED_AUTHORIZATION_PARAMS_SET, additionalAuthorizationParamsSchema, applyDefaultAccessTokenExpiry, authorizationCodeRequest, clientCredentialsToken, clientCredentialsTokenRequest, createAuthorizationURL, createPrivateKeyJwtClientAssertionGetter, decodeBasicCredentials, encodeBasicCredentials, generateCodeChallenge, getJwks, getOAuth2Tokens, getPrimaryClientId, refreshAccessToken, refreshAccessTokenRequest, resolveClientAssertionParams, signPrivateKeyJwtClientAssertion, validateAuthorizationCode, validateToken, verifyAccessToken, verifyJwsAccessToken };
|
|
@@ -30,6 +30,13 @@ interface OAuthProvider<T extends Record<string, any> = Record<string, any>, O e
|
|
|
30
30
|
redirectURI: string;
|
|
31
31
|
display?: string | undefined;
|
|
32
32
|
loginHint?: string | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Extra query parameters to append to the authorization URL.
|
|
35
|
+
* Providers forward these to the shared `createAuthorizationURL` helper,
|
|
36
|
+
* which drops any keys present in `RESERVED_AUTHORIZATION_PARAMS`
|
|
37
|
+
* before applying them.
|
|
38
|
+
*/
|
|
39
|
+
additionalParams?: Record<string, string> | undefined;
|
|
33
40
|
}) => Awaitable<URL>;
|
|
34
41
|
name: string;
|
|
35
42
|
validateAuthorizationCode: (data: {
|
|
@@ -81,6 +88,17 @@ interface OAuthProvider<T extends Record<string, any> = Record<string, any>, O e
|
|
|
81
88
|
* Disable sign up for new users.
|
|
82
89
|
*/
|
|
83
90
|
disableSignUp?: boolean | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Accept callbacks that arrive without a `state` parameter. When true,
|
|
93
|
+
* the shared OAuth callback handler restarts the flow server-side with
|
|
94
|
+
* fresh `state` and PKCE instead of rejecting the request. Intended for
|
|
95
|
+
* providers that initiate OAuth without RP-side flow kickoff (e.g.
|
|
96
|
+
* Clever). Leave unset for any provider that always initiates from the
|
|
97
|
+
* RP.
|
|
98
|
+
*
|
|
99
|
+
* @default false
|
|
100
|
+
*/
|
|
101
|
+
allowIdpInitiated?: boolean | undefined;
|
|
84
102
|
/**
|
|
85
103
|
* Options for the provider
|
|
86
104
|
*/
|
|
@@ -90,9 +108,10 @@ type ProviderOptions<Profile extends Record<string, any> = any> = {
|
|
|
90
108
|
/**
|
|
91
109
|
* The client ID of your application.
|
|
92
110
|
*
|
|
93
|
-
*
|
|
111
|
+
* Some providers accept multiple platform client IDs. The first entry is the
|
|
112
|
+
* primary client ID used for token endpoint client authentication.
|
|
94
113
|
*/
|
|
95
|
-
clientId?:
|
|
114
|
+
clientId?: LiteralString | string[] | undefined;
|
|
96
115
|
/**
|
|
97
116
|
* The client secret of your application
|
|
98
117
|
*/
|
|
@@ -1,61 +1,41 @@
|
|
|
1
|
-
import { ClientAssertionConfig } from "./client-assertion.mjs";
|
|
2
1
|
import { AwaitableFunction } from "../types/helper.mjs";
|
|
3
2
|
import { OAuth2Tokens, ProviderOptions } from "./oauth-provider.mjs";
|
|
3
|
+
import { TokenEndpointAuth, TokenEndpointSecretAuthentication } from "./token-endpoint-auth.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/oauth2/refresh-access-token.d.ts
|
|
6
|
-
|
|
7
|
-
refreshToken,
|
|
8
|
-
options,
|
|
9
|
-
authentication,
|
|
10
|
-
clientAssertion,
|
|
11
|
-
tokenEndpoint,
|
|
12
|
-
extraParams,
|
|
13
|
-
resource
|
|
14
|
-
}: {
|
|
6
|
+
interface RefreshAccessTokenRequestInput {
|
|
15
7
|
refreshToken: string;
|
|
16
8
|
options: AwaitableFunction<Partial<ProviderOptions>>;
|
|
17
|
-
authentication?:
|
|
18
|
-
|
|
9
|
+
authentication?: TokenEndpointSecretAuthentication | undefined;
|
|
10
|
+
tokenEndpointAuth?: TokenEndpointAuth | undefined;
|
|
19
11
|
tokenEndpoint?: string | undefined;
|
|
20
12
|
extraParams?: Record<string, string> | undefined;
|
|
21
13
|
resource?: (string | string[]) | undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
declare function createRefreshAccessTokenRequest({
|
|
14
|
+
}
|
|
15
|
+
interface RefreshAccessTokenInput extends RefreshAccessTokenRequestInput {
|
|
16
|
+
options: Partial<ProviderOptions>;
|
|
17
|
+
tokenEndpoint: string;
|
|
18
|
+
}
|
|
19
|
+
declare function refreshAccessTokenRequest({
|
|
30
20
|
refreshToken,
|
|
31
21
|
options,
|
|
32
22
|
authentication,
|
|
23
|
+
tokenEndpointAuth,
|
|
24
|
+
tokenEndpoint,
|
|
33
25
|
extraParams,
|
|
34
26
|
resource
|
|
35
|
-
}: {
|
|
36
|
-
refreshToken: string;
|
|
37
|
-
options: ProviderOptions;
|
|
38
|
-
authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
|
|
39
|
-
extraParams?: Record<string, string> | undefined;
|
|
40
|
-
resource?: (string | string[]) | undefined;
|
|
41
|
-
}): {
|
|
27
|
+
}: RefreshAccessTokenRequestInput): Promise<{
|
|
42
28
|
body: URLSearchParams;
|
|
43
|
-
headers: Record<string,
|
|
44
|
-
}
|
|
29
|
+
headers: Record<string, string>;
|
|
30
|
+
}>;
|
|
45
31
|
declare function refreshAccessToken({
|
|
46
32
|
refreshToken,
|
|
47
33
|
options,
|
|
48
34
|
tokenEndpoint,
|
|
49
35
|
authentication,
|
|
50
|
-
|
|
51
|
-
extraParams
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
options: Partial<ProviderOptions>;
|
|
55
|
-
tokenEndpoint: string;
|
|
56
|
-
authentication?: ("basic" | "post" | "private_key_jwt") | undefined;
|
|
57
|
-
clientAssertion?: ClientAssertionConfig | undefined;
|
|
58
|
-
extraParams?: Record<string, string> | undefined;
|
|
59
|
-
}): Promise<OAuth2Tokens>;
|
|
36
|
+
tokenEndpointAuth,
|
|
37
|
+
extraParams,
|
|
38
|
+
resource
|
|
39
|
+
}: RefreshAccessTokenInput): Promise<OAuth2Tokens>;
|
|
60
40
|
//#endregion
|
|
61
|
-
export {
|
|
41
|
+
export { refreshAccessToken, refreshAccessTokenRequest };
|
|
@@ -1,33 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { base64 } from "@better-auth/utils/base64";
|
|
1
|
+
import { applyTokenEndpointAuth } from "./token-endpoint-auth.mjs";
|
|
3
2
|
import { betterFetch } from "@better-fetch/fetch";
|
|
4
3
|
//#region src/oauth2/refresh-access-token.ts
|
|
5
|
-
async function refreshAccessTokenRequest({ refreshToken, options, authentication,
|
|
4
|
+
async function refreshAccessTokenRequest({ refreshToken, options, authentication, tokenEndpointAuth, tokenEndpoint, extraParams, resource }) {
|
|
6
5
|
options = typeof options === "function" ? await options() : options;
|
|
7
|
-
|
|
8
|
-
if (!clientAssertion) throw new Error("private_key_jwt authentication requires a clientAssertion configuration");
|
|
9
|
-
const assertionParams = await resolveAssertionParams({
|
|
10
|
-
clientAssertion,
|
|
11
|
-
clientId: Array.isArray(options.clientId) ? options.clientId[0] : options.clientId,
|
|
12
|
-
tokenEndpoint
|
|
13
|
-
});
|
|
14
|
-
extraParams = {
|
|
15
|
-
...extraParams,
|
|
16
|
-
...assertionParams
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
return createRefreshAccessTokenRequest({
|
|
6
|
+
const request = buildRefreshAccessTokenRequest({
|
|
20
7
|
refreshToken,
|
|
21
8
|
options,
|
|
22
|
-
authentication,
|
|
23
9
|
extraParams,
|
|
24
10
|
resource
|
|
25
11
|
});
|
|
12
|
+
await applyTokenEndpointAuth({
|
|
13
|
+
body: request.body,
|
|
14
|
+
headers: request.headers,
|
|
15
|
+
options,
|
|
16
|
+
tokenEndpoint: tokenEndpoint ?? "",
|
|
17
|
+
grantType: "refresh_token",
|
|
18
|
+
tokenEndpointAuth,
|
|
19
|
+
authentication
|
|
20
|
+
});
|
|
21
|
+
return request;
|
|
26
22
|
}
|
|
27
|
-
|
|
28
|
-
* @deprecated use async'd refreshAccessTokenRequest instead
|
|
29
|
-
*/
|
|
30
|
-
function createRefreshAccessTokenRequest({ refreshToken, options, authentication, extraParams, resource }) {
|
|
23
|
+
function buildRefreshAccessTokenRequest({ refreshToken, options, extraParams, resource }) {
|
|
31
24
|
const body = new URLSearchParams();
|
|
32
25
|
const headers = {
|
|
33
26
|
"content-type": "application/x-www-form-urlencoded",
|
|
@@ -35,13 +28,6 @@ function createRefreshAccessTokenRequest({ refreshToken, options, authentication
|
|
|
35
28
|
};
|
|
36
29
|
body.set("grant_type", "refresh_token");
|
|
37
30
|
body.set("refresh_token", refreshToken);
|
|
38
|
-
const primaryClientId = Array.isArray(options.clientId) ? options.clientId[0] : options.clientId;
|
|
39
|
-
if (authentication === "basic") if (primaryClientId) headers["authorization"] = "Basic " + base64.encode(`${primaryClientId}:${options.clientSecret ?? ""}`);
|
|
40
|
-
else headers["authorization"] = "Basic " + base64.encode(`:${options.clientSecret ?? ""}`);
|
|
41
|
-
else {
|
|
42
|
-
body.set("client_id", primaryClientId);
|
|
43
|
-
if (authentication !== "private_key_jwt" && options.clientSecret) body.set("client_secret", options.clientSecret);
|
|
44
|
-
}
|
|
45
31
|
if (resource) if (typeof resource === "string") body.append("resource", resource);
|
|
46
32
|
else for (const _resource of resource) body.append("resource", _resource);
|
|
47
33
|
if (extraParams) for (const [key, value] of Object.entries(extraParams)) body.set(key, value);
|
|
@@ -50,14 +36,15 @@ function createRefreshAccessTokenRequest({ refreshToken, options, authentication
|
|
|
50
36
|
headers
|
|
51
37
|
};
|
|
52
38
|
}
|
|
53
|
-
async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication,
|
|
39
|
+
async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authentication, tokenEndpointAuth, extraParams, resource }) {
|
|
54
40
|
const { body, headers } = await refreshAccessTokenRequest({
|
|
55
41
|
refreshToken,
|
|
56
42
|
options,
|
|
57
43
|
authentication,
|
|
58
|
-
|
|
44
|
+
tokenEndpointAuth,
|
|
59
45
|
tokenEndpoint,
|
|
60
|
-
extraParams
|
|
46
|
+
extraParams,
|
|
47
|
+
resource
|
|
61
48
|
});
|
|
62
49
|
const { data, error } = await betterFetch(tokenEndpoint, {
|
|
63
50
|
method: "POST",
|
|
@@ -83,4 +70,4 @@ async function refreshAccessToken({ refreshToken, options, tokenEndpoint, authen
|
|
|
83
70
|
return tokens;
|
|
84
71
|
}
|
|
85
72
|
//#endregion
|
|
86
|
-
export {
|
|
73
|
+
export { refreshAccessToken, refreshAccessTokenRequest };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ClientAssertionGetter } from "./client-assertion.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/oauth2/token-endpoint-auth.d.ts
|
|
4
|
+
type TokenEndpointAuth = {
|
|
5
|
+
method: "none";
|
|
6
|
+
} | {
|
|
7
|
+
method: "client_secret_basic";
|
|
8
|
+
} | {
|
|
9
|
+
method: "client_secret_post";
|
|
10
|
+
} | {
|
|
11
|
+
method: "private_key_jwt";
|
|
12
|
+
getClientAssertion: ClientAssertionGetter;
|
|
13
|
+
};
|
|
14
|
+
type TokenEndpointAuthMethod = TokenEndpointAuth["method"];
|
|
15
|
+
type TokenEndpointSecretAuthentication = "basic" | "post";
|
|
16
|
+
//#endregion
|
|
17
|
+
export { TokenEndpointAuth, TokenEndpointAuthMethod, TokenEndpointSecretAuthentication };
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getPrimaryClientId } from "./utils.mjs";
|
|
2
|
+
import { encodeBasicCredentials } from "./basic-credentials.mjs";
|
|
3
|
+
import { resolveClientAssertionParams } from "./client-assertion.mjs";
|
|
4
|
+
//#region src/oauth2/token-endpoint-auth.ts
|
|
5
|
+
function getDefaultTokenEndpointAuth(options, authentication) {
|
|
6
|
+
if (authentication === "basic") return { method: "client_secret_basic" };
|
|
7
|
+
if (options.clientSecret) return { method: "client_secret_post" };
|
|
8
|
+
return { method: "none" };
|
|
9
|
+
}
|
|
10
|
+
function assertNoClientSecret(method, options, body) {
|
|
11
|
+
if (options.clientSecret || body.has("client_secret")) throw new Error(`${method} token endpoint authentication cannot be combined with clientSecret`);
|
|
12
|
+
}
|
|
13
|
+
function setClientId(body, clientId) {
|
|
14
|
+
if (clientId) body.set("client_id", clientId);
|
|
15
|
+
}
|
|
16
|
+
function assertClientSecretConfigured(method, options) {
|
|
17
|
+
if (!options.clientSecret) throw new Error(`${method} token endpoint authentication requires clientSecret`);
|
|
18
|
+
}
|
|
19
|
+
function assertClientIdConfigured(method, clientId) {
|
|
20
|
+
if (!clientId) throw new Error(`${method} token endpoint authentication requires clientId`);
|
|
21
|
+
}
|
|
22
|
+
function setClientSecretPostAuth({ body, options, clientId, requireClientSecret }) {
|
|
23
|
+
if (requireClientSecret) assertClientSecretConfigured("client_secret_post", options);
|
|
24
|
+
if (options.clientSecret) {
|
|
25
|
+
assertClientIdConfigured("client_secret_post", clientId);
|
|
26
|
+
setClientId(body, clientId);
|
|
27
|
+
body.set("client_secret", options.clientSecret);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function setClientSecretBasicAuth({ headers, options, clientId, body }) {
|
|
31
|
+
if (body.has("client_secret")) throw new Error("client_secret_basic token endpoint authentication cannot be combined with client_secret body parameters");
|
|
32
|
+
assertClientSecretConfigured("client_secret_basic", options);
|
|
33
|
+
assertClientIdConfigured("client_secret_basic", clientId);
|
|
34
|
+
headers.authorization = encodeBasicCredentials(clientId, options.clientSecret);
|
|
35
|
+
}
|
|
36
|
+
function assertCompleteManualClientAssertion(body) {
|
|
37
|
+
if (body.has("client_assertion") !== body.has("client_assertion_type")) throw new Error("client_assertion and client_assertion_type must both be provided");
|
|
38
|
+
}
|
|
39
|
+
async function applyTokenEndpointAuth({ body, headers, options, tokenEndpoint, grantType, tokenEndpointAuth, authentication }) {
|
|
40
|
+
assertCompleteManualClientAssertion(body);
|
|
41
|
+
const clientId = getPrimaryClientId(options.clientId);
|
|
42
|
+
if (body.has("client_assertion")) {
|
|
43
|
+
if (tokenEndpointAuth) throw new Error("client_assertion body parameters cannot be combined with tokenEndpointAuth");
|
|
44
|
+
assertNoClientSecret("private_key_jwt", options, body);
|
|
45
|
+
setClientId(body, clientId);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const auth = tokenEndpointAuth ?? getDefaultTokenEndpointAuth(options, authentication);
|
|
49
|
+
if (auth.method === "private_key_jwt") {
|
|
50
|
+
assertNoClientSecret(auth.method, options, body);
|
|
51
|
+
assertClientIdConfigured(auth.method, clientId);
|
|
52
|
+
if (!tokenEndpoint) throw new Error("private_key_jwt token endpoint authentication requires tokenEndpoint");
|
|
53
|
+
const assertionParams = await resolveClientAssertionParams({
|
|
54
|
+
getClientAssertion: auth.getClientAssertion,
|
|
55
|
+
context: {
|
|
56
|
+
clientId,
|
|
57
|
+
tokenEndpoint,
|
|
58
|
+
grantType
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
setClientId(body, clientId);
|
|
62
|
+
for (const [key, value] of Object.entries(assertionParams)) body.set(key, value);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (auth.method === "none") {
|
|
66
|
+
assertNoClientSecret(auth.method, options, body);
|
|
67
|
+
if (grantType === "client_credentials") throw new Error("none token endpoint authentication cannot be used with client_credentials grant");
|
|
68
|
+
assertClientIdConfigured(auth.method, clientId);
|
|
69
|
+
setClientId(body, clientId);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (auth.method === "client_secret_basic") {
|
|
73
|
+
setClientSecretBasicAuth({
|
|
74
|
+
headers,
|
|
75
|
+
options,
|
|
76
|
+
clientId,
|
|
77
|
+
body
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setClientSecretPostAuth({
|
|
82
|
+
body,
|
|
83
|
+
options,
|
|
84
|
+
clientId,
|
|
85
|
+
requireClientSecret: tokenEndpointAuth?.method === "client_secret_post"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
//#endregion
|
|
89
|
+
export { applyTokenEndpointAuth };
|
package/dist/oauth2/utils.d.mts
CHANGED
|
@@ -2,6 +2,14 @@ import { OAuth2Tokens } from "./oauth-provider.mjs";
|
|
|
2
2
|
|
|
3
3
|
//#region src/oauth2/utils.d.ts
|
|
4
4
|
declare function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens;
|
|
5
|
+
/**
|
|
6
|
+
* Fill in `accessTokenExpiresAt` from the provider's configured
|
|
7
|
+
* `accessTokenExpiresIn` when the token response omitted `expires_in`. Without a
|
|
8
|
+
* known expiry, `getAccessToken` cannot tell the token is expired and never
|
|
9
|
+
* refreshes it. No-op when the provider already supplied an expiry or no
|
|
10
|
+
* fallback is configured.
|
|
11
|
+
*/
|
|
12
|
+
declare function applyDefaultAccessTokenExpiry(tokens: OAuth2Tokens, accessTokenExpiresIn: number | undefined): OAuth2Tokens;
|
|
5
13
|
/**
|
|
6
14
|
* Return the provider's primary Client ID: the single string, or the entry at
|
|
7
15
|
* array index 0 for the cross-platform form used by ID token audience
|
|
@@ -13,4 +21,4 @@ declare function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens;
|
|
|
13
21
|
declare function getPrimaryClientId(clientId: unknown): string | undefined;
|
|
14
22
|
declare function generateCodeChallenge(codeVerifier: string): Promise<string>;
|
|
15
23
|
//#endregion
|
|
16
|
-
export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|
|
24
|
+
export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|
package/dist/oauth2/utils.mjs
CHANGED
|
@@ -17,6 +17,17 @@ function getOAuth2Tokens(data) {
|
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
20
|
+
* Fill in `accessTokenExpiresAt` from the provider's configured
|
|
21
|
+
* `accessTokenExpiresIn` when the token response omitted `expires_in`. Without a
|
|
22
|
+
* known expiry, `getAccessToken` cannot tell the token is expired and never
|
|
23
|
+
* refreshes it. No-op when the provider already supplied an expiry or no
|
|
24
|
+
* fallback is configured.
|
|
25
|
+
*/
|
|
26
|
+
function applyDefaultAccessTokenExpiry(tokens, accessTokenExpiresIn) {
|
|
27
|
+
if (!tokens.accessTokenExpiresAt && accessTokenExpiresIn) tokens.accessTokenExpiresAt = new Date(Date.now() + accessTokenExpiresIn * 1e3);
|
|
28
|
+
return tokens;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
20
31
|
* Return the provider's primary Client ID: the single string, or the entry at
|
|
21
32
|
* array index 0 for the cross-platform form used by ID token audience
|
|
22
33
|
* verification. Index 0 is the designated primary and pairs with
|
|
@@ -34,4 +45,4 @@ async function generateCodeChallenge(codeVerifier) {
|
|
|
34
45
|
return base64Url.encode(new Uint8Array(hash), { padding: false });
|
|
35
46
|
}
|
|
36
47
|
//#endregion
|
|
37
|
-
export { generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|
|
48
|
+
export { applyDefaultAccessTokenExpiry, generateCodeChallenge, getOAuth2Tokens, getPrimaryClientId };
|