@better-auth/core 1.7.0-beta.6 → 1.7.0-beta.8
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/api/index.d.mts +3 -3
- package/dist/context/global.mjs +1 -1
- package/dist/db/get-tables.mjs +3 -3
- package/dist/db/schema/account.d.mts +1 -1
- package/dist/db/schema/account.mjs +1 -1
- package/dist/error/codes.d.mts +0 -5
- package/dist/error/codes.mjs +0 -5
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +4 -5
- package/dist/oauth2/create-authorization-url.mjs +4 -5
- package/dist/oauth2/index.d.mts +3 -4
- package/dist/oauth2/index.mjs +2 -3
- package/dist/oauth2/oauth-provider.d.mts +44 -48
- package/dist/oauth2/refresh-access-token.mjs +17 -2
- package/dist/oauth2/utils.d.mts +6 -1
- package/dist/oauth2/utils.mjs +24 -2
- package/dist/oauth2/verify-id-token.d.mts +6 -5
- package/dist/oauth2/verify-id-token.mjs +2 -2
- package/dist/social-providers/apple.d.mts +3 -5
- package/dist/social-providers/apple.mjs +5 -5
- package/dist/social-providers/atlassian.d.mts +3 -5
- package/dist/social-providers/atlassian.mjs +4 -4
- package/dist/social-providers/cognito.d.mts +3 -5
- package/dist/social-providers/cognito.mjs +11 -18
- package/dist/social-providers/discord.d.mts +3 -5
- package/dist/social-providers/discord.mjs +6 -7
- package/dist/social-providers/dropbox.d.mts +3 -5
- package/dist/social-providers/dropbox.mjs +5 -5
- package/dist/social-providers/facebook.d.mts +3 -5
- package/dist/social-providers/facebook.mjs +5 -5
- package/dist/social-providers/figma.d.mts +3 -5
- package/dist/social-providers/figma.mjs +5 -5
- package/dist/social-providers/github.d.mts +3 -5
- package/dist/social-providers/github.mjs +4 -4
- package/dist/social-providers/gitlab.d.mts +3 -5
- package/dist/social-providers/gitlab.mjs +6 -6
- package/dist/social-providers/google.d.mts +10 -10
- package/dist/social-providers/google.mjs +12 -13
- package/dist/social-providers/huggingface.d.mts +3 -5
- package/dist/social-providers/huggingface.mjs +8 -8
- package/dist/social-providers/index.d.mts +105 -177
- package/dist/social-providers/kakao.d.mts +3 -5
- package/dist/social-providers/kakao.mjs +8 -8
- package/dist/social-providers/kick.d.mts +3 -5
- package/dist/social-providers/kick.mjs +4 -4
- package/dist/social-providers/line.d.mts +3 -5
- package/dist/social-providers/line.mjs +10 -10
- package/dist/social-providers/linear.d.mts +3 -5
- package/dist/social-providers/linear.mjs +4 -4
- package/dist/social-providers/linkedin.d.mts +3 -5
- package/dist/social-providers/linkedin.mjs +10 -10
- package/dist/social-providers/microsoft-entra-id.d.mts +3 -5
- package/dist/social-providers/microsoft-entra-id.mjs +10 -11
- package/dist/social-providers/naver.d.mts +3 -5
- package/dist/social-providers/naver.mjs +4 -4
- package/dist/social-providers/notion.d.mts +3 -5
- package/dist/social-providers/notion.mjs +4 -4
- package/dist/social-providers/paybin.d.mts +3 -5
- package/dist/social-providers/paybin.mjs +10 -10
- package/dist/social-providers/paypal.d.mts +3 -5
- package/dist/social-providers/paypal.mjs +2 -8
- package/dist/social-providers/polar.d.mts +3 -5
- package/dist/social-providers/polar.mjs +8 -8
- package/dist/social-providers/railway.d.mts +3 -5
- package/dist/social-providers/railway.mjs +9 -9
- package/dist/social-providers/reddit.d.mts +3 -5
- package/dist/social-providers/reddit.mjs +5 -5
- package/dist/social-providers/roblox.d.mts +3 -5
- package/dist/social-providers/roblox.mjs +5 -5
- package/dist/social-providers/salesforce.d.mts +3 -5
- package/dist/social-providers/salesforce.mjs +8 -8
- package/dist/social-providers/slack.d.mts +3 -5
- package/dist/social-providers/slack.mjs +9 -9
- package/dist/social-providers/spotify.d.mts +3 -5
- package/dist/social-providers/spotify.mjs +5 -5
- package/dist/social-providers/tiktok.d.mts +3 -5
- package/dist/social-providers/tiktok.mjs +5 -9
- package/dist/social-providers/twitch.d.mts +3 -5
- package/dist/social-providers/twitch.mjs +4 -4
- package/dist/social-providers/twitter.d.mts +3 -5
- package/dist/social-providers/twitter.mjs +9 -9
- package/dist/social-providers/vercel.d.mts +3 -5
- package/dist/social-providers/vercel.mjs +7 -4
- package/dist/social-providers/vk.d.mts +3 -5
- package/dist/social-providers/vk.mjs +5 -5
- package/dist/social-providers/wechat.d.mts +3 -5
- package/dist/social-providers/wechat.mjs +5 -9
- package/dist/social-providers/zoom.d.mts +3 -6
- package/dist/social-providers/zoom.mjs +9 -15
- package/dist/types/context.d.mts +6 -2
- package/dist/utils/host.d.mts +1 -1
- package/dist/utils/host.mjs +3 -0
- package/package.json +1 -1
- package/src/db/get-tables.ts +3 -8
- package/src/db/schema/account.ts +5 -14
- package/src/error/codes.ts +0 -5
- package/src/oauth2/create-authorization-url.ts +5 -1
- package/src/oauth2/index.ts +3 -12
- package/src/oauth2/oauth-provider.ts +46 -53
- package/src/oauth2/refresh-access-token.ts +30 -5
- package/src/oauth2/utils.ts +39 -1
- package/src/oauth2/verify-id-token.ts +9 -5
- package/src/social-providers/apple.ts +8 -13
- package/src/social-providers/atlassian.ts +8 -12
- package/src/social-providers/cognito.ts +11 -18
- package/src/social-providers/discord.ts +8 -19
- package/src/social-providers/dropbox.ts +7 -13
- package/src/social-providers/facebook.ts +9 -13
- package/src/social-providers/figma.ts +9 -13
- package/src/social-providers/github.ts +8 -12
- package/src/social-providers/gitlab.ts +8 -14
- package/src/social-providers/google.ts +23 -29
- package/src/social-providers/huggingface.ts +8 -12
- package/src/social-providers/kakao.ts +8 -16
- package/src/social-providers/kick.ts +7 -12
- package/src/social-providers/line.ts +10 -14
- package/src/social-providers/linear.ts +6 -12
- package/src/social-providers/linkedin.ts +10 -14
- package/src/social-providers/microsoft-entra-id.ts +8 -18
- package/src/social-providers/naver.ts +6 -12
- package/src/social-providers/notion.ts +6 -12
- package/src/social-providers/paybin.ts +11 -14
- package/src/social-providers/paypal.ts +8 -6
- package/src/social-providers/polar.ts +8 -12
- package/src/social-providers/railway.ts +9 -13
- package/src/social-providers/reddit.ts +7 -18
- package/src/social-providers/roblox.ts +7 -18
- package/src/social-providers/salesforce.ts +8 -12
- package/src/social-providers/slack.ts +9 -18
- package/src/social-providers/spotify.ts +7 -13
- package/src/social-providers/tiktok.ts +7 -13
- package/src/social-providers/twitch.ts +8 -12
- package/src/social-providers/twitter.ts +8 -17
- package/src/social-providers/vercel.ts +10 -16
- package/src/social-providers/vk.ts +7 -13
- package/src/social-providers/wechat.ts +8 -20
- package/src/social-providers/zoom.ts +6 -19
- package/src/types/context.ts +8 -2
- package/src/utils/host.ts +10 -1
- package/dist/oauth2/scopes.d.mts +0 -76
- package/dist/oauth2/scopes.mjs +0 -96
- package/src/oauth2/scopes.ts +0 -118
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
TokenEndpointSecretAuthentication,
|
|
7
7
|
} from "./token-endpoint-auth";
|
|
8
8
|
import { applyTokenEndpointAuth } from "./token-endpoint-auth";
|
|
9
|
+
import { parseScopeField } from "./utils";
|
|
9
10
|
|
|
10
11
|
interface RefreshAccessTokenRequestInput {
|
|
11
12
|
refreshToken: string;
|
|
@@ -29,6 +30,21 @@ interface RefreshAccessTokenInput extends RefreshAccessTokenRequestInput {
|
|
|
29
30
|
tokenEndpoint: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Body keys owned by the refresh-token flow or unsafe to copy from caller input.
|
|
35
|
+
*/
|
|
36
|
+
const BLOCKED_REFRESH_TOKEN_PARAMS = [
|
|
37
|
+
"grant_type",
|
|
38
|
+
"refresh_token",
|
|
39
|
+
"__proto__",
|
|
40
|
+
"constructor",
|
|
41
|
+
"prototype",
|
|
42
|
+
] as const;
|
|
43
|
+
|
|
44
|
+
const BLOCKED_REFRESH_TOKEN_PARAMS_SET: ReadonlySet<string> = new Set(
|
|
45
|
+
BLOCKED_REFRESH_TOKEN_PARAMS,
|
|
46
|
+
);
|
|
47
|
+
|
|
32
48
|
export async function refreshAccessTokenRequest({
|
|
33
49
|
refreshToken,
|
|
34
50
|
options,
|
|
@@ -59,6 +75,17 @@ export async function refreshAccessTokenRequest({
|
|
|
59
75
|
return request;
|
|
60
76
|
}
|
|
61
77
|
|
|
78
|
+
function applyRefreshExtraParams(
|
|
79
|
+
body: URLSearchParams,
|
|
80
|
+
extraParams: Record<string, string> | undefined,
|
|
81
|
+
) {
|
|
82
|
+
if (!extraParams) return;
|
|
83
|
+
for (const [key, value] of Object.entries(extraParams)) {
|
|
84
|
+
if (BLOCKED_REFRESH_TOKEN_PARAMS_SET.has(key)) continue;
|
|
85
|
+
body.set(key, value);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
function buildRefreshAccessTokenRequest({
|
|
63
90
|
refreshToken,
|
|
64
91
|
options,
|
|
@@ -83,9 +110,7 @@ function buildRefreshAccessTokenRequest({
|
|
|
83
110
|
}
|
|
84
111
|
}
|
|
85
112
|
if (extraParams) {
|
|
86
|
-
|
|
87
|
-
body.set(key, value);
|
|
88
|
-
}
|
|
113
|
+
applyRefreshExtraParams(body, extraParams);
|
|
89
114
|
}
|
|
90
115
|
|
|
91
116
|
return {
|
|
@@ -119,7 +144,7 @@ export async function refreshAccessToken({
|
|
|
119
144
|
expires_in?: number | undefined;
|
|
120
145
|
refresh_token_expires_in?: number | undefined;
|
|
121
146
|
token_type?: string | undefined;
|
|
122
|
-
scope?:
|
|
147
|
+
scope?: unknown;
|
|
123
148
|
id_token?: string | undefined;
|
|
124
149
|
}>(tokenEndpoint, {
|
|
125
150
|
method: "POST",
|
|
@@ -133,7 +158,7 @@ export async function refreshAccessToken({
|
|
|
133
158
|
accessToken: data.access_token,
|
|
134
159
|
refreshToken: data.refresh_token,
|
|
135
160
|
tokenType: data.token_type,
|
|
136
|
-
scopes:
|
|
161
|
+
scopes: parseScopeField(data.scope),
|
|
137
162
|
idToken: data.id_token,
|
|
138
163
|
};
|
|
139
164
|
|
package/src/oauth2/utils.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { base64Url } from "@better-auth/utils/base64";
|
|
2
2
|
import type { OAuth2Tokens } from "./oauth-provider";
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parse a provider's `scope` token-response field into a string array.
|
|
6
|
+
*
|
|
7
|
+
* RFC 6749 Section 3.3 defines `scope` as a space-delimited string, but
|
|
8
|
+
* providers vary: some return an already-split array. Accept both forms and
|
|
9
|
+
* drop empty or non-string entries.
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/better-auth/better-auth/issues/9076
|
|
12
|
+
*/
|
|
13
|
+
export function parseScopeField(scope: unknown): string[] {
|
|
14
|
+
if (Array.isArray(scope)) {
|
|
15
|
+
return scope
|
|
16
|
+
.map((s) => (typeof s === "string" ? s.trim() : ""))
|
|
17
|
+
.filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
if (typeof scope === "string") {
|
|
20
|
+
return scope.trim().split(/\s+/).filter(Boolean);
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
4
24
|
|
|
5
25
|
export function getOAuth2Tokens(data: Record<string, any>): OAuth2Tokens {
|
|
6
26
|
const getDate = (seconds: number) => {
|
|
@@ -44,6 +64,24 @@ export function applyDefaultAccessTokenExpiry(
|
|
|
44
64
|
return tokens;
|
|
45
65
|
}
|
|
46
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Compute the union of stored and incoming OAuth scopes, preserving
|
|
69
|
+
* stored insertion order and dropping duplicates.
|
|
70
|
+
*/
|
|
71
|
+
export function mergeScopes(
|
|
72
|
+
stored: string | null | undefined,
|
|
73
|
+
incoming: string[] | undefined,
|
|
74
|
+
): string {
|
|
75
|
+
const existing = stored
|
|
76
|
+
? stored
|
|
77
|
+
.split(",")
|
|
78
|
+
.map((scope) => scope.trim())
|
|
79
|
+
.filter(Boolean)
|
|
80
|
+
: [];
|
|
81
|
+
const next = (incoming ?? []).map((scope) => scope.trim()).filter(Boolean);
|
|
82
|
+
return [...new Set([...existing, ...next])].join(",");
|
|
83
|
+
}
|
|
84
|
+
|
|
47
85
|
/**
|
|
48
86
|
* Return the provider's primary Client ID: the single string, or the entry at
|
|
49
87
|
* array index 0 for the cross-platform form used by ID token audience
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { decodeProtectedHeader, jwtVerify } from "jose";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "./oauth-provider";
|
|
3
|
+
|
|
4
|
+
type ProviderWithIdTokenConfig = Pick<OAuthProvider, "idToken" | "options">;
|
|
3
5
|
|
|
4
6
|
async function sha256Hex(value: string) {
|
|
5
7
|
const data = new TextEncoder().encode(value);
|
|
@@ -29,12 +31,12 @@ async function nonceMatches(
|
|
|
29
31
|
/**
|
|
30
32
|
* Whether a provider can verify a client-submitted id_token.
|
|
31
33
|
*
|
|
32
|
-
* A provider supports id_token sign-in when it declares an {@link
|
|
34
|
+
* A provider supports id_token sign-in when it declares an {@link OAuthProvider.idToken}
|
|
33
35
|
* verification config, or when the integrator supplies a `verifyIdToken` override on the
|
|
34
36
|
* provider options. A provider whose options set `disableIdTokenSignIn`, or that declares
|
|
35
37
|
* neither, rejects the client id_token sign-in path with `ID_TOKEN_NOT_SUPPORTED`.
|
|
36
38
|
*/
|
|
37
|
-
export function supportsIdTokenSignIn(provider:
|
|
39
|
+
export function supportsIdTokenSignIn(provider: ProviderWithIdTokenConfig) {
|
|
38
40
|
const options = (provider.options ?? {}) as Partial<ProviderOptions>;
|
|
39
41
|
if (options.disableIdTokenSignIn) {
|
|
40
42
|
return false;
|
|
@@ -46,7 +48,7 @@ export function supportsIdTokenSignIn(provider: UpstreamProvider<any, any>) {
|
|
|
46
48
|
* Verify a client-submitted id_token against a provider's verification config.
|
|
47
49
|
*
|
|
48
50
|
* This is the single id_token verifier for every social provider. Providers no longer
|
|
49
|
-
* implement their own boolean `verifyIdToken`; they declare an {@link
|
|
51
|
+
* implement their own boolean `verifyIdToken`; they declare an {@link OAuthProvider.idToken}
|
|
50
52
|
* config and this function performs the cryptographic check. The contract is fail-closed: a
|
|
51
53
|
* provider without a config (and without an integrator `verifyIdToken` override) returns
|
|
52
54
|
* `false`, so a forged token can never be accepted by omission.
|
|
@@ -54,7 +56,7 @@ export function supportsIdTokenSignIn(provider: UpstreamProvider<any, any>) {
|
|
|
54
56
|
* @returns `true` only when the token is authentic for the provider.
|
|
55
57
|
*/
|
|
56
58
|
export async function verifyProviderIdToken(
|
|
57
|
-
provider:
|
|
59
|
+
provider: ProviderWithIdTokenConfig,
|
|
58
60
|
token: string,
|
|
59
61
|
nonce?: string,
|
|
60
62
|
): Promise<boolean> {
|
|
@@ -79,6 +81,8 @@ export async function verifyProviderIdToken(
|
|
|
79
81
|
// Opaque (non-JWS) tokens carry no signature to check. They are accepted only when the
|
|
80
82
|
// provider opts in, in which case getUserInfo resolves identity from the access token via
|
|
81
83
|
// the provider's userinfo endpoint, which validates it (e.g. Facebook Graph access tokens).
|
|
84
|
+
// An expected `nonce` is not enforced here: an opaque token carries no `nonce` claim, and the
|
|
85
|
+
// access-token-backed userinfo exchange (not the token itself) is the identity source.
|
|
82
86
|
if (token.split(".").length !== 3) {
|
|
83
87
|
return config.allowOpaqueToken === true;
|
|
84
88
|
}
|
|
@@ -3,12 +3,11 @@ import { betterFetch } from "@better-fetch/fetch";
|
|
|
3
3
|
import { decodeJwt, importJWK } from "jose";
|
|
4
4
|
import { logger } from "../env";
|
|
5
5
|
import { APIError, BetterAuthError } from "../error";
|
|
6
|
-
import type {
|
|
6
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
7
7
|
import {
|
|
8
8
|
createAuthorizationURL,
|
|
9
9
|
getPrimaryClientId,
|
|
10
10
|
refreshAccessToken,
|
|
11
|
-
resolveRequestedScopes,
|
|
12
11
|
validateAuthorizationCode,
|
|
13
12
|
} from "../oauth2";
|
|
14
13
|
export interface AppleProfile {
|
|
@@ -78,14 +77,11 @@ export interface AppleOptions extends ProviderOptions<AppleProfile> {
|
|
|
78
77
|
audience?: (string | string[]) | undefined;
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
const APPLE_DEFAULT_SCOPES = ["email", "name"];
|
|
82
|
-
|
|
83
80
|
export const apple = (options: AppleOptions) => {
|
|
84
81
|
const tokenEndpoint = "https://appleid.apple.com/auth/token";
|
|
85
82
|
return {
|
|
86
83
|
id: "apple",
|
|
87
84
|
name: "Apple",
|
|
88
|
-
callbackPath: "/callback/apple",
|
|
89
85
|
async createAuthorizationURL({
|
|
90
86
|
state,
|
|
91
87
|
scopes,
|
|
@@ -98,22 +94,21 @@ export const apple = (options: AppleOptions) => {
|
|
|
98
94
|
);
|
|
99
95
|
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
100
96
|
}
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
return createAuthorizationURL({
|
|
97
|
+
const _scope = options.disableDefaultScope ? [] : ["email", "name"];
|
|
98
|
+
if (options.scope) _scope.push(...options.scope);
|
|
99
|
+
if (scopes) _scope.push(...scopes);
|
|
100
|
+
const url = await createAuthorizationURL({
|
|
107
101
|
id: "apple",
|
|
108
102
|
options,
|
|
109
103
|
authorizationEndpoint: "https://appleid.apple.com/auth/authorize",
|
|
110
|
-
scopes:
|
|
104
|
+
scopes: _scope,
|
|
111
105
|
state,
|
|
112
106
|
redirectURI,
|
|
113
107
|
responseMode: "form_post",
|
|
114
108
|
responseType: "code id_token",
|
|
115
109
|
additionalParams,
|
|
116
110
|
});
|
|
111
|
+
return url;
|
|
117
112
|
},
|
|
118
113
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
119
114
|
return validateAuthorizationCode({
|
|
@@ -189,7 +184,7 @@ export const apple = (options: AppleOptions) => {
|
|
|
189
184
|
};
|
|
190
185
|
},
|
|
191
186
|
options,
|
|
192
|
-
} satisfies
|
|
187
|
+
} satisfies OAuthProvider<AppleProfile>;
|
|
193
188
|
};
|
|
194
189
|
|
|
195
190
|
export const getApplePublicKey = async (kid: string) => {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { logger } from "../env";
|
|
3
3
|
import { BetterAuthError } from "../error";
|
|
4
|
-
import type {
|
|
4
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
5
5
|
import {
|
|
6
6
|
createAuthorizationURL,
|
|
7
7
|
refreshAccessToken,
|
|
8
|
-
resolveRequestedScopes,
|
|
9
8
|
validateAuthorizationCode,
|
|
10
9
|
} from "../oauth2";
|
|
11
10
|
|
|
@@ -30,14 +29,11 @@ export interface AtlassianOptions extends ProviderOptions<AtlassianProfile> {
|
|
|
30
29
|
clientId: string;
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
const ATLASSIAN_DEFAULT_SCOPES = ["read:jira-user", "offline_access"];
|
|
34
|
-
|
|
35
32
|
export const atlassian = (options: AtlassianOptions) => {
|
|
36
33
|
const tokenEndpoint = "https://auth.atlassian.com/oauth/token";
|
|
37
34
|
return {
|
|
38
35
|
id: "atlassian",
|
|
39
36
|
name: "Atlassian",
|
|
40
|
-
callbackPath: "/callback/atlassian",
|
|
41
37
|
|
|
42
38
|
async createAuthorizationURL({
|
|
43
39
|
state,
|
|
@@ -54,17 +50,17 @@ export const atlassian = (options: AtlassianOptions) => {
|
|
|
54
50
|
throw new BetterAuthError("codeVerifier is required for Atlassian");
|
|
55
51
|
}
|
|
56
52
|
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
);
|
|
53
|
+
const _scopes = options.disableDefaultScope
|
|
54
|
+
? []
|
|
55
|
+
: ["read:jira-user", "offline_access"];
|
|
56
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
57
|
+
if (scopes) _scopes.push(...scopes);
|
|
62
58
|
|
|
63
59
|
return createAuthorizationURL({
|
|
64
60
|
id: "atlassian",
|
|
65
61
|
options,
|
|
66
62
|
authorizationEndpoint: "https://auth.atlassian.com/authorize",
|
|
67
|
-
scopes:
|
|
63
|
+
scopes: _scopes,
|
|
68
64
|
state,
|
|
69
65
|
codeVerifier,
|
|
70
66
|
redirectURI,
|
|
@@ -140,5 +136,5 @@ export const atlassian = (options: AtlassianOptions) => {
|
|
|
140
136
|
},
|
|
141
137
|
|
|
142
138
|
options,
|
|
143
|
-
} satisfies
|
|
139
|
+
} satisfies OAuthProvider<AtlassianProfile>;
|
|
144
140
|
};
|
|
@@ -2,12 +2,11 @@ import { betterFetch } from "@better-fetch/fetch";
|
|
|
2
2
|
import { decodeJwt, importJWK } from "jose";
|
|
3
3
|
import { logger } from "../env";
|
|
4
4
|
import { APIError, BetterAuthError } from "../error";
|
|
5
|
-
import type {
|
|
5
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
6
6
|
import {
|
|
7
7
|
createAuthorizationURL,
|
|
8
8
|
getPrimaryClientId,
|
|
9
9
|
refreshAccessToken,
|
|
10
|
-
resolveRequestedScopes,
|
|
11
10
|
validateAuthorizationCode,
|
|
12
11
|
} from "../oauth2";
|
|
13
12
|
|
|
@@ -58,8 +57,6 @@ export interface CognitoOptions extends ProviderOptions<CognitoProfile> {
|
|
|
58
57
|
identityProvider?: string | undefined;
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
const COGNITO_DEFAULT_SCOPES = ["openid", "profile", "email"];
|
|
62
|
-
|
|
63
60
|
export const cognito = (options: CognitoOptions) => {
|
|
64
61
|
if (!options.domain || !options.region || !options.userPoolId) {
|
|
65
62
|
logger.error(
|
|
@@ -76,7 +73,6 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
76
73
|
return {
|
|
77
74
|
id: "cognito",
|
|
78
75
|
name: "Cognito",
|
|
79
|
-
callbackPath: "/callback/cognito",
|
|
80
76
|
async createAuthorizationURL({
|
|
81
77
|
state,
|
|
82
78
|
scopes,
|
|
@@ -97,19 +93,19 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
97
93
|
);
|
|
98
94
|
throw new BetterAuthError("CLIENT_SECRET_REQUIRED");
|
|
99
95
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
);
|
|
96
|
+
const _scopes = options.disableDefaultScope
|
|
97
|
+
? []
|
|
98
|
+
: ["openid", "profile", "email"];
|
|
99
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
100
|
+
if (scopes) _scopes.push(...scopes);
|
|
105
101
|
|
|
106
|
-
const
|
|
102
|
+
const url = await createAuthorizationURL({
|
|
107
103
|
id: "cognito",
|
|
108
104
|
options: {
|
|
109
105
|
...options,
|
|
110
106
|
},
|
|
111
107
|
authorizationEndpoint,
|
|
112
|
-
scopes:
|
|
108
|
+
scopes: _scopes,
|
|
113
109
|
state,
|
|
114
110
|
codeVerifier,
|
|
115
111
|
redirectURI,
|
|
@@ -130,12 +126,9 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
130
126
|
// Manually append the scope with proper encoding to the URL
|
|
131
127
|
const urlString = url.toString();
|
|
132
128
|
const separator = urlString.includes("?") ? "&" : "?";
|
|
133
|
-
return {
|
|
134
|
-
url: new URL(`${urlString}${separator}scope=${encodedScope}`),
|
|
135
|
-
requestedScopes,
|
|
136
|
-
};
|
|
129
|
+
return new URL(`${urlString}${separator}scope=${encodedScope}`);
|
|
137
130
|
}
|
|
138
|
-
return
|
|
131
|
+
return url;
|
|
139
132
|
},
|
|
140
133
|
|
|
141
134
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -243,7 +236,7 @@ export const cognito = (options: CognitoOptions) => {
|
|
|
243
236
|
},
|
|
244
237
|
|
|
245
238
|
options,
|
|
246
|
-
} satisfies
|
|
239
|
+
} satisfies OAuthProvider<CognitoProfile>;
|
|
247
240
|
};
|
|
248
241
|
|
|
249
242
|
export const getCognitoPublicKey = async (
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
-
resolveRequestedScopes,
|
|
7
6
|
validateAuthorizationCode,
|
|
8
7
|
} from "../oauth2";
|
|
9
8
|
export interface DiscordProfile extends Record<string, any> {
|
|
@@ -84,31 +83,21 @@ export interface DiscordOptions extends ProviderOptions<DiscordProfile> {
|
|
|
84
83
|
permissions?: number | undefined;
|
|
85
84
|
}
|
|
86
85
|
|
|
87
|
-
const DISCORD_DEFAULT_SCOPES = ["identify", "email"];
|
|
88
|
-
|
|
89
86
|
export const discord = (options: DiscordOptions) => {
|
|
90
87
|
const tokenEndpoint = "https://discord.com/api/oauth2/token";
|
|
91
88
|
return {
|
|
92
89
|
id: "discord",
|
|
93
90
|
name: "Discord",
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
additionalParams,
|
|
100
|
-
}) {
|
|
101
|
-
const requestedScopes = resolveRequestedScopes(
|
|
102
|
-
options,
|
|
103
|
-
DISCORD_DEFAULT_SCOPES,
|
|
104
|
-
scopes,
|
|
105
|
-
);
|
|
106
|
-
const hasBotScope = requestedScopes.includes("bot");
|
|
91
|
+
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
92
|
+
const _scopes = options.disableDefaultScope ? [] : ["identify", "email"];
|
|
93
|
+
if (scopes) _scopes.push(...scopes);
|
|
94
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
95
|
+
const hasBotScope = _scopes.includes("bot");
|
|
107
96
|
return createAuthorizationURL({
|
|
108
97
|
id: "discord",
|
|
109
98
|
options,
|
|
110
99
|
authorizationEndpoint: "https://discord.com/api/oauth2/authorize",
|
|
111
|
-
scopes:
|
|
100
|
+
scopes: _scopes,
|
|
112
101
|
state,
|
|
113
102
|
redirectURI,
|
|
114
103
|
prompt: options.prompt || "none",
|
|
@@ -181,5 +170,5 @@ export const discord = (options: DiscordOptions) => {
|
|
|
181
170
|
};
|
|
182
171
|
},
|
|
183
172
|
options,
|
|
184
|
-
} satisfies
|
|
173
|
+
} satisfies OAuthProvider<DiscordProfile>;
|
|
185
174
|
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
-
resolveRequestedScopes,
|
|
7
6
|
validateAuthorizationCode,
|
|
8
7
|
} from "../oauth2";
|
|
9
8
|
|
|
@@ -26,15 +25,12 @@ export interface DropboxOptions extends ProviderOptions<DropboxProfile> {
|
|
|
26
25
|
accessType?: ("offline" | "online" | "legacy") | undefined;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
const DROPBOX_DEFAULT_SCOPES = ["account_info.read"];
|
|
30
|
-
|
|
31
28
|
export const dropbox = (options: DropboxOptions) => {
|
|
32
29
|
const tokenEndpoint = "https://api.dropboxapi.com/oauth2/token";
|
|
33
30
|
|
|
34
31
|
return {
|
|
35
32
|
id: "dropbox",
|
|
36
33
|
name: "Dropbox",
|
|
37
|
-
callbackPath: "/callback/dropbox",
|
|
38
34
|
createAuthorizationURL: async ({
|
|
39
35
|
state,
|
|
40
36
|
scopes,
|
|
@@ -42,16 +38,14 @@ export const dropbox = (options: DropboxOptions) => {
|
|
|
42
38
|
redirectURI,
|
|
43
39
|
additionalParams,
|
|
44
40
|
}) => {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
return createAuthorizationURL({
|
|
41
|
+
const _scopes = options.disableDefaultScope ? [] : ["account_info.read"];
|
|
42
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
43
|
+
if (scopes) _scopes.push(...scopes);
|
|
44
|
+
return await createAuthorizationURL({
|
|
51
45
|
id: "dropbox",
|
|
52
46
|
options,
|
|
53
47
|
authorizationEndpoint: "https://www.dropbox.com/oauth2/authorize",
|
|
54
|
-
scopes:
|
|
48
|
+
scopes: _scopes,
|
|
55
49
|
state,
|
|
56
50
|
redirectURI,
|
|
57
51
|
codeVerifier,
|
|
@@ -116,5 +110,5 @@ export const dropbox = (options: DropboxOptions) => {
|
|
|
116
110
|
};
|
|
117
111
|
},
|
|
118
112
|
options,
|
|
119
|
-
} satisfies
|
|
113
|
+
} satisfies OAuthProvider<DropboxProfile>;
|
|
120
114
|
};
|
|
@@ -2,12 +2,11 @@ import { betterFetch } from "@better-fetch/fetch";
|
|
|
2
2
|
import { createRemoteJWKSet, decodeJwt } from "jose";
|
|
3
3
|
import { logger } from "../env";
|
|
4
4
|
import { BetterAuthError } from "../error";
|
|
5
|
-
import type {
|
|
5
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
6
6
|
import {
|
|
7
7
|
createAuthorizationURL,
|
|
8
8
|
getPrimaryClientId,
|
|
9
9
|
refreshAccessToken,
|
|
10
|
-
resolveRequestedScopes,
|
|
11
10
|
validateAuthorizationCode,
|
|
12
11
|
} from "../oauth2";
|
|
13
12
|
export interface FacebookProfile {
|
|
@@ -92,13 +91,10 @@ export interface FacebookOptions extends ProviderOptions<FacebookProfile> {
|
|
|
92
91
|
configId?: string | undefined;
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
const FACEBOOK_DEFAULT_SCOPES = ["email", "public_profile"];
|
|
96
|
-
|
|
97
94
|
export const facebook = (options: FacebookOptions) => {
|
|
98
95
|
return {
|
|
99
96
|
id: "facebook",
|
|
100
97
|
name: "Facebook",
|
|
101
|
-
callbackPath: "/callback/facebook",
|
|
102
98
|
async createAuthorizationURL({
|
|
103
99
|
state,
|
|
104
100
|
scopes,
|
|
@@ -112,16 +108,16 @@ export const facebook = (options: FacebookOptions) => {
|
|
|
112
108
|
);
|
|
113
109
|
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
114
110
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
return createAuthorizationURL({
|
|
111
|
+
const _scopes = options.disableDefaultScope
|
|
112
|
+
? []
|
|
113
|
+
: ["email", "public_profile"];
|
|
114
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
115
|
+
if (scopes) _scopes.push(...scopes);
|
|
116
|
+
return await createAuthorizationURL({
|
|
121
117
|
id: "facebook",
|
|
122
118
|
options,
|
|
123
119
|
authorizationEndpoint: "https://www.facebook.com/v24.0/dialog/oauth",
|
|
124
|
-
scopes:
|
|
120
|
+
scopes: _scopes,
|
|
125
121
|
state,
|
|
126
122
|
redirectURI,
|
|
127
123
|
loginHint,
|
|
@@ -262,5 +258,5 @@ export const facebook = (options: FacebookOptions) => {
|
|
|
262
258
|
};
|
|
263
259
|
},
|
|
264
260
|
options,
|
|
265
|
-
} satisfies
|
|
261
|
+
} satisfies OAuthProvider<FacebookProfile>;
|
|
266
262
|
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { logger } from "../env";
|
|
3
3
|
import { BetterAuthError } from "../error";
|
|
4
|
-
import type {
|
|
4
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
5
5
|
import {
|
|
6
6
|
createAuthorizationURL,
|
|
7
7
|
refreshAccessToken,
|
|
8
|
-
resolveRequestedScopes,
|
|
9
8
|
validateAuthorizationCode,
|
|
10
9
|
} from "../oauth2";
|
|
11
10
|
|
|
@@ -20,14 +19,11 @@ export interface FigmaOptions extends ProviderOptions<FigmaProfile> {
|
|
|
20
19
|
clientId: string;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
const FIGMA_DEFAULT_SCOPES = ["current_user:read"];
|
|
24
|
-
|
|
25
22
|
export const figma = (options: FigmaOptions) => {
|
|
26
23
|
const tokenEndpoint = "https://api.figma.com/v1/oauth/token";
|
|
27
24
|
return {
|
|
28
25
|
id: "figma",
|
|
29
26
|
name: "Figma",
|
|
30
|
-
callbackPath: "/callback/figma",
|
|
31
27
|
async createAuthorizationURL({
|
|
32
28
|
state,
|
|
33
29
|
scopes,
|
|
@@ -45,22 +41,22 @@ export const figma = (options: FigmaOptions) => {
|
|
|
45
41
|
throw new BetterAuthError("codeVerifier is required for Figma");
|
|
46
42
|
}
|
|
47
43
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
scopes,
|
|
52
|
-
);
|
|
44
|
+
const _scopes = options.disableDefaultScope ? [] : ["current_user:read"];
|
|
45
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
46
|
+
if (scopes) _scopes.push(...scopes);
|
|
53
47
|
|
|
54
|
-
|
|
48
|
+
const url = await createAuthorizationURL({
|
|
55
49
|
id: "figma",
|
|
56
50
|
options,
|
|
57
51
|
authorizationEndpoint: "https://www.figma.com/oauth",
|
|
58
|
-
scopes:
|
|
52
|
+
scopes: _scopes,
|
|
59
53
|
state,
|
|
60
54
|
codeVerifier,
|
|
61
55
|
redirectURI,
|
|
62
56
|
additionalParams,
|
|
63
57
|
});
|
|
58
|
+
|
|
59
|
+
return url;
|
|
64
60
|
},
|
|
65
61
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
66
62
|
return validateAuthorizationCode({
|
|
@@ -125,5 +121,5 @@ export const figma = (options: FigmaOptions) => {
|
|
|
125
121
|
}
|
|
126
122
|
},
|
|
127
123
|
options,
|
|
128
|
-
} satisfies
|
|
124
|
+
} satisfies OAuthProvider<FigmaProfile>;
|
|
129
125
|
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { logger } from "../env";
|
|
3
|
-
import type {
|
|
3
|
+
import type { OAuthProvider, ProviderOptions } from "../oauth2";
|
|
4
4
|
import {
|
|
5
5
|
createAuthorizationURL,
|
|
6
6
|
getOAuth2Tokens,
|
|
7
7
|
refreshAccessToken,
|
|
8
|
-
resolveRequestedScopes,
|
|
9
8
|
} from "../oauth2";
|
|
10
9
|
import { authorizationCodeRequest } from "../oauth2/validate-authorization-code";
|
|
11
10
|
|
|
@@ -59,14 +58,11 @@ export interface GithubProfile {
|
|
|
59
58
|
export interface GithubOptions extends ProviderOptions<GithubProfile> {
|
|
60
59
|
clientId: string;
|
|
61
60
|
}
|
|
62
|
-
const GITHUB_DEFAULT_SCOPES = ["read:user", "user:email"];
|
|
63
|
-
|
|
64
61
|
export const github = (options: GithubOptions) => {
|
|
65
62
|
const tokenEndpoint = "https://github.com/login/oauth/access_token";
|
|
66
63
|
return {
|
|
67
64
|
id: "github",
|
|
68
65
|
name: "GitHub",
|
|
69
|
-
callbackPath: "/callback/github",
|
|
70
66
|
createAuthorizationURL({
|
|
71
67
|
state,
|
|
72
68
|
scopes,
|
|
@@ -75,16 +71,16 @@ export const github = (options: GithubOptions) => {
|
|
|
75
71
|
redirectURI,
|
|
76
72
|
additionalParams,
|
|
77
73
|
}) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
74
|
+
const _scopes = options.disableDefaultScope
|
|
75
|
+
? []
|
|
76
|
+
: ["read:user", "user:email"];
|
|
77
|
+
if (options.scope) _scopes.push(...options.scope);
|
|
78
|
+
if (scopes) _scopes.push(...scopes);
|
|
83
79
|
return createAuthorizationURL({
|
|
84
80
|
id: "github",
|
|
85
81
|
options,
|
|
86
82
|
authorizationEndpoint: "https://github.com/login/oauth/authorize",
|
|
87
|
-
scopes:
|
|
83
|
+
scopes: _scopes,
|
|
88
84
|
state,
|
|
89
85
|
codeVerifier,
|
|
90
86
|
redirectURI,
|
|
@@ -186,5 +182,5 @@ export const github = (options: GithubOptions) => {
|
|
|
186
182
|
};
|
|
187
183
|
},
|
|
188
184
|
options,
|
|
189
|
-
} satisfies
|
|
185
|
+
} satisfies OAuthProvider<GithubProfile>;
|
|
190
186
|
};
|