@better-auth/core 1.7.0-beta.6 → 1.7.0-beta.7
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 +1 -1
- package/dist/instrumentation/tracer.mjs +1 -1
- package/dist/oauth2/create-authorization-url.d.mts +3 -1
- package/dist/oauth2/create-authorization-url.mjs +3 -1
- package/dist/oauth2/index.d.mts +2 -2
- package/dist/oauth2/oauth-provider.d.mts +37 -3
- package/dist/oauth2/refresh-access-token.mjs +15 -1
- package/dist/social-providers/apple.d.mts +2 -0
- package/dist/social-providers/atlassian.d.mts +2 -0
- package/dist/social-providers/cognito.d.mts +2 -0
- package/dist/social-providers/discord.d.mts +2 -0
- package/dist/social-providers/dropbox.d.mts +2 -0
- package/dist/social-providers/facebook.d.mts +2 -0
- package/dist/social-providers/figma.d.mts +2 -0
- package/dist/social-providers/github.d.mts +2 -0
- package/dist/social-providers/gitlab.d.mts +2 -0
- package/dist/social-providers/google.d.mts +2 -0
- package/dist/social-providers/huggingface.d.mts +2 -0
- package/dist/social-providers/index.d.mts +70 -0
- package/dist/social-providers/kakao.d.mts +2 -0
- package/dist/social-providers/kick.d.mts +2 -0
- package/dist/social-providers/line.d.mts +2 -0
- package/dist/social-providers/linear.d.mts +2 -0
- package/dist/social-providers/linkedin.d.mts +2 -0
- package/dist/social-providers/microsoft-entra-id.d.mts +2 -0
- package/dist/social-providers/naver.d.mts +2 -0
- package/dist/social-providers/notion.d.mts +2 -0
- package/dist/social-providers/paybin.d.mts +2 -0
- package/dist/social-providers/paypal.d.mts +2 -0
- package/dist/social-providers/polar.d.mts +2 -0
- package/dist/social-providers/railway.d.mts +2 -0
- package/dist/social-providers/reddit.d.mts +2 -0
- package/dist/social-providers/roblox.d.mts +2 -0
- package/dist/social-providers/salesforce.d.mts +2 -0
- package/dist/social-providers/slack.d.mts +2 -0
- package/dist/social-providers/spotify.d.mts +2 -0
- package/dist/social-providers/tiktok.d.mts +2 -0
- package/dist/social-providers/twitch.d.mts +2 -0
- package/dist/social-providers/twitter.d.mts +2 -0
- package/dist/social-providers/vercel.d.mts +2 -0
- package/dist/social-providers/vk.d.mts +2 -0
- package/dist/social-providers/wechat.d.mts +2 -0
- package/dist/social-providers/zoom.d.mts +2 -0
- package/dist/utils/host.d.mts +1 -1
- package/dist/utils/host.mjs +3 -0
- package/package.json +1 -1
- package/src/oauth2/create-authorization-url.ts +4 -0
- package/src/oauth2/index.ts +1 -0
- package/src/oauth2/oauth-provider.ts +40 -2
- package/src/oauth2/refresh-access-token.ts +27 -3
- package/src/oauth2/verify-id-token.ts +2 -0
- package/src/utils/host.ts +10 -1
|
@@ -64,6 +64,7 @@ declare const paypal: (options: PayPalOptions) => {
|
|
|
64
64
|
redirectURI: string;
|
|
65
65
|
display?: string | undefined;
|
|
66
66
|
loginHint?: string | undefined;
|
|
67
|
+
idTokenNonce?: string | undefined;
|
|
67
68
|
additionalParams?: Record<string, string> | undefined;
|
|
68
69
|
}): Promise<{
|
|
69
70
|
url: URL;
|
|
@@ -89,6 +90,7 @@ declare const paypal: (options: PayPalOptions) => {
|
|
|
89
90
|
accessTokenExpiresAt: Date | undefined;
|
|
90
91
|
}>);
|
|
91
92
|
getUserInfo(token: OAuth2Tokens & {
|
|
93
|
+
expectedIdTokenNonce?: string | undefined;
|
|
92
94
|
user?: {
|
|
93
95
|
name?: {
|
|
94
96
|
firstName?: string;
|
|
@@ -39,6 +39,7 @@ declare const polar: (options: PolarOptions) => {
|
|
|
39
39
|
redirectURI: string;
|
|
40
40
|
display?: string | undefined;
|
|
41
41
|
loginHint?: string | undefined;
|
|
42
|
+
idTokenNonce?: string | undefined;
|
|
42
43
|
additionalParams?: Record<string, string> | undefined;
|
|
43
44
|
}): Promise<{
|
|
44
45
|
url: URL;
|
|
@@ -56,6 +57,7 @@ declare const polar: (options: PolarOptions) => {
|
|
|
56
57
|
}) => Promise<OAuth2Tokens>;
|
|
57
58
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
58
59
|
getUserInfo(token: OAuth2Tokens & {
|
|
60
|
+
expectedIdTokenNonce?: string | undefined;
|
|
59
61
|
user?: {
|
|
60
62
|
name?: {
|
|
61
63
|
firstName?: string;
|
|
@@ -30,6 +30,7 @@ declare const railway: (options: RailwayOptions) => {
|
|
|
30
30
|
redirectURI: string;
|
|
31
31
|
display?: string | undefined;
|
|
32
32
|
loginHint?: string | undefined;
|
|
33
|
+
idTokenNonce?: string | undefined;
|
|
33
34
|
additionalParams?: Record<string, string> | undefined;
|
|
34
35
|
}): Promise<{
|
|
35
36
|
url: URL;
|
|
@@ -47,6 +48,7 @@ declare const railway: (options: RailwayOptions) => {
|
|
|
47
48
|
}) => Promise<OAuth2Tokens>;
|
|
48
49
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
49
50
|
getUserInfo(token: OAuth2Tokens & {
|
|
51
|
+
expectedIdTokenNonce?: string | undefined;
|
|
50
52
|
user?: {
|
|
51
53
|
name?: {
|
|
52
54
|
firstName?: string;
|
|
@@ -28,6 +28,7 @@ declare const reddit: (options: RedditOptions) => {
|
|
|
28
28
|
redirectURI: string;
|
|
29
29
|
display?: string | undefined;
|
|
30
30
|
loginHint?: string | undefined;
|
|
31
|
+
idTokenNonce?: string | undefined;
|
|
31
32
|
additionalParams?: Record<string, string> | undefined;
|
|
32
33
|
}): Promise<{
|
|
33
34
|
url: URL;
|
|
@@ -44,6 +45,7 @@ declare const reddit: (options: RedditOptions) => {
|
|
|
44
45
|
}) => Promise<OAuth2Tokens>;
|
|
45
46
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
46
47
|
getUserInfo(token: OAuth2Tokens & {
|
|
48
|
+
expectedIdTokenNonce?: string | undefined;
|
|
47
49
|
user?: {
|
|
48
50
|
name?: {
|
|
49
51
|
firstName?: string;
|
|
@@ -36,6 +36,7 @@ declare const roblox: (options: RobloxOptions) => {
|
|
|
36
36
|
redirectURI: string;
|
|
37
37
|
display?: string | undefined;
|
|
38
38
|
loginHint?: string | undefined;
|
|
39
|
+
idTokenNonce?: string | undefined;
|
|
39
40
|
additionalParams?: Record<string, string> | undefined;
|
|
40
41
|
}): Promise<{
|
|
41
42
|
url: URL;
|
|
@@ -52,6 +53,7 @@ declare const roblox: (options: RobloxOptions) => {
|
|
|
52
53
|
}) => Promise<OAuth2Tokens>;
|
|
53
54
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
54
55
|
getUserInfo(token: OAuth2Tokens & {
|
|
56
|
+
expectedIdTokenNonce?: string | undefined;
|
|
55
57
|
user?: {
|
|
56
58
|
name?: {
|
|
57
59
|
firstName?: string;
|
|
@@ -44,6 +44,7 @@ declare const salesforce: (options: SalesforceOptions) => {
|
|
|
44
44
|
redirectURI: string;
|
|
45
45
|
display?: string | undefined;
|
|
46
46
|
loginHint?: string | undefined;
|
|
47
|
+
idTokenNonce?: string | undefined;
|
|
47
48
|
additionalParams?: Record<string, string> | undefined;
|
|
48
49
|
}): Promise<{
|
|
49
50
|
url: URL;
|
|
@@ -61,6 +62,7 @@ declare const salesforce: (options: SalesforceOptions) => {
|
|
|
61
62
|
}) => Promise<OAuth2Tokens>;
|
|
62
63
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
63
64
|
getUserInfo(token: OAuth2Tokens & {
|
|
65
|
+
expectedIdTokenNonce?: string | undefined;
|
|
64
66
|
user?: {
|
|
65
67
|
name?: {
|
|
66
68
|
firstName?: string;
|
|
@@ -49,6 +49,7 @@ declare const slack: (options: SlackOptions) => {
|
|
|
49
49
|
redirectURI: string;
|
|
50
50
|
display?: string | undefined;
|
|
51
51
|
loginHint?: string | undefined;
|
|
52
|
+
idTokenNonce?: string | undefined;
|
|
52
53
|
additionalParams?: Record<string, string> | undefined;
|
|
53
54
|
}): Promise<{
|
|
54
55
|
url: URL;
|
|
@@ -65,6 +66,7 @@ declare const slack: (options: SlackOptions) => {
|
|
|
65
66
|
}) => Promise<OAuth2Tokens>;
|
|
66
67
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
67
68
|
getUserInfo(token: OAuth2Tokens & {
|
|
69
|
+
expectedIdTokenNonce?: string | undefined;
|
|
68
70
|
user?: {
|
|
69
71
|
name?: {
|
|
70
72
|
firstName?: string;
|
|
@@ -28,6 +28,7 @@ declare const spotify: (options: SpotifyOptions) => {
|
|
|
28
28
|
redirectURI: string;
|
|
29
29
|
display?: string | undefined;
|
|
30
30
|
loginHint?: string | undefined;
|
|
31
|
+
idTokenNonce?: string | undefined;
|
|
31
32
|
additionalParams?: Record<string, string> | undefined;
|
|
32
33
|
}): Promise<{
|
|
33
34
|
url: URL;
|
|
@@ -45,6 +46,7 @@ declare const spotify: (options: SpotifyOptions) => {
|
|
|
45
46
|
}) => Promise<OAuth2Tokens>;
|
|
46
47
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
47
48
|
getUserInfo(token: OAuth2Tokens & {
|
|
49
|
+
expectedIdTokenNonce?: string | undefined;
|
|
48
50
|
user?: {
|
|
49
51
|
name?: {
|
|
50
52
|
firstName?: string;
|
|
@@ -134,6 +134,7 @@ declare const tiktok: (options: TiktokOptions) => {
|
|
|
134
134
|
redirectURI: string;
|
|
135
135
|
display?: string | undefined;
|
|
136
136
|
loginHint?: string | undefined;
|
|
137
|
+
idTokenNonce?: string | undefined;
|
|
137
138
|
additionalParams?: Record<string, string> | undefined;
|
|
138
139
|
}): {
|
|
139
140
|
url: URL;
|
|
@@ -150,6 +151,7 @@ declare const tiktok: (options: TiktokOptions) => {
|
|
|
150
151
|
}) => Promise<OAuth2Tokens>;
|
|
151
152
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
152
153
|
getUserInfo(token: OAuth2Tokens & {
|
|
154
|
+
expectedIdTokenNonce?: string | undefined;
|
|
153
155
|
user?: {
|
|
154
156
|
name?: {
|
|
155
157
|
firstName?: string;
|
|
@@ -45,6 +45,7 @@ declare const twitch: (options: TwitchOptions) => {
|
|
|
45
45
|
redirectURI: string;
|
|
46
46
|
display?: string | undefined;
|
|
47
47
|
loginHint?: string | undefined;
|
|
48
|
+
idTokenNonce?: string | undefined;
|
|
48
49
|
additionalParams?: Record<string, string> | undefined;
|
|
49
50
|
}): Promise<{
|
|
50
51
|
url: URL;
|
|
@@ -61,6 +62,7 @@ declare const twitch: (options: TwitchOptions) => {
|
|
|
61
62
|
}) => Promise<OAuth2Tokens>;
|
|
62
63
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
63
64
|
getUserInfo(token: OAuth2Tokens & {
|
|
65
|
+
expectedIdTokenNonce?: string | undefined;
|
|
64
66
|
user?: {
|
|
65
67
|
name?: {
|
|
66
68
|
firstName?: string;
|
|
@@ -90,6 +90,7 @@ declare const twitter: (options: TwitterOption) => {
|
|
|
90
90
|
redirectURI: string;
|
|
91
91
|
display?: string | undefined;
|
|
92
92
|
loginHint?: string | undefined;
|
|
93
|
+
idTokenNonce?: string | undefined;
|
|
93
94
|
additionalParams?: Record<string, string> | undefined;
|
|
94
95
|
}): Promise<{
|
|
95
96
|
url: URL;
|
|
@@ -107,6 +108,7 @@ declare const twitter: (options: TwitterOption) => {
|
|
|
107
108
|
}) => Promise<OAuth2Tokens>;
|
|
108
109
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
109
110
|
getUserInfo(token: OAuth2Tokens & {
|
|
111
|
+
expectedIdTokenNonce?: string | undefined;
|
|
110
112
|
user?: {
|
|
111
113
|
name?: {
|
|
112
114
|
firstName?: string;
|
|
@@ -28,6 +28,7 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
28
28
|
redirectURI: string;
|
|
29
29
|
display?: string | undefined;
|
|
30
30
|
loginHint?: string | undefined;
|
|
31
|
+
idTokenNonce?: string | undefined;
|
|
31
32
|
additionalParams?: Record<string, string> | undefined;
|
|
32
33
|
}): Promise<{
|
|
33
34
|
url: URL;
|
|
@@ -44,6 +45,7 @@ declare const vercel: (options: VercelOptions) => {
|
|
|
44
45
|
deviceId?: string | undefined;
|
|
45
46
|
}) => Promise<OAuth2Tokens>;
|
|
46
47
|
getUserInfo(token: OAuth2Tokens & {
|
|
48
|
+
expectedIdTokenNonce?: string | undefined;
|
|
47
49
|
user?: {
|
|
48
50
|
name?: {
|
|
49
51
|
firstName?: string;
|
|
@@ -34,6 +34,7 @@ declare const vk: (options: VkOption) => {
|
|
|
34
34
|
redirectURI: string;
|
|
35
35
|
display?: string | undefined;
|
|
36
36
|
loginHint?: string | undefined;
|
|
37
|
+
idTokenNonce?: string | undefined;
|
|
37
38
|
additionalParams?: Record<string, string> | undefined;
|
|
38
39
|
}): Promise<{
|
|
39
40
|
url: URL;
|
|
@@ -52,6 +53,7 @@ declare const vk: (options: VkOption) => {
|
|
|
52
53
|
}) => Promise<OAuth2Tokens>;
|
|
53
54
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
54
55
|
getUserInfo(data: OAuth2Tokens & {
|
|
56
|
+
expectedIdTokenNonce?: string | undefined;
|
|
55
57
|
user?: {
|
|
56
58
|
name?: {
|
|
57
59
|
firstName?: string;
|
|
@@ -66,6 +66,7 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
66
66
|
redirectURI: string;
|
|
67
67
|
display?: string | undefined;
|
|
68
68
|
loginHint?: string | undefined;
|
|
69
|
+
idTokenNonce?: string | undefined;
|
|
69
70
|
additionalParams?: Record<string, string> | undefined;
|
|
70
71
|
}): {
|
|
71
72
|
url: URL;
|
|
@@ -95,6 +96,7 @@ declare const wechat: (options: WeChatOptions) => {
|
|
|
95
96
|
scopes: string[];
|
|
96
97
|
}>);
|
|
97
98
|
getUserInfo(token: OAuth2Tokens & {
|
|
99
|
+
expectedIdTokenNonce?: string | undefined;
|
|
98
100
|
user?: {
|
|
99
101
|
name?: {
|
|
100
102
|
firstName?: string;
|
|
@@ -130,6 +130,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
130
130
|
redirectURI: string;
|
|
131
131
|
display?: string | undefined;
|
|
132
132
|
loginHint?: string | undefined;
|
|
133
|
+
idTokenNonce?: string | undefined;
|
|
133
134
|
additionalParams?: Record<string, string> | undefined;
|
|
134
135
|
}) => Promise<{
|
|
135
136
|
url: URL;
|
|
@@ -147,6 +148,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
|
|
|
147
148
|
}) => Promise<OAuth2Tokens>;
|
|
148
149
|
refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
|
|
149
150
|
getUserInfo(token: OAuth2Tokens & {
|
|
151
|
+
expectedIdTokenNonce?: string | undefined;
|
|
150
152
|
user?: {
|
|
151
153
|
name?: {
|
|
152
154
|
firstName?: string;
|
package/dist/utils/host.d.mts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* The semantic kind of a host, derived from RFC 6890 special-purpose registries
|
|
27
27
|
* plus a few domain-name categories (localhost, cloud metadata FQDNs).
|
|
28
28
|
*/
|
|
29
|
-
type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
|
|
29
|
+
type HostKind = /** IPv4 `127.0.0.0/8` or IPv6 `::1`. */"loopback" /** DNS name `localhost` or RFC 6761 `.localhost` TLD. */ | "localhost" /** IPv4 `0.0.0.0` or IPv6 `::` — "this host on this network", not loopback. */ | "unspecified" /** RFC 1918 `10/8`, `172.16/12`, `192.168/16`, or IPv6 ULA `fc00::/7`. */ | "private" /** IPv4 `169.254/16` or IPv6 `fe80::/10`. Includes AWS IMDS `169.254.169.254`. */ | "linkLocal" /** RFC 6598 carrier-grade NAT `100.64.0.0/10`. */ | "sharedAddressSpace" /** RFC 5737 `192.0.2/24`, `198.51.100/24`, `203.0.113/24`, or RFC 3849 `2001:db8::/32`. */ | "documentation" /** RFC 2544 `198.18.0.0/15`. */ | "benchmarking" /** IPv4 `224.0.0.0/4` or IPv6 `ff00::/8`. */ | "multicast" /** IPv4 limited broadcast `255.255.255.255`. */ | "broadcast" /** Other RFC 6890 special-purpose ranges (0.0.0.0/8, 192.0.0.0/24, 192.88.99.0/24, 240.0.0.0/4, 2001::/32, etc.). */ | "reserved" /** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */ | "cloudMetadata" /** Any host not matching a special-purpose range above. */ | "public";
|
|
30
30
|
/**
|
|
31
31
|
* The syntactic form of the input host: an IPv4 literal, an IPv6 literal, or
|
|
32
32
|
* a domain name. IPv4-mapped IPv6 (`::ffff:192.0.2.1`) is reported as `ipv4`
|
package/dist/utils/host.mjs
CHANGED
|
@@ -86,6 +86,7 @@ function classifyIPv4(ip) {
|
|
|
86
86
|
if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
|
|
87
87
|
if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
|
|
88
88
|
if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
|
|
89
|
+
if (inIPv4Range(n, ipv4ToUint32("192.88.99.0"), 24)) return "reserved";
|
|
89
90
|
if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
|
|
90
91
|
return "public";
|
|
91
92
|
}
|
|
@@ -124,6 +125,7 @@ function classifyIPv6(expanded) {
|
|
|
124
125
|
const secondByte = Number.parseInt(expanded.slice(2, 4), 16);
|
|
125
126
|
if (firstByte === 255) return "multicast";
|
|
126
127
|
if (firstByte === 254 && (secondByte & 192) === 128) return "linkLocal";
|
|
128
|
+
if (firstByte === 254 && (secondByte & 192) === 192) return "reserved";
|
|
127
129
|
if ((firstByte & 254) === 252) return "private";
|
|
128
130
|
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
129
131
|
if (expanded.startsWith("2001:0002:0000:")) return "benchmarking";
|
|
@@ -146,6 +148,7 @@ function classifyIPv6(expanded) {
|
|
|
146
148
|
if (expanded.startsWith("0100:0000:0000:0000:")) return "reserved";
|
|
147
149
|
if (expanded.startsWith("3fff:0")) return "documentation";
|
|
148
150
|
if (expanded.startsWith("5f00:")) return "reserved";
|
|
151
|
+
if (expanded.startsWith("0000:0000:0000:0000:0000:0000:")) return "reserved";
|
|
149
152
|
return "public";
|
|
150
153
|
}
|
|
151
154
|
/**
|
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ export const RESERVED_AUTHORIZATION_PARAMS = [
|
|
|
15
15
|
"response_type",
|
|
16
16
|
"code_challenge",
|
|
17
17
|
"code_challenge_method",
|
|
18
|
+
"nonce",
|
|
18
19
|
"scope",
|
|
19
20
|
] as const;
|
|
20
21
|
|
|
@@ -37,6 +38,7 @@ export async function createAuthorizationURL({
|
|
|
37
38
|
responseType,
|
|
38
39
|
display,
|
|
39
40
|
loginHint,
|
|
41
|
+
nonce,
|
|
40
42
|
hd,
|
|
41
43
|
responseMode,
|
|
42
44
|
additionalParams,
|
|
@@ -56,6 +58,7 @@ export async function createAuthorizationURL({
|
|
|
56
58
|
responseType?: string | undefined;
|
|
57
59
|
display?: string | undefined;
|
|
58
60
|
loginHint?: string | undefined;
|
|
61
|
+
nonce?: string | undefined;
|
|
59
62
|
hd?: string | undefined;
|
|
60
63
|
responseMode?: string | undefined;
|
|
61
64
|
additionalParams?: Record<string, string> | undefined;
|
|
@@ -77,6 +80,7 @@ export async function createAuthorizationURL({
|
|
|
77
80
|
duration && url.searchParams.set("duration", duration);
|
|
78
81
|
display && url.searchParams.set("display", display);
|
|
79
82
|
loginHint && url.searchParams.set("login_hint", loginHint);
|
|
83
|
+
nonce && url.searchParams.set("nonce", nonce);
|
|
80
84
|
prompt && url.searchParams.set("prompt", prompt);
|
|
81
85
|
hd && url.searchParams.set("hd", hd);
|
|
82
86
|
accessType && url.searchParams.set("access_type", accessType);
|
package/src/oauth2/index.ts
CHANGED
|
@@ -77,6 +77,18 @@ export type OAuth2UserInfo = {
|
|
|
77
77
|
emailVerified: boolean;
|
|
78
78
|
};
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Request metadata available to provider refresh hooks.
|
|
82
|
+
*
|
|
83
|
+
* The refresh flow may be triggered by endpoints such as `getAccessToken` or
|
|
84
|
+
* `refreshToken`; this context gives provider hooks access to the triggering
|
|
85
|
+
* request without exposing the full endpoint implementation surface.
|
|
86
|
+
*/
|
|
87
|
+
export interface OAuthRefreshContext {
|
|
88
|
+
headers?: Headers | undefined;
|
|
89
|
+
request?: Request | undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
80
92
|
/**
|
|
81
93
|
* The result of building a provider authorization URL.
|
|
82
94
|
*
|
|
@@ -142,6 +154,12 @@ export interface UpstreamProvider<
|
|
|
142
154
|
redirectURI: string;
|
|
143
155
|
display?: string | undefined;
|
|
144
156
|
loginHint?: string | undefined;
|
|
157
|
+
/**
|
|
158
|
+
* OIDC nonce generated by the redirect initiator and persisted in OAuth
|
|
159
|
+
* state. Providers that set `requiresIdTokenNonce` must forward this to
|
|
160
|
+
* the authorization URL as the `nonce` parameter.
|
|
161
|
+
*/
|
|
162
|
+
idTokenNonce?: string | undefined;
|
|
145
163
|
/**
|
|
146
164
|
* Extra query parameters to append to the authorization URL.
|
|
147
165
|
* Providers forward these to the shared `createAuthorizationURL` helper,
|
|
@@ -159,6 +177,12 @@ export interface UpstreamProvider<
|
|
|
159
177
|
}) => Promise<OAuth2Tokens | null>;
|
|
160
178
|
getUserInfo: (
|
|
161
179
|
token: OAuth2Tokens & {
|
|
180
|
+
/**
|
|
181
|
+
* OIDC nonce recovered from OAuth state. Providers that required an
|
|
182
|
+
* ID-token nonce must pass this to `verifyProviderIdToken` before
|
|
183
|
+
* trusting ID-token claims.
|
|
184
|
+
*/
|
|
185
|
+
expectedIdTokenNonce?: string | undefined;
|
|
162
186
|
/**
|
|
163
187
|
* The user object from the provider
|
|
164
188
|
* This is only available for some providers like Apple
|
|
@@ -178,10 +202,17 @@ export interface UpstreamProvider<
|
|
|
178
202
|
data: T;
|
|
179
203
|
} | null>;
|
|
180
204
|
/**
|
|
181
|
-
* Custom function to refresh a token
|
|
205
|
+
* Custom function to refresh a token.
|
|
206
|
+
*
|
|
207
|
+
* Receives request metadata from the endpoint that triggered the refresh.
|
|
208
|
+
* Providers that don't need request-scoped data can ignore the second
|
|
209
|
+
* argument.
|
|
182
210
|
*/
|
|
183
211
|
refreshAccessToken?:
|
|
184
|
-
| ((
|
|
212
|
+
| ((
|
|
213
|
+
refreshToken: string,
|
|
214
|
+
ctx?: OAuthRefreshContext,
|
|
215
|
+
) => Promise<OAuth2Tokens>)
|
|
185
216
|
| undefined;
|
|
186
217
|
/**
|
|
187
218
|
* Declarative id_token verification config consumed by the shared
|
|
@@ -195,6 +226,13 @@ export interface UpstreamProvider<
|
|
|
195
226
|
* against this value to prevent authorization server mix-up attacks.
|
|
196
227
|
*/
|
|
197
228
|
issuer?: string | undefined;
|
|
229
|
+
/**
|
|
230
|
+
* Require shared OAuth redirect routes to bind ID-token verification to an
|
|
231
|
+
* authorization request nonce. When true, routes generate `idTokenNonce`,
|
|
232
|
+
* pass it to `createAuthorizationURL`, persist it in state, and provide it
|
|
233
|
+
* back to `getUserInfo` as `expectedIdTokenNonce`.
|
|
234
|
+
*/
|
|
235
|
+
requiresIdTokenNonce?: boolean | undefined;
|
|
198
236
|
/**
|
|
199
237
|
* Disable implicit sign up for new users. When set to true for the provider,
|
|
200
238
|
* sign-in need to be called with with requestSignUp as true to create new users.
|
|
@@ -29,6 +29,21 @@ interface RefreshAccessTokenInput extends RefreshAccessTokenRequestInput {
|
|
|
29
29
|
tokenEndpoint: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Body keys owned by the refresh-token flow or unsafe to copy from caller input.
|
|
34
|
+
*/
|
|
35
|
+
const BLOCKED_REFRESH_TOKEN_PARAMS = [
|
|
36
|
+
"grant_type",
|
|
37
|
+
"refresh_token",
|
|
38
|
+
"__proto__",
|
|
39
|
+
"constructor",
|
|
40
|
+
"prototype",
|
|
41
|
+
] as const;
|
|
42
|
+
|
|
43
|
+
const BLOCKED_REFRESH_TOKEN_PARAMS_SET: ReadonlySet<string> = new Set(
|
|
44
|
+
BLOCKED_REFRESH_TOKEN_PARAMS,
|
|
45
|
+
);
|
|
46
|
+
|
|
32
47
|
export async function refreshAccessTokenRequest({
|
|
33
48
|
refreshToken,
|
|
34
49
|
options,
|
|
@@ -59,6 +74,17 @@ export async function refreshAccessTokenRequest({
|
|
|
59
74
|
return request;
|
|
60
75
|
}
|
|
61
76
|
|
|
77
|
+
function applyRefreshExtraParams(
|
|
78
|
+
body: URLSearchParams,
|
|
79
|
+
extraParams: Record<string, string> | undefined,
|
|
80
|
+
) {
|
|
81
|
+
if (!extraParams) return;
|
|
82
|
+
for (const [key, value] of Object.entries(extraParams)) {
|
|
83
|
+
if (BLOCKED_REFRESH_TOKEN_PARAMS_SET.has(key)) continue;
|
|
84
|
+
body.set(key, value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
62
88
|
function buildRefreshAccessTokenRequest({
|
|
63
89
|
refreshToken,
|
|
64
90
|
options,
|
|
@@ -83,9 +109,7 @@ function buildRefreshAccessTokenRequest({
|
|
|
83
109
|
}
|
|
84
110
|
}
|
|
85
111
|
if (extraParams) {
|
|
86
|
-
|
|
87
|
-
body.set(key, value);
|
|
88
|
-
}
|
|
112
|
+
applyRefreshExtraParams(body, extraParams);
|
|
89
113
|
}
|
|
90
114
|
|
|
91
115
|
return {
|
|
@@ -79,6 +79,8 @@ export async function verifyProviderIdToken(
|
|
|
79
79
|
// Opaque (non-JWS) tokens carry no signature to check. They are accepted only when the
|
|
80
80
|
// provider opts in, in which case getUserInfo resolves identity from the access token via
|
|
81
81
|
// the provider's userinfo endpoint, which validates it (e.g. Facebook Graph access tokens).
|
|
82
|
+
// An expected `nonce` is not enforced here: an opaque token carries no `nonce` claim, and the
|
|
83
|
+
// access-token-backed userinfo exchange (not the token itself) is the identity source.
|
|
82
84
|
if (token.split(".").length !== 3) {
|
|
83
85
|
return config.allowOpaqueToken === true;
|
|
84
86
|
}
|
package/src/utils/host.ts
CHANGED
|
@@ -49,7 +49,7 @@ export type HostKind =
|
|
|
49
49
|
| "multicast"
|
|
50
50
|
/** IPv4 limited broadcast `255.255.255.255`. */
|
|
51
51
|
| "broadcast"
|
|
52
|
-
/** Other RFC 6890 special-purpose ranges (0/8, 192.0.0/24, 240/4, 2001::/32, etc.). */
|
|
52
|
+
/** Other RFC 6890 special-purpose ranges (0.0.0.0/8, 192.0.0.0/24, 192.88.99.0/24, 240.0.0.0/4, 2001::/32, etc.). */
|
|
53
53
|
| "reserved"
|
|
54
54
|
/** Cloud metadata service FQDN (e.g. `metadata.google.internal`). */
|
|
55
55
|
| "cloudMetadata"
|
|
@@ -183,6 +183,8 @@ function classifyIPv4(ip: string): HostKind {
|
|
|
183
183
|
if (inIPv4Range(n, ipv4ToUint32("224.0.0.0"), 4)) return "multicast";
|
|
184
184
|
if (inIPv4Range(n, ipv4ToUint32("0.0.0.0"), 8)) return "reserved";
|
|
185
185
|
if (inIPv4Range(n, ipv4ToUint32("192.0.0.0"), 24)) return "reserved";
|
|
186
|
+
// 6to4 relay anycast (RFC 7526, deprecated), not globally reachable.
|
|
187
|
+
if (inIPv4Range(n, ipv4ToUint32("192.88.99.0"), 24)) return "reserved";
|
|
186
188
|
if (inIPv4Range(n, ipv4ToUint32("240.0.0.0"), 4)) return "reserved";
|
|
187
189
|
|
|
188
190
|
return "public";
|
|
@@ -231,6 +233,8 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
231
233
|
|
|
232
234
|
if (firstByte === 0xff) return "multicast";
|
|
233
235
|
if (firstByte === 0xfe && (secondByte & 0xc0) === 0x80) return "linkLocal";
|
|
236
|
+
// fec0::/10 — deprecated site-local (RFC 3879), not globally reachable.
|
|
237
|
+
if (firstByte === 0xfe && (secondByte & 0xc0) === 0xc0) return "reserved";
|
|
234
238
|
if ((firstByte & 0xfe) === 0xfc) return "private";
|
|
235
239
|
|
|
236
240
|
if (expanded.startsWith("2001:0db8:")) return "documentation";
|
|
@@ -270,6 +274,11 @@ function classifyIPv6(expanded: string): HostKind {
|
|
|
270
274
|
// 5f00::/16 — SRv6 SIDs (RFC 9602), not globally reachable.
|
|
271
275
|
if (expanded.startsWith("5f00:")) return "reserved";
|
|
272
276
|
|
|
277
|
+
// ::/96 — deprecated IPv4-compatible IPv6 (RFC 4291 §2.5.5.1). `::` and
|
|
278
|
+
// `::1` are matched above; the rest of the block embeds an IPv4 (e.g.
|
|
279
|
+
// `::127.0.0.1`) and is never a valid public target.
|
|
280
|
+
if (expanded.startsWith("0000:0000:0000:0000:0000:0000:")) return "reserved";
|
|
281
|
+
|
|
273
282
|
return "public";
|
|
274
283
|
}
|
|
275
284
|
|