@better-auth/core 1.7.0-beta.4 → 1.7.0-beta.6
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 +47 -4
- package/dist/api/index.mjs +40 -1
- package/dist/context/global.mjs +1 -1
- package/dist/context/transaction.d.mts +7 -4
- package/dist/context/transaction.mjs +6 -3
- package/dist/db/adapter/factory.mjs +57 -31
- package/dist/db/adapter/index.d.mts +54 -10
- package/dist/db/adapter/types.d.mts +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/db/type.d.mts +12 -7
- package/dist/env/env-impl.mjs +1 -1
- package/dist/error/codes.d.mts +5 -0
- package/dist/error/codes.mjs +5 -0
- package/dist/index.d.mts +2 -2
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +4 -1
- package/dist/oauth2/create-authorization-url.mjs +5 -2
- package/dist/oauth2/dpop.d.mts +142 -0
- package/dist/oauth2/dpop.mjs +246 -0
- package/dist/oauth2/index.d.mts +6 -3
- package/dist/oauth2/index.mjs +5 -2
- package/dist/oauth2/oauth-provider.d.mts +128 -9
- package/dist/oauth2/refresh-access-token.mjs +1 -1
- package/dist/oauth2/scopes.d.mts +76 -0
- package/dist/oauth2/scopes.mjs +96 -0
- package/dist/oauth2/utils.mjs +2 -1
- package/dist/oauth2/verify-id-token.d.mts +26 -0
- package/dist/oauth2/verify-id-token.mjs +62 -0
- package/dist/oauth2/verify.d.mts +88 -15
- package/dist/oauth2/verify.mjs +187 -19
- package/dist/social-providers/apple.d.mts +14 -2
- package/dist/social-providers/apple.mjs +12 -36
- package/dist/social-providers/atlassian.d.mts +5 -1
- package/dist/social-providers/atlassian.mjs +4 -4
- package/dist/social-providers/cognito.d.mts +13 -2
- package/dist/social-providers/cognito.mjs +24 -32
- package/dist/social-providers/discord.d.mts +5 -1
- package/dist/social-providers/discord.mjs +7 -6
- package/dist/social-providers/dropbox.d.mts +5 -1
- package/dist/social-providers/dropbox.mjs +5 -5
- package/dist/social-providers/facebook.d.mts +21 -2
- package/dist/social-providers/facebook.mjs +46 -22
- package/dist/social-providers/figma.d.mts +5 -1
- package/dist/social-providers/figma.mjs +5 -5
- package/dist/social-providers/github.d.mts +5 -1
- package/dist/social-providers/github.mjs +4 -4
- package/dist/social-providers/gitlab.d.mts +5 -1
- package/dist/social-providers/gitlab.mjs +6 -6
- package/dist/social-providers/google.d.mts +29 -3
- package/dist/social-providers/google.mjs +24 -30
- package/dist/social-providers/huggingface.d.mts +5 -1
- package/dist/social-providers/huggingface.mjs +8 -8
- package/dist/social-providers/index.d.mts +222 -42
- package/dist/social-providers/kakao.d.mts +5 -1
- package/dist/social-providers/kakao.mjs +8 -8
- package/dist/social-providers/kick.d.mts +5 -1
- package/dist/social-providers/kick.mjs +4 -4
- package/dist/social-providers/line.d.mts +8 -2
- package/dist/social-providers/line.mjs +12 -14
- package/dist/social-providers/linear.d.mts +5 -1
- package/dist/social-providers/linear.mjs +4 -4
- package/dist/social-providers/linkedin.d.mts +5 -1
- package/dist/social-providers/linkedin.mjs +10 -10
- package/dist/social-providers/microsoft-entra-id.d.mts +41 -6
- package/dist/social-providers/microsoft-entra-id.mjs +40 -36
- package/dist/social-providers/naver.d.mts +5 -1
- package/dist/social-providers/naver.mjs +4 -4
- package/dist/social-providers/notion.d.mts +5 -1
- package/dist/social-providers/notion.mjs +4 -4
- package/dist/social-providers/paybin.d.mts +5 -1
- package/dist/social-providers/paybin.mjs +10 -10
- package/dist/social-providers/paypal.d.mts +5 -2
- package/dist/social-providers/paypal.mjs +8 -13
- package/dist/social-providers/polar.d.mts +5 -1
- package/dist/social-providers/polar.mjs +8 -8
- package/dist/social-providers/railway.d.mts +5 -1
- package/dist/social-providers/railway.mjs +9 -9
- package/dist/social-providers/reddit.d.mts +5 -1
- package/dist/social-providers/reddit.mjs +9 -8
- package/dist/social-providers/roblox.d.mts +5 -1
- package/dist/social-providers/roblox.mjs +5 -5
- package/dist/social-providers/salesforce.d.mts +5 -1
- package/dist/social-providers/salesforce.mjs +8 -8
- package/dist/social-providers/slack.d.mts +5 -1
- package/dist/social-providers/slack.mjs +9 -9
- package/dist/social-providers/spotify.d.mts +5 -1
- package/dist/social-providers/spotify.mjs +5 -5
- package/dist/social-providers/tiktok.d.mts +5 -1
- package/dist/social-providers/tiktok.mjs +9 -5
- package/dist/social-providers/twitch.d.mts +5 -1
- package/dist/social-providers/twitch.mjs +4 -4
- package/dist/social-providers/twitter.d.mts +6 -4
- package/dist/social-providers/twitter.mjs +9 -9
- package/dist/social-providers/vercel.d.mts +5 -1
- package/dist/social-providers/vercel.mjs +4 -7
- package/dist/social-providers/vk.d.mts +5 -1
- package/dist/social-providers/vk.mjs +5 -5
- package/dist/social-providers/wechat.d.mts +5 -1
- package/dist/social-providers/wechat.mjs +10 -6
- package/dist/social-providers/zoom.d.mts +6 -1
- package/dist/social-providers/zoom.mjs +15 -9
- package/dist/types/context.d.mts +27 -8
- package/dist/types/index.d.mts +1 -1
- package/dist/types/init-options.d.mts +137 -6
- package/dist/types/plugin-client.d.mts +12 -2
- package/dist/utils/host.mjs +4 -0
- package/dist/utils/url.mjs +4 -3
- package/package.json +7 -7
- package/src/api/index.ts +82 -0
- package/src/context/transaction.ts +45 -12
- package/src/db/adapter/factory.ts +127 -64
- package/src/db/adapter/index.ts +54 -9
- package/src/db/adapter/types.ts +1 -0
- package/src/db/get-tables.ts +8 -3
- package/src/db/schema/account.ts +14 -2
- package/src/db/type.ts +12 -7
- package/src/env/env-impl.ts +1 -2
- package/src/error/codes.ts +5 -0
- package/src/oauth2/create-authorization-url.ts +2 -2
- package/src/oauth2/dpop.ts +568 -0
- package/src/oauth2/index.ts +61 -2
- package/src/oauth2/oauth-provider.ts +140 -10
- package/src/oauth2/refresh-access-token.ts +2 -2
- package/src/oauth2/scopes.ts +118 -0
- package/src/oauth2/utils.ts +2 -5
- package/src/oauth2/verify-id-token.ts +111 -0
- package/src/oauth2/verify.ts +372 -58
- package/src/social-providers/apple.ts +24 -61
- package/src/social-providers/atlassian.ts +12 -8
- package/src/social-providers/cognito.ts +25 -47
- package/src/social-providers/discord.ts +19 -8
- package/src/social-providers/dropbox.ts +13 -7
- package/src/social-providers/facebook.ts +97 -51
- package/src/social-providers/figma.ts +13 -9
- package/src/social-providers/github.ts +12 -8
- package/src/social-providers/gitlab.ts +14 -8
- package/src/social-providers/google.ts +66 -47
- package/src/social-providers/huggingface.ts +12 -8
- package/src/social-providers/kakao.ts +16 -8
- package/src/social-providers/kick.ts +12 -7
- package/src/social-providers/line.ts +37 -37
- package/src/social-providers/linear.ts +12 -6
- package/src/social-providers/linkedin.ts +14 -10
- package/src/social-providers/microsoft-entra-id.ts +103 -59
- package/src/social-providers/naver.ts +12 -6
- package/src/social-providers/notion.ts +12 -6
- package/src/social-providers/paybin.ts +14 -11
- package/src/social-providers/paypal.ts +6 -25
- package/src/social-providers/polar.ts +12 -8
- package/src/social-providers/railway.ts +13 -9
- package/src/social-providers/reddit.ts +25 -10
- package/src/social-providers/roblox.ts +18 -7
- package/src/social-providers/salesforce.ts +12 -8
- package/src/social-providers/slack.ts +18 -9
- package/src/social-providers/spotify.ts +13 -7
- package/src/social-providers/tiktok.ts +13 -7
- package/src/social-providers/twitch.ts +12 -8
- package/src/social-providers/twitter.ts +17 -8
- package/src/social-providers/vercel.ts +16 -10
- package/src/social-providers/vk.ts +13 -7
- package/src/social-providers/wechat.ts +28 -9
- package/src/social-providers/zoom.ts +19 -6
- package/src/types/context.ts +26 -8
- package/src/types/index.ts +7 -0
- package/src/types/init-options.ts +159 -8
- package/src/types/plugin-client.ts +16 -2
- package/src/utils/host.ts +15 -0
- package/src/utils/url.ts +10 -4
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import { decodeJwt,
|
|
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 { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
6
6
|
import {
|
|
7
7
|
createAuthorizationURL,
|
|
8
8
|
getPrimaryClientId,
|
|
9
9
|
refreshAccessToken,
|
|
10
|
+
resolveRequestedScopes,
|
|
10
11
|
validateAuthorizationCode,
|
|
11
12
|
} from "../oauth2";
|
|
12
13
|
|
|
@@ -48,15 +49,33 @@ export interface GoogleOptions extends ProviderOptions<GoogleProfile> {
|
|
|
48
49
|
*/
|
|
49
50
|
display?: ("page" | "popup" | "touch" | "wap") | undefined;
|
|
50
51
|
/**
|
|
51
|
-
* The hosted domain
|
|
52
|
+
* The hosted domain (Google Workspace) the user must belong to.
|
|
53
|
+
*
|
|
54
|
+
* This is sent to Google as the `hd` authorization hint and, when set, is
|
|
55
|
+
* also enforced against the `hd` claim of the returned id token/profile.
|
|
56
|
+
* Sign-in is rejected when the claim is missing or does not match, so this
|
|
57
|
+
* can be used to restrict sign-in to a Workspace domain.
|
|
52
58
|
*/
|
|
53
59
|
hd?: string | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Enable incremental authorization via Google's `include_granted_scopes`
|
|
62
|
+
* parameter. When enabled, Google reports the user's full granted scope set
|
|
63
|
+
* in the token response.
|
|
64
|
+
*
|
|
65
|
+
* @default true
|
|
66
|
+
*/
|
|
67
|
+
includeGrantedScopes?: boolean | undefined;
|
|
54
68
|
}
|
|
55
69
|
|
|
70
|
+
const GOOGLE_DEFAULT_SCOPES = ["email", "profile", "openid"];
|
|
71
|
+
|
|
56
72
|
export const google = (options: GoogleOptions) => {
|
|
57
73
|
return {
|
|
58
74
|
id: "google",
|
|
59
75
|
name: "Google",
|
|
76
|
+
callbackPath: "/callback/google",
|
|
77
|
+
grantAuthority:
|
|
78
|
+
options.includeGrantedScopes !== false ? "full-grant" : "projection",
|
|
60
79
|
async createAuthorizationURL({
|
|
61
80
|
state,
|
|
62
81
|
scopes,
|
|
@@ -75,16 +94,16 @@ export const google = (options: GoogleOptions) => {
|
|
|
75
94
|
if (!codeVerifier) {
|
|
76
95
|
throw new BetterAuthError("codeVerifier is required for Google");
|
|
77
96
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
97
|
+
const requestedScopes = resolveRequestedScopes(
|
|
98
|
+
options,
|
|
99
|
+
GOOGLE_DEFAULT_SCOPES,
|
|
100
|
+
scopes,
|
|
101
|
+
);
|
|
102
|
+
return createAuthorizationURL({
|
|
84
103
|
id: "google",
|
|
85
104
|
options,
|
|
86
105
|
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
87
|
-
scopes:
|
|
106
|
+
scopes: requestedScopes,
|
|
88
107
|
state,
|
|
89
108
|
codeVerifier,
|
|
90
109
|
redirectURI,
|
|
@@ -93,12 +112,17 @@ export const google = (options: GoogleOptions) => {
|
|
|
93
112
|
display: display || options.display,
|
|
94
113
|
loginHint,
|
|
95
114
|
hd: options.hd,
|
|
96
|
-
additionalParams:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
115
|
+
additionalParams:
|
|
116
|
+
options.includeGrantedScopes === false
|
|
117
|
+
? { ...(additionalParams ?? {}) }
|
|
118
|
+
: {
|
|
119
|
+
...(additionalParams ?? {}),
|
|
120
|
+
// Not caller-overridable: the emitted param must stay in
|
|
121
|
+
// lockstep with `grantAuthority` (driven by the option), or
|
|
122
|
+
// the callback would treat a non-authoritative grant as full.
|
|
123
|
+
include_granted_scopes: "true",
|
|
124
|
+
},
|
|
100
125
|
});
|
|
101
|
-
return url;
|
|
102
126
|
},
|
|
103
127
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
104
128
|
return validateAuthorizationCode({
|
|
@@ -122,37 +146,20 @@ export const google = (options: GoogleOptions) => {
|
|
|
122
146
|
tokenEndpoint: "https://oauth2.googleapis.com/token",
|
|
123
147
|
});
|
|
124
148
|
},
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const publicKey = await getGooglePublicKey(kid);
|
|
141
|
-
const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
|
|
142
|
-
algorithms: [jwtAlg],
|
|
143
|
-
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
144
|
-
audience: options.clientId,
|
|
145
|
-
maxTokenAge: "1h",
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (nonce && jwtClaims.nonce !== nonce) {
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return true;
|
|
153
|
-
} catch {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
149
|
+
idToken: {
|
|
150
|
+
// https://developers.google.com/identity/sign-in/web/backend-auth#verify-the-integrity-of-the-id-token
|
|
151
|
+
jwks: (header) => getGooglePublicKey(header.kid!),
|
|
152
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
153
|
+
audience: options.clientId,
|
|
154
|
+
maxTokenAge: "1h",
|
|
155
|
+
// Google's `hd` authorization parameter is only a UI hint and can be
|
|
156
|
+
// removed or changed by the user. When a hosted domain is configured,
|
|
157
|
+
// the `hd` claim in the verified id token is the authoritative value
|
|
158
|
+
// and must match, otherwise accounts outside the workspace domain would
|
|
159
|
+
// be accepted on the id_token sign-in path.
|
|
160
|
+
verifyClaims: options.hd
|
|
161
|
+
? (claims) => claims.hd === options.hd
|
|
162
|
+
: undefined,
|
|
156
163
|
},
|
|
157
164
|
async getUserInfo(token) {
|
|
158
165
|
if (options.getUserInfo) {
|
|
@@ -162,6 +169,18 @@ export const google = (options: GoogleOptions) => {
|
|
|
162
169
|
return null;
|
|
163
170
|
}
|
|
164
171
|
const user = decodeJwt(token.idToken) as GoogleProfile;
|
|
172
|
+
// Enforce the configured hosted domain on the callback profile path
|
|
173
|
+
// as well. The `hd` claim must be present and match, since the
|
|
174
|
+
// authorization-time `hd` hint does not restrict which account signs
|
|
175
|
+
// in.
|
|
176
|
+
if (options.hd && user.hd !== options.hd) {
|
|
177
|
+
logger.error(
|
|
178
|
+
`Google sign-in rejected: id token hosted domain (hd) "${
|
|
179
|
+
user.hd ?? "<missing>"
|
|
180
|
+
}" does not match the configured "hd" option "${options.hd}".`,
|
|
181
|
+
);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
165
184
|
const userMap = await options.mapProfileToUser?.(user);
|
|
166
185
|
return {
|
|
167
186
|
user: {
|
|
@@ -176,7 +195,7 @@ export const google = (options: GoogleOptions) => {
|
|
|
176
195
|
};
|
|
177
196
|
},
|
|
178
197
|
options,
|
|
179
|
-
} satisfies
|
|
198
|
+
} satisfies UpstreamProvider<GoogleProfile>;
|
|
180
199
|
};
|
|
181
200
|
|
|
182
201
|
export const getGooglePublicKey = async (kid: string) => {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -42,11 +43,14 @@ export interface HuggingFaceOptions
|
|
|
42
43
|
clientId: string;
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
const HUGGINGFACE_DEFAULT_SCOPES = ["openid", "profile", "email"];
|
|
47
|
+
|
|
45
48
|
export const huggingface = (options: HuggingFaceOptions) => {
|
|
46
49
|
const tokenEndpoint = "https://huggingface.co/oauth/token";
|
|
47
50
|
return {
|
|
48
51
|
id: "huggingface",
|
|
49
52
|
name: "Hugging Face",
|
|
53
|
+
callbackPath: "/callback/huggingface",
|
|
50
54
|
createAuthorizationURL({
|
|
51
55
|
state,
|
|
52
56
|
scopes,
|
|
@@ -54,16 +58,16 @@ export const huggingface = (options: HuggingFaceOptions) => {
|
|
|
54
58
|
redirectURI,
|
|
55
59
|
additionalParams,
|
|
56
60
|
}) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
const requestedScopes = resolveRequestedScopes(
|
|
62
|
+
options,
|
|
63
|
+
HUGGINGFACE_DEFAULT_SCOPES,
|
|
64
|
+
scopes,
|
|
65
|
+
);
|
|
62
66
|
return createAuthorizationURL({
|
|
63
67
|
id: "huggingface",
|
|
64
68
|
options,
|
|
65
69
|
authorizationEndpoint: "https://huggingface.co/oauth/authorize",
|
|
66
|
-
scopes:
|
|
70
|
+
scopes: requestedScopes,
|
|
67
71
|
state,
|
|
68
72
|
codeVerifier,
|
|
69
73
|
redirectURI,
|
|
@@ -122,5 +126,5 @@ export const huggingface = (options: HuggingFaceOptions) => {
|
|
|
122
126
|
};
|
|
123
127
|
},
|
|
124
128
|
options,
|
|
125
|
-
} satisfies
|
|
129
|
+
} satisfies UpstreamProvider<HuggingFaceProfile>;
|
|
126
130
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -101,22 +102,29 @@ export interface KakaoOptions extends ProviderOptions<KakaoProfile> {
|
|
|
101
102
|
clientId: string;
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
const KAKAO_DEFAULT_SCOPES = [
|
|
106
|
+
"account_email",
|
|
107
|
+
"profile_image",
|
|
108
|
+
"profile_nickname",
|
|
109
|
+
];
|
|
110
|
+
|
|
104
111
|
export const kakao = (options: KakaoOptions) => {
|
|
105
112
|
const tokenEndpoint = "https://kauth.kakao.com/oauth/token";
|
|
106
113
|
return {
|
|
107
114
|
id: "kakao",
|
|
108
115
|
name: "Kakao",
|
|
116
|
+
callbackPath: "/callback/kakao",
|
|
109
117
|
createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
const requestedScopes = resolveRequestedScopes(
|
|
119
|
+
options,
|
|
120
|
+
KAKAO_DEFAULT_SCOPES,
|
|
121
|
+
scopes,
|
|
122
|
+
);
|
|
115
123
|
return createAuthorizationURL({
|
|
116
124
|
id: "kakao",
|
|
117
125
|
options,
|
|
118
126
|
authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
|
|
119
|
-
scopes:
|
|
127
|
+
scopes: requestedScopes,
|
|
120
128
|
state,
|
|
121
129
|
redirectURI,
|
|
122
130
|
additionalParams,
|
|
@@ -176,5 +184,5 @@ export const kakao = (options: KakaoOptions) => {
|
|
|
176
184
|
};
|
|
177
185
|
},
|
|
178
186
|
options,
|
|
179
|
-
} satisfies
|
|
187
|
+
} satisfies UpstreamProvider<KakaoProfile>;
|
|
180
188
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -29,10 +30,13 @@ export interface KickOptions extends ProviderOptions<KickProfile> {
|
|
|
29
30
|
clientId: string;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
const KICK_DEFAULT_SCOPES = ["user:read"];
|
|
34
|
+
|
|
32
35
|
export const kick = (options: KickOptions) => {
|
|
33
36
|
return {
|
|
34
37
|
id: "kick",
|
|
35
38
|
name: "Kick",
|
|
39
|
+
callbackPath: "/callback/kick",
|
|
36
40
|
createAuthorizationURL({
|
|
37
41
|
state,
|
|
38
42
|
scopes,
|
|
@@ -40,16 +44,17 @@ export const kick = (options: KickOptions) => {
|
|
|
40
44
|
codeVerifier,
|
|
41
45
|
additionalParams,
|
|
42
46
|
}) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
+
const requestedScopes = resolveRequestedScopes(
|
|
48
|
+
options,
|
|
49
|
+
KICK_DEFAULT_SCOPES,
|
|
50
|
+
scopes,
|
|
51
|
+
);
|
|
47
52
|
return createAuthorizationURL({
|
|
48
53
|
id: "kick",
|
|
49
54
|
redirectURI,
|
|
50
55
|
options,
|
|
51
56
|
authorizationEndpoint: "https://id.kick.com/oauth/authorize",
|
|
52
|
-
scopes:
|
|
57
|
+
scopes: requestedScopes,
|
|
53
58
|
codeVerifier,
|
|
54
59
|
state,
|
|
55
60
|
additionalParams,
|
|
@@ -112,5 +117,5 @@ export const kick = (options: KickOptions) => {
|
|
|
112
117
|
};
|
|
113
118
|
},
|
|
114
119
|
options,
|
|
115
|
-
} satisfies
|
|
120
|
+
} satisfies UpstreamProvider<KickProfile>;
|
|
116
121
|
};
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
2
|
import { decodeJwt } from "jose";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
4
4
|
import {
|
|
5
5
|
createAuthorizationURL,
|
|
6
6
|
refreshAccessToken,
|
|
7
|
+
resolveRequestedScopes,
|
|
7
8
|
validateAuthorizationCode,
|
|
8
9
|
} from "../oauth2";
|
|
9
10
|
|
|
@@ -32,6 +33,8 @@ export interface LineOptions
|
|
|
32
33
|
clientId: string;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
const LINE_DEFAULT_SCOPES = ["openid", "profile", "email"];
|
|
37
|
+
|
|
35
38
|
/**
|
|
36
39
|
* LINE Login v2.1
|
|
37
40
|
* - Authorization endpoint: https://access.line.me/oauth2/v2.1/authorize
|
|
@@ -50,7 +53,8 @@ export const line = (options: LineOptions) => {
|
|
|
50
53
|
return {
|
|
51
54
|
id: "line",
|
|
52
55
|
name: "LINE",
|
|
53
|
-
|
|
56
|
+
callbackPath: "/callback/line",
|
|
57
|
+
createAuthorizationURL({
|
|
54
58
|
state,
|
|
55
59
|
scopes,
|
|
56
60
|
codeVerifier,
|
|
@@ -58,16 +62,16 @@ export const line = (options: LineOptions) => {
|
|
|
58
62
|
loginHint,
|
|
59
63
|
additionalParams,
|
|
60
64
|
}) {
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return
|
|
65
|
+
const requestedScopes = resolveRequestedScopes(
|
|
66
|
+
options,
|
|
67
|
+
LINE_DEFAULT_SCOPES,
|
|
68
|
+
scopes,
|
|
69
|
+
);
|
|
70
|
+
return createAuthorizationURL({
|
|
67
71
|
id: "line",
|
|
68
72
|
options,
|
|
69
73
|
authorizationEndpoint,
|
|
70
|
-
scopes:
|
|
74
|
+
scopes: requestedScopes,
|
|
71
75
|
state,
|
|
72
76
|
codeVerifier,
|
|
73
77
|
redirectURI,
|
|
@@ -96,34 +100,30 @@ export const line = (options: LineOptions) => {
|
|
|
96
100
|
tokenEndpoint,
|
|
97
101
|
});
|
|
98
102
|
},
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
method: "POST",
|
|
114
|
-
headers: {
|
|
115
|
-
"content-type": "application/x-www-form-urlencoded",
|
|
103
|
+
idToken: {
|
|
104
|
+
verify: async (token, nonce) => {
|
|
105
|
+
const body = new URLSearchParams();
|
|
106
|
+
body.set("id_token", token);
|
|
107
|
+
body.set("client_id", options.clientId);
|
|
108
|
+
if (nonce) body.set("nonce", nonce);
|
|
109
|
+
const { data, error } = await betterFetch<LineIdTokenPayload>(
|
|
110
|
+
verifyIdTokenEndpoint,
|
|
111
|
+
{
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: {
|
|
114
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
115
|
+
},
|
|
116
|
+
body,
|
|
116
117
|
},
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return true;
|
|
118
|
+
);
|
|
119
|
+
if (error || !data) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// aud must match clientId; nonce (if provided) must also match nonce
|
|
123
|
+
if (data.aud !== options.clientId) return false;
|
|
124
|
+
if (data.nonce && data.nonce !== nonce) return false;
|
|
125
|
+
return true;
|
|
126
|
+
},
|
|
127
127
|
},
|
|
128
128
|
async getUserInfo(token) {
|
|
129
129
|
if (options.getUserInfo) {
|
|
@@ -167,5 +167,5 @@ export const line = (options: LineOptions) => {
|
|
|
167
167
|
};
|
|
168
168
|
},
|
|
169
169
|
options,
|
|
170
|
-
} satisfies
|
|
170
|
+
} satisfies UpstreamProvider<LineUserInfo | LineIdTokenPayload, LineOptions>;
|
|
171
171
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -26,11 +27,14 @@ export interface LinearOptions extends ProviderOptions<LinearUser> {
|
|
|
26
27
|
clientId: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
const LINEAR_DEFAULT_SCOPES = ["read"];
|
|
31
|
+
|
|
29
32
|
export const linear = (options: LinearOptions) => {
|
|
30
33
|
const tokenEndpoint = "https://api.linear.app/oauth/token";
|
|
31
34
|
return {
|
|
32
35
|
id: "linear",
|
|
33
36
|
name: "Linear",
|
|
37
|
+
callbackPath: "/callback/linear",
|
|
34
38
|
createAuthorizationURL({
|
|
35
39
|
state,
|
|
36
40
|
scopes,
|
|
@@ -38,14 +42,16 @@ export const linear = (options: LinearOptions) => {
|
|
|
38
42
|
redirectURI,
|
|
39
43
|
additionalParams,
|
|
40
44
|
}) {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
const requestedScopes = resolveRequestedScopes(
|
|
46
|
+
options,
|
|
47
|
+
LINEAR_DEFAULT_SCOPES,
|
|
48
|
+
scopes,
|
|
49
|
+
);
|
|
44
50
|
return createAuthorizationURL({
|
|
45
51
|
id: "linear",
|
|
46
52
|
options,
|
|
47
53
|
authorizationEndpoint: "https://linear.app/oauth/authorize",
|
|
48
|
-
scopes:
|
|
54
|
+
scopes: requestedScopes,
|
|
49
55
|
state,
|
|
50
56
|
redirectURI,
|
|
51
57
|
loginHint,
|
|
@@ -124,5 +130,5 @@ export const linear = (options: LinearOptions) => {
|
|
|
124
130
|
};
|
|
125
131
|
},
|
|
126
132
|
options,
|
|
127
|
-
} satisfies
|
|
133
|
+
} satisfies UpstreamProvider<LinearUser>;
|
|
128
134
|
};
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { betterFetch } from "@better-fetch/fetch";
|
|
2
|
-
import type {
|
|
2
|
+
import type { ProviderOptions, UpstreamProvider } from "../oauth2";
|
|
3
3
|
import {
|
|
4
4
|
createAuthorizationURL,
|
|
5
5
|
refreshAccessToken,
|
|
6
|
+
resolveRequestedScopes,
|
|
6
7
|
validateAuthorizationCode,
|
|
7
8
|
} from "../oauth2";
|
|
8
9
|
|
|
@@ -24,6 +25,8 @@ export interface LinkedInOptions extends ProviderOptions<LinkedInProfile> {
|
|
|
24
25
|
clientId: string;
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
const LINKEDIN_DEFAULT_SCOPES = ["profile", "email", "openid"];
|
|
29
|
+
|
|
27
30
|
export const linkedin = (options: LinkedInOptions) => {
|
|
28
31
|
const authorizationEndpoint =
|
|
29
32
|
"https://www.linkedin.com/oauth/v2/authorization";
|
|
@@ -32,23 +35,24 @@ export const linkedin = (options: LinkedInOptions) => {
|
|
|
32
35
|
return {
|
|
33
36
|
id: "linkedin",
|
|
34
37
|
name: "Linkedin",
|
|
35
|
-
|
|
38
|
+
callbackPath: "/callback/linkedin",
|
|
39
|
+
createAuthorizationURL: ({
|
|
36
40
|
state,
|
|
37
41
|
scopes,
|
|
38
42
|
redirectURI,
|
|
39
43
|
loginHint,
|
|
40
44
|
additionalParams,
|
|
41
45
|
}) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return
|
|
46
|
+
const requestedScopes = resolveRequestedScopes(
|
|
47
|
+
options,
|
|
48
|
+
LINKEDIN_DEFAULT_SCOPES,
|
|
49
|
+
scopes,
|
|
50
|
+
);
|
|
51
|
+
return createAuthorizationURL({
|
|
48
52
|
id: "linkedin",
|
|
49
53
|
options,
|
|
50
54
|
authorizationEndpoint,
|
|
51
|
-
scopes:
|
|
55
|
+
scopes: requestedScopes,
|
|
52
56
|
state,
|
|
53
57
|
loginHint,
|
|
54
58
|
redirectURI,
|
|
@@ -108,5 +112,5 @@ export const linkedin = (options: LinkedInOptions) => {
|
|
|
108
112
|
};
|
|
109
113
|
},
|
|
110
114
|
options,
|
|
111
|
-
} satisfies
|
|
115
|
+
} satisfies UpstreamProvider<LinkedInProfile>;
|
|
112
116
|
};
|