@better-auth/core 1.7.0-beta.0 → 1.7.0-beta.10
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 +44 -1
- package/dist/api/index.mjs +69 -4
- 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 +91 -3
- package/dist/db/adapter/get-id-field.mjs +1 -1
- package/dist/db/adapter/index.d.mts +87 -3
- package/dist/db/adapter/types.d.mts +1 -1
- package/dist/db/get-tables.mjs +2 -1
- package/dist/db/type.d.mts +17 -0
- package/dist/env/env-impl.mjs +1 -1
- package/dist/error/codes.d.mts +1 -0
- package/dist/error/codes.mjs +1 -0
- package/dist/error/index.d.mts +7 -0
- package/dist/index.d.mts +2 -2
- package/dist/instrumentation/api.mjs +12 -0
- package/dist/instrumentation/noop.mjs +42 -0
- package/dist/instrumentation/pure.index.d.mts +7 -0
- package/dist/instrumentation/pure.index.mjs +7 -0
- package/dist/instrumentation/tracer.mjs +6 -3
- 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 +20 -31
- package/dist/oauth2/create-authorization-url.d.mts +11 -1
- package/dist/oauth2/create-authorization-url.mjs +27 -7
- package/dist/oauth2/dpop.d.mts +142 -0
- package/dist/oauth2/dpop.mjs +246 -0
- package/dist/oauth2/index.d.mts +14 -9
- package/dist/oauth2/index.mjs +12 -8
- package/dist/oauth2/oauth-provider.d.mts +150 -10
- package/dist/oauth2/refresh-access-token.d.mts +20 -40
- package/dist/oauth2/refresh-access-token.mjs +38 -36
- package/dist/oauth2/reject-redirects.mjs +65 -0
- 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 +23 -1
- package/dist/oauth2/utils.mjs +48 -2
- package/dist/oauth2/validate-authorization-code.d.mts +17 -52
- package/dist/oauth2/validate-authorization-code.mjs +28 -34
- package/dist/oauth2/verify-id-token.d.mts +27 -0
- package/dist/oauth2/verify-id-token.mjs +62 -0
- package/dist/oauth2/verify.d.mts +88 -15
- package/dist/oauth2/verify.mjs +203 -25
- package/dist/social-providers/apple.d.mts +16 -4
- package/dist/social-providers/apple.mjs +17 -24
- package/dist/social-providers/atlassian.d.mts +5 -1
- package/dist/social-providers/atlassian.mjs +6 -3
- package/dist/social-providers/cognito.d.mts +27 -3
- package/dist/social-providers/cognito.mjs +15 -25
- package/dist/social-providers/discord.d.mts +7 -3
- package/dist/social-providers/discord.mjs +16 -3
- package/dist/social-providers/dropbox.d.mts +5 -1
- package/dist/social-providers/dropbox.mjs +5 -4
- package/dist/social-providers/facebook.d.mts +24 -5
- package/dist/social-providers/facebook.mjs +54 -20
- package/dist/social-providers/figma.d.mts +5 -1
- package/dist/social-providers/figma.mjs +4 -3
- package/dist/social-providers/github.d.mts +6 -2
- package/dist/social-providers/github.mjs +5 -4
- package/dist/social-providers/gitlab.d.mts +5 -1
- package/dist/social-providers/gitlab.mjs +3 -2
- package/dist/social-providers/google.d.mts +53 -5
- package/dist/social-providers/google.mjs +69 -25
- package/dist/social-providers/huggingface.d.mts +5 -1
- package/dist/social-providers/huggingface.mjs +3 -2
- package/dist/social-providers/index.d.mts +221 -45
- package/dist/social-providers/index.mjs +2 -2
- package/dist/social-providers/kakao.d.mts +5 -1
- package/dist/social-providers/kakao.mjs +3 -2
- package/dist/social-providers/kick.d.mts +5 -1
- package/dist/social-providers/kick.mjs +3 -2
- package/dist/social-providers/line.d.mts +8 -2
- package/dist/social-providers/line.mjs +5 -6
- package/dist/social-providers/linear.d.mts +5 -1
- package/dist/social-providers/linear.mjs +3 -2
- package/dist/social-providers/linkedin.d.mts +7 -3
- package/dist/social-providers/linkedin.mjs +4 -3
- package/dist/social-providers/microsoft-entra-id.d.mts +41 -7
- package/dist/social-providers/microsoft-entra-id.mjs +37 -28
- package/dist/social-providers/naver.d.mts +5 -1
- package/dist/social-providers/naver.mjs +3 -2
- package/dist/social-providers/notion.d.mts +5 -1
- package/dist/social-providers/notion.mjs +5 -2
- package/dist/social-providers/paybin.d.mts +5 -1
- package/dist/social-providers/paybin.mjs +4 -3
- package/dist/social-providers/paypal.d.mts +6 -2
- package/dist/social-providers/paypal.mjs +19 -14
- package/dist/social-providers/polar.d.mts +5 -1
- package/dist/social-providers/polar.mjs +3 -2
- package/dist/social-providers/railway.d.mts +5 -1
- package/dist/social-providers/railway.mjs +3 -2
- package/dist/social-providers/reddit.d.mts +5 -1
- package/dist/social-providers/reddit.mjs +7 -5
- package/dist/social-providers/roblox.d.mts +6 -2
- package/dist/social-providers/roblox.mjs +12 -2
- package/dist/social-providers/salesforce.d.mts +5 -1
- package/dist/social-providers/salesforce.mjs +4 -3
- package/dist/social-providers/slack.d.mts +6 -2
- package/dist/social-providers/slack.mjs +11 -8
- package/dist/social-providers/spotify.d.mts +5 -1
- package/dist/social-providers/spotify.mjs +3 -2
- package/dist/social-providers/tiktok.d.mts +5 -1
- package/dist/social-providers/tiktok.mjs +14 -2
- package/dist/social-providers/twitch.d.mts +5 -1
- package/dist/social-providers/twitch.mjs +3 -2
- package/dist/social-providers/twitter.d.mts +4 -1
- package/dist/social-providers/twitter.mjs +2 -1
- package/dist/social-providers/vercel.d.mts +5 -1
- package/dist/social-providers/vercel.mjs +3 -2
- package/dist/social-providers/vk.d.mts +5 -1
- package/dist/social-providers/vk.mjs +3 -2
- package/dist/social-providers/wechat.d.mts +5 -1
- package/dist/social-providers/wechat.mjs +8 -2
- package/dist/social-providers/zoom.d.mts +5 -1
- package/dist/social-providers/zoom.mjs +10 -17
- package/dist/types/context.d.mts +55 -6
- package/dist/types/index.d.mts +1 -1
- package/dist/types/init-options.d.mts +182 -12
- package/dist/types/plugin-client.d.mts +12 -2
- package/dist/utils/async.d.mts +22 -0
- package/dist/utils/async.mjs +32 -0
- package/dist/utils/host.d.mts +147 -0
- package/dist/utils/host.mjs +298 -0
- package/dist/utils/ip.d.mts +28 -5
- package/dist/utils/ip.mjs +118 -4
- package/dist/utils/is-api-error.d.mts +6 -0
- package/dist/utils/is-api-error.mjs +8 -0
- 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 +34 -4
- package/package.json +26 -16
- package/src/api/index.ts +121 -5
- package/src/context/transaction.ts +45 -12
- package/src/db/adapter/factory.ts +193 -7
- package/src/db/adapter/get-id-field.ts +2 -2
- package/src/db/adapter/index.ts +85 -2
- package/src/db/adapter/types.ts +2 -0
- package/src/db/get-tables.ts +9 -1
- package/src/db/schema/account.ts +4 -1
- package/src/db/schema/user.ts +3 -0
- package/src/db/type.ts +17 -0
- package/src/env/env-impl.ts +1 -2
- package/src/error/codes.ts +1 -0
- package/src/error/index.ts +9 -0
- package/src/instrumentation/api.ts +17 -0
- package/src/instrumentation/noop.ts +74 -0
- package/src/instrumentation/pure.index.ts +31 -0
- package/src/instrumentation/tracer.ts +8 -3
- 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 +50 -74
- package/src/oauth2/create-authorization-url.ts +33 -7
- package/src/oauth2/dpop.ts +568 -0
- package/src/oauth2/index.ts +81 -11
- package/src/oauth2/oauth-provider.ts +159 -11
- package/src/oauth2/refresh-access-token.ts +82 -83
- package/src/oauth2/reject-redirects.ts +75 -0
- package/src/oauth2/token-endpoint-auth.ts +221 -0
- package/src/oauth2/utils.ts +72 -5
- package/src/oauth2/validate-authorization-code.ts +69 -89
- package/src/oauth2/verify-id-token.ts +115 -0
- package/src/oauth2/verify.ts +409 -78
- package/src/social-providers/apple.ts +30 -40
- package/src/social-providers/atlassian.ts +8 -1
- package/src/social-providers/cognito.ts +36 -39
- package/src/social-providers/discord.ts +22 -18
- package/src/social-providers/dropbox.ts +7 -5
- package/src/social-providers/facebook.ts +108 -52
- package/src/social-providers/figma.ts +8 -1
- package/src/social-providers/github.ts +5 -3
- package/src/social-providers/gitlab.ts +2 -0
- package/src/social-providers/google.ts +141 -39
- 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 +25 -27
- package/src/social-providers/linear.ts +8 -1
- package/src/social-providers/linkedin.ts +5 -3
- package/src/social-providers/microsoft-entra-id.ts +100 -55
- 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 +28 -17
- package/src/social-providers/polar.ts +8 -1
- package/src/social-providers/railway.ts +8 -1
- package/src/social-providers/reddit.ts +9 -4
- 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 +17 -2
- package/src/social-providers/zoom.ts +15 -19
- package/src/types/context.ts +57 -5
- package/src/types/index.ts +7 -0
- package/src/types/init-options.ts +204 -14
- package/src/types/plugin-client.ts +16 -2
- package/src/utils/async.ts +53 -0
- package/src/utils/host.ts +425 -0
- package/src/utils/ip.ts +197 -13
- package/src/utils/is-api-error.ts +10 -0
- package/src/utils/redirect-uri.ts +54 -0
- package/src/utils/string.ts +37 -0
- package/src/utils/url.ts +38 -4
|
@@ -2,7 +2,7 @@ import { logger } from "../env/logger.mjs";
|
|
|
2
2
|
import { getOAuth2Tokens } from "../oauth2/utils.mjs";
|
|
3
3
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
4
4
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import { authorizationCodeRequest } from "../oauth2/validate-authorization-code.mjs";
|
|
6
6
|
import { betterFetch } from "@better-fetch/fetch";
|
|
7
7
|
//#region src/social-providers/github.ts
|
|
8
8
|
const github = (options) => {
|
|
@@ -10,7 +10,7 @@ const github = (options) => {
|
|
|
10
10
|
return {
|
|
11
11
|
id: "github",
|
|
12
12
|
name: "GitHub",
|
|
13
|
-
createAuthorizationURL({ state, scopes, loginHint, codeVerifier, redirectURI }) {
|
|
13
|
+
createAuthorizationURL({ state, scopes, loginHint, codeVerifier, redirectURI, additionalParams }) {
|
|
14
14
|
const _scopes = options.disableDefaultScope ? [] : ["read:user", "user:email"];
|
|
15
15
|
if (options.scope) _scopes.push(...options.scope);
|
|
16
16
|
if (scopes) _scopes.push(...scopes);
|
|
@@ -23,11 +23,12 @@ const github = (options) => {
|
|
|
23
23
|
codeVerifier,
|
|
24
24
|
redirectURI,
|
|
25
25
|
loginHint,
|
|
26
|
-
prompt: options.prompt
|
|
26
|
+
prompt: options.prompt,
|
|
27
|
+
additionalParams
|
|
27
28
|
});
|
|
28
29
|
},
|
|
29
30
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
30
|
-
const { body, headers: requestHeaders } =
|
|
31
|
+
const { body, headers: requestHeaders } = await authorizationCodeRequest({
|
|
31
32
|
code,
|
|
32
33
|
codeVerifier,
|
|
33
34
|
redirectURI,
|
|
@@ -57,7 +57,8 @@ declare const gitlab: (options: GitlabOptions) => {
|
|
|
57
57
|
scopes,
|
|
58
58
|
codeVerifier,
|
|
59
59
|
loginHint,
|
|
60
|
-
redirectURI
|
|
60
|
+
redirectURI,
|
|
61
|
+
additionalParams
|
|
61
62
|
}: {
|
|
62
63
|
state: string;
|
|
63
64
|
codeVerifier: string;
|
|
@@ -65,6 +66,8 @@ declare const gitlab: (options: GitlabOptions) => {
|
|
|
65
66
|
redirectURI: string;
|
|
66
67
|
display?: string | undefined;
|
|
67
68
|
loginHint?: string | undefined;
|
|
69
|
+
idTokenNonce?: string | undefined;
|
|
70
|
+
additionalParams?: Record<string, string> | undefined;
|
|
68
71
|
}) => Promise<URL>;
|
|
69
72
|
validateAuthorizationCode: ({
|
|
70
73
|
code,
|
|
@@ -78,6 +81,7 @@ declare const gitlab: (options: GitlabOptions) => {
|
|
|
78
81
|
}) => Promise<OAuth2Tokens>;
|
|
79
82
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
80
83
|
getUserInfo(token: OAuth2Tokens & {
|
|
84
|
+
expectedIdTokenNonce?: string | undefined;
|
|
81
85
|
user?: {
|
|
82
86
|
name?: {
|
|
83
87
|
firstName?: string;
|
|
@@ -20,7 +20,7 @@ const gitlab = (options) => {
|
|
|
20
20
|
return {
|
|
21
21
|
id: issuerId,
|
|
22
22
|
name: "Gitlab",
|
|
23
|
-
createAuthorizationURL: async ({ state, scopes, codeVerifier, loginHint, redirectURI }) => {
|
|
23
|
+
createAuthorizationURL: async ({ state, scopes, codeVerifier, loginHint, redirectURI, additionalParams }) => {
|
|
24
24
|
const _scopes = options.disableDefaultScope ? [] : ["read_user"];
|
|
25
25
|
if (options.scope) _scopes.push(...options.scope);
|
|
26
26
|
if (scopes) _scopes.push(...scopes);
|
|
@@ -32,7 +32,8 @@ const gitlab = (options) => {
|
|
|
32
32
|
state,
|
|
33
33
|
redirectURI,
|
|
34
34
|
codeVerifier,
|
|
35
|
-
loginHint
|
|
35
|
+
loginHint,
|
|
36
|
+
additionalParams
|
|
36
37
|
});
|
|
37
38
|
},
|
|
38
39
|
validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
|
|
2
|
+
import * as jose from "jose";
|
|
3
|
+
import { JWTPayload } from "jose";
|
|
4
|
+
|
|
2
5
|
//#region src/social-providers/google.d.ts
|
|
3
6
|
interface GoogleProfile {
|
|
4
7
|
aud: string;
|
|
@@ -27,7 +30,7 @@ interface GoogleProfile {
|
|
|
27
30
|
sub: string;
|
|
28
31
|
}
|
|
29
32
|
interface GoogleOptions extends ProviderOptions<GoogleProfile> {
|
|
30
|
-
clientId: string;
|
|
33
|
+
clientId: string | string[];
|
|
31
34
|
/**
|
|
32
35
|
* The access type to use for the authorization code request
|
|
33
36
|
*/
|
|
@@ -37,10 +40,45 @@ interface GoogleOptions extends ProviderOptions<GoogleProfile> {
|
|
|
37
40
|
*/
|
|
38
41
|
display?: ("page" | "popup" | "touch" | "wap") | undefined;
|
|
39
42
|
/**
|
|
40
|
-
* The hosted domain
|
|
43
|
+
* The hosted domain (Google Workspace) the user must belong to.
|
|
44
|
+
*
|
|
45
|
+
* This is sent to Google as the `hd` authorization hint and, when set, is
|
|
46
|
+
* also enforced against the `hd` claim of the returned id token/profile.
|
|
47
|
+
* Set `hd: "*"` to require any Workspace hosted-domain claim. Sign-in is
|
|
48
|
+
* rejected when the claim is missing or does not satisfy this restriction.
|
|
41
49
|
*/
|
|
42
50
|
hd?: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Whether to send `include_granted_scopes=true` to Google's authorization
|
|
53
|
+
* endpoint, which lets new access tokens cover scopes from prior grants
|
|
54
|
+
* in addition to the ones requested for this flow. Set to `false` when
|
|
55
|
+
* each OAuth flow should request only its own scopes.
|
|
56
|
+
*
|
|
57
|
+
* Defaults to `true`.
|
|
58
|
+
*
|
|
59
|
+
* @see https://developers.google.com/identity/protocols/oauth2/web-server#incrementalAuth
|
|
60
|
+
*/
|
|
61
|
+
includeGrantedScopes?: boolean | undefined;
|
|
62
|
+
}
|
|
63
|
+
interface VerifyGoogleIdTokenOptions {
|
|
64
|
+
token: string;
|
|
65
|
+
audience: string | string[];
|
|
66
|
+
nonce?: string | undefined;
|
|
43
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Verifies a Google ID token against Google's issuer, audience, signature,
|
|
70
|
+
* expiry, and maximum token age.
|
|
71
|
+
*/
|
|
72
|
+
declare const verifyGoogleIdToken: ({
|
|
73
|
+
token,
|
|
74
|
+
audience,
|
|
75
|
+
nonce
|
|
76
|
+
}: VerifyGoogleIdTokenOptions) => Promise<JWTPayload | null>;
|
|
77
|
+
/**
|
|
78
|
+
* Checks whether Google's verified `hd` claim satisfies the configured hosted
|
|
79
|
+
* domain restriction. `hd: "*"` accepts any Google Workspace hosted domain.
|
|
80
|
+
*/
|
|
81
|
+
declare const isGoogleHostedDomainAllowed: (configuredHostedDomain: string | undefined, tokenHostedDomain: unknown) => boolean;
|
|
44
82
|
declare const google: (options: GoogleOptions) => {
|
|
45
83
|
id: "google";
|
|
46
84
|
name: string;
|
|
@@ -50,7 +88,8 @@ declare const google: (options: GoogleOptions) => {
|
|
|
50
88
|
codeVerifier,
|
|
51
89
|
redirectURI,
|
|
52
90
|
loginHint,
|
|
53
|
-
display
|
|
91
|
+
display,
|
|
92
|
+
additionalParams
|
|
54
93
|
}: {
|
|
55
94
|
state: string;
|
|
56
95
|
codeVerifier: string;
|
|
@@ -58,6 +97,8 @@ declare const google: (options: GoogleOptions) => {
|
|
|
58
97
|
redirectURI: string;
|
|
59
98
|
display?: string | undefined;
|
|
60
99
|
loginHint?: string | undefined;
|
|
100
|
+
idTokenNonce?: string | undefined;
|
|
101
|
+
additionalParams?: Record<string, string> | undefined;
|
|
61
102
|
}): Promise<URL>;
|
|
62
103
|
validateAuthorizationCode: ({
|
|
63
104
|
code,
|
|
@@ -70,8 +111,15 @@ declare const google: (options: GoogleOptions) => {
|
|
|
70
111
|
deviceId?: string | undefined;
|
|
71
112
|
}) => Promise<OAuth2Tokens>;
|
|
72
113
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
73
|
-
|
|
114
|
+
idToken: {
|
|
115
|
+
jwks: (header: jose.JWTHeaderParameters) => Promise<Uint8Array<ArrayBufferLike> | CryptoKey>;
|
|
116
|
+
issuer: string[];
|
|
117
|
+
audience: string | string[];
|
|
118
|
+
maxTokenAge: string;
|
|
119
|
+
verifyClaims: ((claims: Record<string, unknown>) => boolean) | undefined;
|
|
120
|
+
};
|
|
74
121
|
getUserInfo(token: OAuth2Tokens & {
|
|
122
|
+
expectedIdTokenNonce?: string | undefined;
|
|
75
123
|
user?: {
|
|
76
124
|
name?: {
|
|
77
125
|
firstName?: string;
|
|
@@ -94,4 +142,4 @@ declare const google: (options: GoogleOptions) => {
|
|
|
94
142
|
};
|
|
95
143
|
declare const getGooglePublicKey: (kid: string) => Promise<Uint8Array<ArrayBufferLike> | CryptoKey>;
|
|
96
144
|
//#endregion
|
|
97
|
-
export { GoogleOptions, GoogleProfile, getGooglePublicKey, google };
|
|
145
|
+
export { GoogleOptions, GoogleProfile, VerifyGoogleIdTokenOptions, getGooglePublicKey, google, isGoogleHostedDomainAllowed, verifyGoogleIdToken };
|
|
@@ -1,17 +1,60 @@
|
|
|
1
|
-
import { logger } from "../env/logger.mjs";
|
|
2
1
|
import { APIError, BetterAuthError } from "../error/index.mjs";
|
|
2
|
+
import { logger } from "../env/logger.mjs";
|
|
3
|
+
import { getPrimaryClientId } from "../oauth2/utils.mjs";
|
|
3
4
|
import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
|
|
4
5
|
import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
|
|
5
6
|
import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
|
|
6
7
|
import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
|
|
7
8
|
import { betterFetch } from "@better-fetch/fetch";
|
|
8
9
|
//#region src/social-providers/google.ts
|
|
10
|
+
const GOOGLE_ID_TOKEN_MAX_AGE = "1h";
|
|
11
|
+
const GOOGLE_ID_TOKEN_ALGORITHM = "RS256";
|
|
12
|
+
const GOOGLE_ID_TOKEN_ALGORITHMS = [GOOGLE_ID_TOKEN_ALGORITHM];
|
|
13
|
+
function isGoogleIdTokenAlgorithm(algorithm) {
|
|
14
|
+
return GOOGLE_ID_TOKEN_ALGORITHMS.includes(algorithm);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Verifies a Google ID token against Google's issuer, audience, signature,
|
|
18
|
+
* expiry, and maximum token age.
|
|
19
|
+
*/
|
|
20
|
+
const verifyGoogleIdToken = async ({ token, audience, nonce }) => {
|
|
21
|
+
try {
|
|
22
|
+
const { kid, alg } = decodeProtectedHeader(token);
|
|
23
|
+
if (!isGoogleIdTokenAlgorithm(alg)) return null;
|
|
24
|
+
const publicKeys = await getGooglePublicKeys(kid);
|
|
25
|
+
for (const publicKey of publicKeys) try {
|
|
26
|
+
const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
|
|
27
|
+
algorithms: GOOGLE_ID_TOKEN_ALGORITHMS,
|
|
28
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
29
|
+
audience,
|
|
30
|
+
maxTokenAge: GOOGLE_ID_TOKEN_MAX_AGE
|
|
31
|
+
});
|
|
32
|
+
if (nonce && jwtClaims.nonce !== nonce) return null;
|
|
33
|
+
return jwtClaims;
|
|
34
|
+
} catch {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Checks whether Google's verified `hd` claim satisfies the configured hosted
|
|
44
|
+
* domain restriction. `hd: "*"` accepts any Google Workspace hosted domain.
|
|
45
|
+
*/
|
|
46
|
+
const isGoogleHostedDomainAllowed = (configuredHostedDomain, tokenHostedDomain) => {
|
|
47
|
+
if (!configuredHostedDomain) return true;
|
|
48
|
+
if (typeof tokenHostedDomain !== "string" || !tokenHostedDomain) return false;
|
|
49
|
+
if (configuredHostedDomain === "*") return true;
|
|
50
|
+
return tokenHostedDomain === configuredHostedDomain;
|
|
51
|
+
};
|
|
9
52
|
const google = (options) => {
|
|
10
53
|
return {
|
|
11
54
|
id: "google",
|
|
12
55
|
name: "Google",
|
|
13
|
-
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, display }) {
|
|
14
|
-
if (!options.clientId || !options.clientSecret) {
|
|
56
|
+
async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, display, additionalParams }) {
|
|
57
|
+
if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
|
|
15
58
|
logger.error("Client Id and Client Secret is required for Google. Make sure to provide them in the options.");
|
|
16
59
|
throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
|
|
17
60
|
}
|
|
@@ -36,7 +79,10 @@ const google = (options) => {
|
|
|
36
79
|
display: display || options.display,
|
|
37
80
|
loginHint,
|
|
38
81
|
hd: options.hd,
|
|
39
|
-
additionalParams: {
|
|
82
|
+
additionalParams: {
|
|
83
|
+
...options.includeGrantedScopes === false ? {} : { include_granted_scopes: "true" },
|
|
84
|
+
...additionalParams ?? {}
|
|
85
|
+
}
|
|
40
86
|
});
|
|
41
87
|
},
|
|
42
88
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|
|
@@ -59,28 +105,21 @@ const google = (options) => {
|
|
|
59
105
|
tokenEndpoint: "https://oauth2.googleapis.com/token"
|
|
60
106
|
});
|
|
61
107
|
},
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const { payload: jwtClaims } = await jwtVerify(token, await getGooglePublicKey(kid), {
|
|
69
|
-
algorithms: [jwtAlg],
|
|
70
|
-
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
71
|
-
audience: options.clientId,
|
|
72
|
-
maxTokenAge: "1h"
|
|
73
|
-
});
|
|
74
|
-
if (nonce && jwtClaims.nonce !== nonce) return false;
|
|
75
|
-
return true;
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
108
|
+
idToken: {
|
|
109
|
+
jwks: (header) => getGooglePublicKey(header.kid),
|
|
110
|
+
issuer: ["https://accounts.google.com", "accounts.google.com"],
|
|
111
|
+
audience: options.clientId,
|
|
112
|
+
maxTokenAge: GOOGLE_ID_TOKEN_MAX_AGE,
|
|
113
|
+
verifyClaims: options.hd ? (claims) => isGoogleHostedDomainAllowed(options.hd, claims.hd) : void 0
|
|
79
114
|
},
|
|
80
115
|
async getUserInfo(token) {
|
|
81
116
|
if (options.getUserInfo) return options.getUserInfo(token);
|
|
82
117
|
if (!token.idToken) return null;
|
|
83
118
|
const user = decodeJwt(token.idToken);
|
|
119
|
+
if (!isGoogleHostedDomainAllowed(options.hd, user.hd)) {
|
|
120
|
+
logger.error(`Google sign-in rejected: id token hosted domain (hd) "${user.hd ?? "<missing>"}" does not satisfy the configured "hd" option "${options.hd}".`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
84
123
|
const userMap = await options.mapProfileToUser?.(user);
|
|
85
124
|
return {
|
|
86
125
|
user: {
|
|
@@ -98,11 +137,16 @@ const google = (options) => {
|
|
|
98
137
|
};
|
|
99
138
|
};
|
|
100
139
|
const getGooglePublicKey = async (kid) => {
|
|
140
|
+
const [publicKey] = await getGooglePublicKeys(kid);
|
|
141
|
+
if (!publicKey) throw new Error(`JWK with kid ${kid} not found`);
|
|
142
|
+
return publicKey;
|
|
143
|
+
};
|
|
144
|
+
const getGooglePublicKeys = async (kid) => {
|
|
101
145
|
const { data } = await betterFetch("https://www.googleapis.com/oauth2/v3/certs");
|
|
102
146
|
if (!data?.keys) throw new APIError("BAD_REQUEST", { message: "Keys not found" });
|
|
103
|
-
const
|
|
104
|
-
if (!
|
|
105
|
-
return
|
|
147
|
+
const jwks = kid ? data.keys.filter((key) => key.kid === kid) : data.keys;
|
|
148
|
+
if (!jwks.length) throw new Error(`JWK with kid ${kid} not found`);
|
|
149
|
+
return Promise.all(jwks.map((jwk) => importJWK(jwk, GOOGLE_ID_TOKEN_ALGORITHM)));
|
|
106
150
|
};
|
|
107
151
|
//#endregion
|
|
108
|
-
export { getGooglePublicKey, google };
|
|
152
|
+
export { getGooglePublicKey, google, isGoogleHostedDomainAllowed, verifyGoogleIdToken };
|
|
@@ -38,7 +38,8 @@ declare const huggingface: (options: HuggingFaceOptions) => {
|
|
|
38
38
|
state,
|
|
39
39
|
scopes,
|
|
40
40
|
codeVerifier,
|
|
41
|
-
redirectURI
|
|
41
|
+
redirectURI,
|
|
42
|
+
additionalParams
|
|
42
43
|
}: {
|
|
43
44
|
state: string;
|
|
44
45
|
codeVerifier: string;
|
|
@@ -46,6 +47,8 @@ declare const huggingface: (options: HuggingFaceOptions) => {
|
|
|
46
47
|
redirectURI: string;
|
|
47
48
|
display?: string | undefined;
|
|
48
49
|
loginHint?: string | undefined;
|
|
50
|
+
idTokenNonce?: string | undefined;
|
|
51
|
+
additionalParams?: Record<string, string> | undefined;
|
|
49
52
|
}): Promise<URL>;
|
|
50
53
|
validateAuthorizationCode: ({
|
|
51
54
|
code,
|
|
@@ -59,6 +62,7 @@ declare const huggingface: (options: HuggingFaceOptions) => {
|
|
|
59
62
|
}) => Promise<OAuth2Tokens>;
|
|
60
63
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
61
64
|
getUserInfo(token: OAuth2Tokens & {
|
|
65
|
+
expectedIdTokenNonce?: string | undefined;
|
|
62
66
|
user?: {
|
|
63
67
|
name?: {
|
|
64
68
|
firstName?: string;
|
|
@@ -8,7 +8,7 @@ const huggingface = (options) => {
|
|
|
8
8
|
return {
|
|
9
9
|
id: "huggingface",
|
|
10
10
|
name: "Hugging Face",
|
|
11
|
-
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
|
|
11
|
+
createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
|
|
12
12
|
const _scopes = options.disableDefaultScope ? [] : [
|
|
13
13
|
"openid",
|
|
14
14
|
"profile",
|
|
@@ -23,7 +23,8 @@ const huggingface = (options) => {
|
|
|
23
23
|
scopes: _scopes,
|
|
24
24
|
state,
|
|
25
25
|
codeVerifier,
|
|
26
|
-
redirectURI
|
|
26
|
+
redirectURI,
|
|
27
|
+
additionalParams
|
|
27
28
|
});
|
|
28
29
|
},
|
|
29
30
|
validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
|