@aura-stack/auth 0.4.0-rc.5 → 0.5.0
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/@types/index.d.ts +8 -3
- package/dist/@types/router.d.cjs +0 -17
- package/dist/@types/router.d.d.ts +7 -2
- package/dist/@types/router.d.js +0 -1
- package/dist/actions/callback/access-token.cjs +130 -71
- package/dist/actions/callback/access-token.d.ts +9 -4
- package/dist/actions/callback/access-token.js +3 -4
- package/dist/actions/callback/callback.cjs +428 -152
- package/dist/actions/callback/callback.d.ts +11 -3
- package/dist/actions/callback/callback.js +12 -10
- package/dist/actions/callback/userinfo.cjs +159 -65
- package/dist/actions/callback/userinfo.d.ts +8 -3
- package/dist/actions/callback/userinfo.js +7 -6
- package/dist/actions/csrfToken/csrfToken.cjs +70 -19
- package/dist/actions/csrfToken/csrfToken.js +8 -7
- package/dist/actions/index.cjs +780 -348
- package/dist/actions/index.d.ts +6 -2
- package/dist/actions/index.js +23 -18
- package/dist/actions/session/session.cjs +107 -26
- package/dist/actions/session/session.js +7 -5
- package/dist/actions/signIn/authorization-url.cjs +288 -0
- package/dist/actions/signIn/authorization-url.d.ts +31 -0
- package/dist/actions/signIn/authorization-url.js +16 -0
- package/dist/actions/signIn/authorization.cjs +209 -211
- package/dist/actions/signIn/authorization.d.ts +32 -21
- package/dist/actions/signIn/authorization.js +12 -9
- package/dist/actions/signIn/signIn.cjs +470 -235
- package/dist/actions/signIn/signIn.d.ts +12 -3
- package/dist/actions/signIn/signIn.js +11 -8
- package/dist/actions/signOut/signOut.cjs +376 -228
- package/dist/actions/signOut/signOut.d.ts +1 -1
- package/dist/actions/signOut/signOut.js +10 -9
- package/dist/api/createApi.cjs +750 -0
- package/dist/api/createApi.d.ts +12 -0
- package/dist/api/createApi.js +19 -0
- package/dist/api/getSession.cjs +141 -0
- package/dist/api/getSession.d.ts +16 -0
- package/dist/api/getSession.js +10 -0
- package/dist/api/signIn.cjs +549 -0
- package/dist/api/signIn.d.ts +26 -0
- package/dist/api/signIn.js +15 -0
- package/dist/api/signOut.cjs +279 -0
- package/dist/api/signOut.d.ts +16 -0
- package/dist/api/signOut.js +13 -0
- package/dist/assert.cjs +150 -5
- package/dist/assert.d.ts +26 -3
- package/dist/assert.js +17 -3
- package/dist/{chunk-YRCB5FLE.js → chunk-2A5B7GWR.js} +52 -6
- package/dist/chunk-2GQLSIJ2.js +40 -0
- package/dist/chunk-2IR674WX.js +44 -0
- package/dist/chunk-3J5TUH2I.js +50 -0
- package/dist/chunk-4RWSYUKX.js +98 -0
- package/dist/chunk-4YHJ4IEQ.js +25 -0
- package/dist/chunk-54CZPKR4.js +25 -0
- package/dist/chunk-5LZ7TOM3.js +25 -0
- package/dist/chunk-7BE46WWS.js +88 -0
- package/dist/chunk-7YYXFKLR.js +35 -0
- package/dist/chunk-C3A37LQC.js +33 -0
- package/dist/chunk-CITNGXDA.js +31 -0
- package/dist/chunk-CWX724AG.js +78 -0
- package/dist/chunk-D2CSIUKP.js +74 -0
- package/dist/chunk-E6G5YCI6.js +25 -0
- package/dist/chunk-EBAMFRB7.js +34 -0
- package/dist/chunk-EEE7UM5T.js +25 -0
- package/dist/{chunk-HT4YLL7N.js → chunk-FPCVZUVG.js} +10 -8
- package/dist/chunk-FW4W3REU.js +25 -0
- package/dist/chunk-GNNBM2WJ.js +83 -0
- package/dist/chunk-IPKO6UQN.js +25 -0
- package/dist/chunk-JOCGX3RP.js +59 -0
- package/dist/chunk-KBXWTD6E.js +94 -0
- package/dist/chunk-KMMAZFSJ.js +25 -0
- package/dist/chunk-LATR3NIV.js +117 -0
- package/dist/chunk-LAYPUDQF.js +39 -0
- package/dist/chunk-LDU7A2JE.js +25 -0
- package/dist/chunk-LX3TJ2TJ.js +294 -0
- package/dist/chunk-NHZBQNRR.js +143 -0
- package/dist/chunk-OVHNRULD.js +33 -0
- package/dist/chunk-PDP3PHB3.js +127 -0
- package/dist/chunk-PHYNROD4.js +47 -0
- package/dist/chunk-QQEKY4XP.js +29 -0
- package/dist/chunk-U4RK4LKJ.js +348 -0
- package/dist/{chunk-RRLIF4PQ.js → chunk-U5663F2U.js} +16 -1
- package/dist/chunk-UN7X6SU5.js +53 -0
- package/dist/chunk-UZQJJD6A.js +100 -0
- package/dist/chunk-V6LLEAR4.js +80 -0
- package/dist/chunk-WHNDRO3N.js +50 -0
- package/dist/{chunk-W6LG7BFW.js → chunk-XY5R3EHH.js} +30 -23
- package/dist/client/client.cjs +135 -0
- package/dist/client/client.d.ts +85 -0
- package/dist/client/client.js +9 -0
- package/dist/client/index.cjs +135 -0
- package/dist/client/index.d.ts +14 -0
- package/dist/client/index.js +10 -0
- package/dist/context.cjs +1237 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.js +28 -0
- package/dist/cookie.cjs +57 -22
- package/dist/cookie.d.ts +11 -6
- package/dist/cookie.js +3 -2
- package/dist/createAuth.cjs +2320 -0
- package/dist/createAuth.d.ts +12 -0
- package/dist/createAuth.js +48 -0
- package/dist/env.cjs +78 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +12 -0
- package/dist/errors.cjs +17 -0
- package/dist/errors.d.ts +15 -4
- package/dist/errors.js +5 -1
- package/dist/headers.cjs +28 -2
- package/dist/headers.d.ts +25 -1
- package/dist/headers.js +9 -3
- package/dist/index-_aXtxb_s.d.ts +1377 -0
- package/dist/index.cjs +1843 -610
- package/dist/index.d.ts +11 -92
- package/dist/index.js +53 -85
- package/dist/jose.cjs +113 -38
- package/dist/jose.d.ts +12 -23
- package/dist/jose.js +17 -7
- package/dist/logger.cjs +424 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +17 -0
- package/dist/oauth/atlassian.cjs +57 -0
- package/dist/oauth/atlassian.d.ts +12 -0
- package/dist/oauth/atlassian.js +6 -0
- package/dist/oauth/bitbucket.cjs +19 -15
- package/dist/oauth/bitbucket.d.ts +7 -2
- package/dist/oauth/bitbucket.js +1 -1
- package/dist/oauth/discord.cjs +27 -24
- package/dist/oauth/discord.d.ts +7 -2
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/dropbox.cjs +53 -0
- package/dist/oauth/dropbox.d.ts +12 -0
- package/dist/oauth/dropbox.js +6 -0
- package/dist/oauth/figma.cjs +19 -16
- package/dist/oauth/figma.d.ts +7 -2
- package/dist/oauth/figma.js +1 -1
- package/dist/oauth/github.cjs +19 -8
- package/dist/oauth/github.d.ts +7 -2
- package/dist/oauth/github.js +1 -1
- package/dist/oauth/gitlab.cjs +19 -16
- package/dist/oauth/gitlab.d.ts +7 -2
- package/dist/oauth/gitlab.js +1 -1
- package/dist/oauth/index.cjs +529 -239
- package/dist/oauth/index.d.ts +7 -2
- package/dist/oauth/index.js +39 -22
- package/dist/oauth/mailchimp.cjs +19 -16
- package/dist/oauth/mailchimp.d.ts +7 -2
- package/dist/oauth/mailchimp.js +1 -1
- package/dist/oauth/notion.cjs +131 -0
- package/dist/oauth/notion.d.ts +12 -0
- package/dist/oauth/notion.js +9 -0
- package/dist/oauth/pinterest.cjs +19 -16
- package/dist/oauth/pinterest.d.ts +7 -2
- package/dist/oauth/pinterest.js +1 -1
- package/dist/oauth/spotify.cjs +19 -16
- package/dist/oauth/spotify.d.ts +7 -2
- package/dist/oauth/spotify.js +1 -1
- package/dist/oauth/strava.cjs +19 -16
- package/dist/oauth/strava.d.ts +7 -2
- package/dist/oauth/strava.js +1 -1
- package/dist/oauth/twitch.cjs +95 -0
- package/dist/oauth/twitch.d.ts +12 -0
- package/dist/oauth/twitch.js +7 -0
- package/dist/oauth/x.cjs +19 -16
- package/dist/oauth/x.d.ts +7 -2
- package/dist/oauth/x.js +1 -1
- package/dist/schemas.cjs +89 -42
- package/dist/schemas.d.ts +114 -18
- package/dist/schemas.js +5 -3
- package/dist/secure.cjs +73 -31
- package/dist/secure.d.ts +11 -11
- package/dist/secure.js +7 -6
- package/dist/utils.cjs +203 -90
- package/dist/utils.d.ts +21 -40
- package/dist/utils.js +21 -12
- package/package.json +9 -6
- package/dist/chunk-3EUWD5BB.js +0 -63
- package/dist/chunk-42XB3YCW.js +0 -22
- package/dist/chunk-6R2YZ4AC.js +0 -22
- package/dist/chunk-A3N4PVAT.js +0 -70
- package/dist/chunk-B737EUJV.js +0 -22
- package/dist/chunk-CXLATHS5.js +0 -143
- package/dist/chunk-E3OXBRYF.js +0 -22
- package/dist/chunk-EIL2FPSS.js +0 -22
- package/dist/chunk-EMKJA2GJ.js +0 -89
- package/dist/chunk-FIPU4MLT.js +0 -21
- package/dist/chunk-FKRDCWBF.js +0 -22
- package/dist/chunk-GA2SMTJO.js +0 -58
- package/dist/chunk-HP34YGGJ.js +0 -22
- package/dist/chunk-IKHPGFCW.js +0 -14
- package/dist/chunk-IUYZQTJV.js +0 -30
- package/dist/chunk-IVET23KF.js +0 -58
- package/dist/chunk-JVFTCTTE.js +0 -33
- package/dist/chunk-KRNOMBXQ.js +0 -22
- package/dist/chunk-KSWLO5ZU.js +0 -102
- package/dist/chunk-N2APGLXA.js +0 -71
- package/dist/chunk-N4SX7TZT.js +0 -96
- package/dist/chunk-STHEPPUZ.js +0 -11
- package/dist/chunk-TLE4PXY3.js +0 -39
- package/dist/index-B8jeIElf.d.ts +0 -679
- /package/dist/{chunk-DIVDFNAP.js → chunk-5X7JZMEF.js} +0 -0
package/dist/actions/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,14 +15,6 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/actions/index.ts
|
|
@@ -39,8 +29,7 @@ __export(actions_exports, {
|
|
|
39
29
|
module.exports = __toCommonJS(actions_exports);
|
|
40
30
|
|
|
41
31
|
// src/actions/signIn/signIn.ts
|
|
42
|
-
var
|
|
43
|
-
var import_router2 = require("@aura-stack/router");
|
|
32
|
+
var import_v42 = require("zod/v4");
|
|
44
33
|
|
|
45
34
|
// src/headers.ts
|
|
46
35
|
var cacheControl = {
|
|
@@ -49,12 +38,26 @@ var cacheControl = {
|
|
|
49
38
|
Expires: "0",
|
|
50
39
|
Vary: "Cookie"
|
|
51
40
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
var contentSecurityPolicy = {
|
|
42
|
+
"Content-Security-Policy": [
|
|
43
|
+
"default-src 'none'",
|
|
44
|
+
"script-src 'self'",
|
|
45
|
+
"frame-src 'none'",
|
|
46
|
+
"object-src 'none'",
|
|
47
|
+
"frame-ancestors 'none'",
|
|
48
|
+
"base-uri 'none'"
|
|
49
|
+
].join("; ")
|
|
50
|
+
};
|
|
51
|
+
var secureHeaders = {
|
|
52
|
+
"X-Content-Type-Options": "nosniff",
|
|
53
|
+
"X-Frame-Options": "DENY",
|
|
54
|
+
"Referrer-Policy": "strict-origin-when-cross-origin"
|
|
55
|
+
};
|
|
56
|
+
var secureApiHeaders = {
|
|
57
|
+
...cacheControl,
|
|
58
|
+
...contentSecurityPolicy,
|
|
59
|
+
...secureHeaders
|
|
60
|
+
};
|
|
58
61
|
|
|
59
62
|
// src/errors.ts
|
|
60
63
|
var OAuthProtocolError = class extends Error {
|
|
@@ -95,124 +98,322 @@ var isNativeError = (error) => {
|
|
|
95
98
|
var isOAuthProtocolError = (error) => {
|
|
96
99
|
return error instanceof OAuthProtocolError;
|
|
97
100
|
};
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
|
|
102
|
+
// src/api/signIn.ts
|
|
103
|
+
var import_router2 = require("@aura-stack/router");
|
|
104
|
+
|
|
105
|
+
// src/schemas.ts
|
|
106
|
+
var import_v4 = require("zod/v4");
|
|
107
|
+
var AuthorizeConfigSchema = import_v4.z.union([
|
|
108
|
+
(0, import_v4.string)().url(),
|
|
109
|
+
(0, import_v4.object)({
|
|
110
|
+
url: (0, import_v4.string)().url(),
|
|
111
|
+
params: (0, import_v4.object)({
|
|
112
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
113
|
+
scope: (0, import_v4.string)().optional()
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
]);
|
|
117
|
+
var AccessTokenConfigSchema = import_v4.z.union([
|
|
118
|
+
(0, import_v4.string)().url(),
|
|
119
|
+
(0, import_v4.object)({
|
|
120
|
+
url: (0, import_v4.string)().url(),
|
|
121
|
+
headers: import_v4.z.record((0, import_v4.string)(), (0, import_v4.string)()).optional()
|
|
122
|
+
})
|
|
123
|
+
]);
|
|
124
|
+
var UserInfoConfigSchema = import_v4.z.union([
|
|
125
|
+
(0, import_v4.string)().url(),
|
|
126
|
+
(0, import_v4.object)({
|
|
127
|
+
url: (0, import_v4.string)().url(),
|
|
128
|
+
headers: import_v4.z.record((0, import_v4.string)(), (0, import_v4.string)()).optional(),
|
|
129
|
+
method: (0, import_v4.string)().optional()
|
|
130
|
+
})
|
|
131
|
+
]);
|
|
132
|
+
var OAuthProviderCredentialsSchema = (0, import_v4.object)({
|
|
133
|
+
id: (0, import_v4.string)(),
|
|
134
|
+
name: (0, import_v4.string)(),
|
|
135
|
+
authorize: AuthorizeConfigSchema.optional(),
|
|
136
|
+
/** @deprecated */
|
|
137
|
+
authorizeURL: (0, import_v4.string)().url().optional(),
|
|
138
|
+
accessToken: AccessTokenConfigSchema,
|
|
139
|
+
/** @deprecated */
|
|
140
|
+
scope: (0, import_v4.string)().optional(),
|
|
141
|
+
userInfo: UserInfoConfigSchema,
|
|
142
|
+
/** @deprecated */
|
|
143
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
144
|
+
clientId: (0, import_v4.string)(),
|
|
145
|
+
clientSecret: (0, import_v4.string)(),
|
|
146
|
+
profile: import_v4.z.function().optional()
|
|
147
|
+
});
|
|
148
|
+
var OAuthProviderConfigSchema = (0, import_v4.object)({
|
|
149
|
+
authorize: AuthorizeConfigSchema.optional(),
|
|
150
|
+
/** @deprecated */
|
|
151
|
+
authorizeURL: (0, import_v4.string)().url().optional(),
|
|
152
|
+
accessToken: AccessTokenConfigSchema,
|
|
153
|
+
/** @deprecated */
|
|
154
|
+
scope: (0, import_v4.string)().optional(),
|
|
155
|
+
userInfo: UserInfoConfigSchema,
|
|
156
|
+
/** @deprecated */
|
|
157
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
158
|
+
clientId: (0, import_v4.string)(),
|
|
159
|
+
clientSecret: (0, import_v4.string)()
|
|
160
|
+
});
|
|
161
|
+
var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
162
|
+
redirectURI: (0, import_v4.string)(),
|
|
163
|
+
state: (0, import_v4.string)(),
|
|
164
|
+
codeChallenge: (0, import_v4.string)(),
|
|
165
|
+
codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
|
|
166
|
+
});
|
|
167
|
+
var OAuthAuthorizationResponse = (0, import_v4.object)({
|
|
168
|
+
state: (0, import_v4.string)({ message: "Missing state parameter in the OAuth authorization response." }),
|
|
169
|
+
code: (0, import_v4.string)({ message: "Missing code parameter in the OAuth authorization response." })
|
|
170
|
+
});
|
|
171
|
+
var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
172
|
+
error: (0, import_v4.enum)([
|
|
173
|
+
"invalid_request",
|
|
174
|
+
"unauthorized_client",
|
|
175
|
+
"access_denied",
|
|
176
|
+
"unsupported_response_type",
|
|
177
|
+
"invalid_scope",
|
|
178
|
+
"server_error",
|
|
179
|
+
"temporarily_unavailable"
|
|
180
|
+
]),
|
|
181
|
+
error_description: (0, import_v4.string)().optional(),
|
|
182
|
+
error_uri: (0, import_v4.string)().optional(),
|
|
183
|
+
state: (0, import_v4.string)()
|
|
184
|
+
});
|
|
185
|
+
var OAuthAccessToken = OAuthProviderConfigSchema.extend({
|
|
186
|
+
redirectURI: (0, import_v4.string)(),
|
|
187
|
+
code: (0, import_v4.string)(),
|
|
188
|
+
codeVerifier: (0, import_v4.string)().min(43).max(128)
|
|
189
|
+
});
|
|
190
|
+
var OAuthAccessTokenResponse = (0, import_v4.object)({
|
|
191
|
+
access_token: (0, import_v4.string)(),
|
|
192
|
+
token_type: (0, import_v4.string)().optional(),
|
|
193
|
+
expires_in: (0, import_v4.number)().optional(),
|
|
194
|
+
refresh_token: (0, import_v4.string)().optional(),
|
|
195
|
+
scope: (0, import_v4.union)([(0, import_v4.string)().optional().or((0, import_v4.null)()), (0, import_v4.array)((0, import_v4.string)()).optional()])
|
|
196
|
+
});
|
|
197
|
+
var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
|
|
198
|
+
error: (0, import_v4.enum)([
|
|
199
|
+
"invalid_request",
|
|
200
|
+
"invalid_client",
|
|
201
|
+
"invalid_grant",
|
|
202
|
+
"unauthorized_client",
|
|
203
|
+
"unsupported_grant_type",
|
|
204
|
+
"invalid_scope"
|
|
205
|
+
]),
|
|
206
|
+
error_description: (0, import_v4.string)().optional(),
|
|
207
|
+
error_uri: (0, import_v4.string)().optional()
|
|
208
|
+
});
|
|
209
|
+
var OAuthErrorResponse = (0, import_v4.object)({
|
|
210
|
+
error: (0, import_v4.string)(),
|
|
211
|
+
error_description: (0, import_v4.string)().optional()
|
|
212
|
+
});
|
|
213
|
+
var OAuthEnvSchema = (0, import_v4.object)({
|
|
214
|
+
clientId: import_v4.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
215
|
+
clientSecret: import_v4.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
216
|
+
});
|
|
101
217
|
|
|
102
218
|
// src/utils.ts
|
|
103
|
-
var
|
|
104
|
-
|
|
219
|
+
var import_router = require("@aura-stack/router");
|
|
220
|
+
|
|
221
|
+
// src/env.ts
|
|
222
|
+
var import_meta = {};
|
|
223
|
+
var env = new Proxy({}, {
|
|
224
|
+
get(_, prop) {
|
|
225
|
+
if (typeof prop !== "string") return void 0;
|
|
226
|
+
const hasProperty = (process2) => {
|
|
227
|
+
return process2 && Object.prototype.hasOwnProperty.call(process2, prop);
|
|
228
|
+
};
|
|
229
|
+
try {
|
|
230
|
+
if (typeof process !== "undefined" && hasProperty(process.env)) {
|
|
231
|
+
return process.env[prop];
|
|
232
|
+
}
|
|
233
|
+
if (typeof import_meta !== "undefined" && hasProperty(import_meta.env)) {
|
|
234
|
+
return import_meta.env[prop];
|
|
235
|
+
}
|
|
236
|
+
if (typeof Deno !== "undefined" && Deno.env?.get) {
|
|
237
|
+
return Deno.env.get(prop);
|
|
238
|
+
}
|
|
239
|
+
if (typeof Bun !== "undefined" && hasProperty(Bun.env)) {
|
|
240
|
+
return Bun.env[prop];
|
|
241
|
+
}
|
|
242
|
+
const globalValue = globalThis[prop];
|
|
243
|
+
return typeof globalValue === "string" ? globalValue : void 0;
|
|
244
|
+
} catch {
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
var getEnv = (key) => {
|
|
250
|
+
const keys = [`AURA_AUTH_${key.toUpperCase()}`, `AURA_${key.toUpperCase()}`, `AUTH_${key.toUpperCase()}`, key.toUpperCase()];
|
|
251
|
+
return env[keys.find((k) => env[k]) ?? ""];
|
|
105
252
|
};
|
|
106
|
-
|
|
107
|
-
|
|
253
|
+
|
|
254
|
+
// src/assert.ts
|
|
255
|
+
var import_crypto = require("@aura-stack/jose/crypto");
|
|
256
|
+
var unsafeChars = [
|
|
257
|
+
"<",
|
|
258
|
+
">",
|
|
259
|
+
'"',
|
|
260
|
+
"`",
|
|
261
|
+
" ",
|
|
262
|
+
"\r",
|
|
263
|
+
"\n",
|
|
264
|
+
" ",
|
|
265
|
+
"\\",
|
|
266
|
+
"%2F",
|
|
267
|
+
"%5C",
|
|
268
|
+
"%2f",
|
|
269
|
+
"%5c",
|
|
270
|
+
"\r\n",
|
|
271
|
+
"%0A",
|
|
272
|
+
"%0D",
|
|
273
|
+
"%0a",
|
|
274
|
+
"%0d",
|
|
275
|
+
"..",
|
|
276
|
+
"//",
|
|
277
|
+
"///",
|
|
278
|
+
"...",
|
|
279
|
+
"%20",
|
|
280
|
+
"\0"
|
|
281
|
+
];
|
|
282
|
+
var isValidURL = (value) => {
|
|
283
|
+
if (!new RegExp(/^https?:\/\/[^/]/).test(value)) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
const match = value.match(/^(https?:\/\/)(.*)$/);
|
|
287
|
+
if (!match) return false;
|
|
288
|
+
const rest = match[2];
|
|
289
|
+
for (const char of unsafeChars) {
|
|
290
|
+
if (rest.includes(char)) return false;
|
|
291
|
+
}
|
|
292
|
+
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()?#*+,;=:@-]*)*\/?$/;
|
|
293
|
+
return regex.test(match[0]);
|
|
108
294
|
};
|
|
109
|
-
var
|
|
110
|
-
return
|
|
111
|
-
const newKey = type === "snake" ? toSnakeCase(key) : toUpperCase(key);
|
|
112
|
-
return { ...previous, [newKey]: value };
|
|
113
|
-
}, {});
|
|
295
|
+
var isJWTPayloadWithToken = (payload) => {
|
|
296
|
+
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
114
297
|
};
|
|
115
|
-
var
|
|
116
|
-
if (
|
|
117
|
-
|
|
298
|
+
var isRelativeURL = (value) => {
|
|
299
|
+
if (value.length > 100) return false;
|
|
300
|
+
for (const char of unsafeChars) {
|
|
301
|
+
if (value.includes(char)) return false;
|
|
302
|
+
}
|
|
303
|
+
const regex = /^\/[a-zA-Z0-9\-_\/.?&=#]*\/?$/;
|
|
304
|
+
return regex.test(value);
|
|
305
|
+
};
|
|
306
|
+
var isSameOrigin = (origin, expected) => {
|
|
307
|
+
const originURL = new URL(origin);
|
|
308
|
+
const expectedURL = new URL(expected);
|
|
309
|
+
return equals(originURL.origin, expectedURL.origin);
|
|
118
310
|
};
|
|
119
|
-
var
|
|
311
|
+
var patternToRegex = (pattern) => {
|
|
120
312
|
try {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (path !== "/" && path.endsWith("/")) {
|
|
135
|
-
path = path.replace(/\/+$/, "/");
|
|
136
|
-
} else if (path !== "/") {
|
|
137
|
-
path = path.replace(/\/+$/, "");
|
|
138
|
-
}
|
|
139
|
-
return protocol + domain + path;
|
|
140
|
-
}
|
|
141
|
-
let sanitized = decodedURL.replace(/\/\.\.\//g, "/").replace(/\/\.\.$/, "").replace(/\.{2,}/g, "").replace(/\/{2,}/g, "/");
|
|
142
|
-
if (sanitized !== "/" && sanitized.endsWith("/")) {
|
|
143
|
-
sanitized = sanitized.replace(/\/+$/, "/");
|
|
144
|
-
} else if (sanitized !== "/") {
|
|
145
|
-
sanitized = sanitized.replace(/\/+$/, "");
|
|
146
|
-
}
|
|
147
|
-
return sanitized;
|
|
313
|
+
if (pattern.length > 2048) return null;
|
|
314
|
+
pattern = pattern.replace(/\\/g, "");
|
|
315
|
+
const match = pattern.match(/^(https?):\/\/([a-zA-Z0-9.*-]{1,253})(?::(\d{1,5}|\*))?(?:\/.*)?$/);
|
|
316
|
+
if (!match) return null;
|
|
317
|
+
const [, protocol, host, port] = match;
|
|
318
|
+
const hasWildcard = host.includes("*");
|
|
319
|
+
if (hasWildcard && !host.startsWith("*.")) return null;
|
|
320
|
+
if (hasWildcard && host.slice(2).includes("*")) return null;
|
|
321
|
+
const domain = hasWildcard ? host.slice(2) : host;
|
|
322
|
+
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
323
|
+
const hostRegex = hasWildcard ? `[^.]+\\.${escapedDomain}` : escapedDomain;
|
|
324
|
+
const portRegex = port === "*" ? ":\\d{1,5}" : port ? `:${port}` : "";
|
|
325
|
+
return new RegExp(`^${protocol}:\\/\\/${hostRegex}${portRegex}$`);
|
|
148
326
|
} catch {
|
|
149
|
-
return
|
|
327
|
+
return null;
|
|
150
328
|
}
|
|
151
329
|
};
|
|
152
|
-
var
|
|
153
|
-
if (!
|
|
154
|
-
if (!path.startsWith("/") || path.includes("://") || path.includes("\r") || path.includes("\n")) return false;
|
|
155
|
-
if (/[\x00-\x1F\x7F]/.test(path) || path.includes("\0")) return false;
|
|
156
|
-
const sanitized = sanitizeURL(path);
|
|
157
|
-
if (sanitized.includes("..")) return false;
|
|
158
|
-
return true;
|
|
159
|
-
};
|
|
160
|
-
var getNormalizedOriginPath = (path) => {
|
|
330
|
+
var isTrustedOrigin = (url, trustedOrigins) => {
|
|
331
|
+
if (!isValidURL(url) || trustedOrigins.length === 0) return false;
|
|
161
332
|
try {
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
333
|
+
const urlOrigin = new URL(url).origin;
|
|
334
|
+
for (const pattern of trustedOrigins) {
|
|
335
|
+
const regex = patternToRegex(pattern);
|
|
336
|
+
if (regex?.test(urlOrigin)) return true;
|
|
337
|
+
try {
|
|
338
|
+
if (isValidURL(pattern) && equals(new URL(pattern).origin, urlOrigin)) return true;
|
|
339
|
+
} catch {
|
|
340
|
+
}
|
|
341
|
+
}
|
|
166
342
|
} catch {
|
|
167
|
-
return sanitizeURL(path);
|
|
168
343
|
}
|
|
344
|
+
return false;
|
|
345
|
+
};
|
|
346
|
+
var timingSafeEqual = (a, b) => {
|
|
347
|
+
const bufferA = import_crypto.encoder.encode(a);
|
|
348
|
+
const bufferB = import_crypto.encoder.encode(b);
|
|
349
|
+
const len = Math.max(bufferA.length, bufferB.length);
|
|
350
|
+
let diff = 0;
|
|
351
|
+
for (let i = 0; i < len; i++) {
|
|
352
|
+
diff |= (bufferA[i] ?? 0) ^ (bufferB[i] ?? 0);
|
|
353
|
+
}
|
|
354
|
+
return diff === 0 && bufferA.length === bufferB.length;
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
// src/utils.ts
|
|
358
|
+
var AURA_AUTH_VERSION = "0.4.0";
|
|
359
|
+
var equals = (a, b) => {
|
|
360
|
+
if (a === null || b === null || a === void 0 || b === void 0) return false;
|
|
361
|
+
return a === b;
|
|
362
|
+
};
|
|
363
|
+
var getBaseURL = (request) => {
|
|
364
|
+
const url = new URL(request.url);
|
|
365
|
+
return `${url.origin}${url.pathname}`;
|
|
169
366
|
};
|
|
170
367
|
var toISOString = (date) => {
|
|
171
368
|
return new Date(date).toISOString();
|
|
172
369
|
};
|
|
173
|
-
var
|
|
174
|
-
|
|
175
|
-
|
|
370
|
+
var extractPath = (url) => {
|
|
371
|
+
const pathRegex = /^https?:\/\/[a-zA-Z0-9_\-\.]+(:\d+)?(\/.*)$/;
|
|
372
|
+
const match = url.match(pathRegex);
|
|
373
|
+
return match && match[2] ? match[2] : "/";
|
|
374
|
+
};
|
|
375
|
+
var getErrorName = (error) => {
|
|
376
|
+
if (error instanceof Error) {
|
|
377
|
+
return error.name;
|
|
176
378
|
}
|
|
177
|
-
return error
|
|
178
|
-
const key = issue.path.join(".");
|
|
179
|
-
return {
|
|
180
|
-
...previous,
|
|
181
|
-
[key]: {
|
|
182
|
-
code: issue.code,
|
|
183
|
-
message: issue.message
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
}, {});
|
|
379
|
+
return typeof error === "string" ? error : "UnknownError";
|
|
187
380
|
};
|
|
188
381
|
|
|
189
|
-
// src/
|
|
190
|
-
var
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
382
|
+
// src/jose.ts
|
|
383
|
+
var import_jose = require("@aura-stack/jose");
|
|
384
|
+
var import_jose2 = require("@aura-stack/jose/jose");
|
|
385
|
+
var import_crypto2 = require("@aura-stack/jose/crypto");
|
|
386
|
+
var jwtVerificationOptions = {
|
|
387
|
+
algorithms: ["HS256"],
|
|
388
|
+
typ: "JWT"
|
|
197
389
|
};
|
|
198
390
|
|
|
199
391
|
// src/secure.ts
|
|
200
392
|
var generateSecure = (length = 32) => {
|
|
201
|
-
return
|
|
393
|
+
return import_jose2.base64url.encode((0, import_crypto2.getRandomBytes)(length));
|
|
202
394
|
};
|
|
203
|
-
var
|
|
204
|
-
return
|
|
395
|
+
var createSecretValue = (length = 32) => {
|
|
396
|
+
return import_jose2.base64url.encode((0, import_crypto2.getRandomBytes)(length));
|
|
397
|
+
};
|
|
398
|
+
var createHash = async (data) => {
|
|
399
|
+
const subtle = (0, import_crypto2.getSubtleCrypto)();
|
|
400
|
+
const digest = await subtle.digest("SHA-256", import_crypto2.encoder.encode(data));
|
|
401
|
+
return import_jose2.base64url.encode(new Uint8Array(digest));
|
|
205
402
|
};
|
|
206
403
|
var createPKCE = async (verifier) => {
|
|
207
|
-
const
|
|
208
|
-
const
|
|
404
|
+
const byteLength = verifier ? void 0 : Math.floor(Math.random() * (96 - 32 + 1) + 32);
|
|
405
|
+
const codeVerifier = verifier ?? generateSecure(byteLength ?? 64);
|
|
406
|
+
if (codeVerifier.length < 43 || codeVerifier.length > 128) {
|
|
407
|
+
throw new AuthSecurityError("PKCE_VERIFIER_INVALID", "The code verifier must be between 43 and 128 characters in length.");
|
|
408
|
+
}
|
|
409
|
+
const codeChallenge = await createHash(codeVerifier);
|
|
209
410
|
return { codeVerifier, codeChallenge, method: "S256" };
|
|
210
411
|
};
|
|
211
412
|
var createCSRF = async (jose, csrfCookie) => {
|
|
212
413
|
try {
|
|
213
414
|
const token = generateSecure(32);
|
|
214
415
|
if (csrfCookie) {
|
|
215
|
-
await jose.verifyJWS(csrfCookie);
|
|
416
|
+
await jose.verifyJWS(csrfCookie, jwtVerificationOptions);
|
|
216
417
|
return csrfCookie;
|
|
217
418
|
}
|
|
218
419
|
return jose.signJWS({ token });
|
|
@@ -223,20 +424,18 @@ var createCSRF = async (jose, csrfCookie) => {
|
|
|
223
424
|
};
|
|
224
425
|
var verifyCSRF = async (jose, cookie, header) => {
|
|
225
426
|
try {
|
|
226
|
-
const cookiePayload = await jose.verifyJWS(cookie);
|
|
227
|
-
const headerPayload = await jose.verifyJWS(header);
|
|
427
|
+
const cookiePayload = await jose.verifyJWS(cookie, jwtVerificationOptions);
|
|
428
|
+
const headerPayload = await jose.verifyJWS(header, jwtVerificationOptions);
|
|
228
429
|
if (!isJWTPayloadWithToken(cookiePayload)) {
|
|
229
430
|
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
230
431
|
}
|
|
231
432
|
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
232
433
|
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
233
434
|
}
|
|
234
|
-
|
|
235
|
-
const headerBuffer = Buffer.from(headerPayload.token);
|
|
236
|
-
if (!equals(headerBuffer.length, cookieBuffer.length)) {
|
|
435
|
+
if (!equals(cookiePayload.token.length, headerPayload.token.length)) {
|
|
237
436
|
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
238
437
|
}
|
|
239
|
-
if (!
|
|
438
|
+
if (!timingSafeEqual(cookiePayload.token, headerPayload.token)) {
|
|
240
439
|
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
241
440
|
}
|
|
242
441
|
return true;
|
|
@@ -245,193 +444,244 @@ var verifyCSRF = async (jose, cookie, header) => {
|
|
|
245
444
|
}
|
|
246
445
|
};
|
|
247
446
|
|
|
248
|
-
// src/
|
|
249
|
-
var
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
error_uri: (0, import_zod.string)().optional()
|
|
306
|
-
});
|
|
307
|
-
var OAuthErrorResponse = (0, import_zod.object)({
|
|
308
|
-
error: (0, import_zod.string)(),
|
|
309
|
-
error_description: (0, import_zod.string)().optional()
|
|
310
|
-
});
|
|
311
|
-
var OAuthEnvSchema = (0, import_zod.object)({
|
|
312
|
-
clientId: import_zod.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
313
|
-
clientSecret: import_zod.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
314
|
-
});
|
|
447
|
+
// src/actions/signIn/authorization-url.ts
|
|
448
|
+
var setSearchParams = (url, params) => {
|
|
449
|
+
for (const [key, value] of Object.entries(params)) {
|
|
450
|
+
if (value !== void 0 && value !== "") {
|
|
451
|
+
url.searchParams.set(key, value);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
var buildAuthorizationURL = (oauth, redirect_uri, state, code_challenge, code_challenge_method) => {
|
|
456
|
+
const authorizeConfig = oauth.authorize;
|
|
457
|
+
const baseURL = typeof authorizeConfig === "string" ? authorizeConfig : authorizeConfig?.url ?? oauth.authorizeURL;
|
|
458
|
+
if (!baseURL) {
|
|
459
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "Missing authorization URL in OAuth provider configuration.");
|
|
460
|
+
}
|
|
461
|
+
const url = new URL(baseURL);
|
|
462
|
+
const authorizeParams = typeof authorizeConfig === "string" ? void 0 : authorizeConfig?.params;
|
|
463
|
+
setSearchParams(url, {
|
|
464
|
+
response_type: authorizeParams?.responseType ?? oauth.responseType ?? "code",
|
|
465
|
+
client_id: oauth.clientId,
|
|
466
|
+
redirect_uri,
|
|
467
|
+
state,
|
|
468
|
+
code_challenge,
|
|
469
|
+
code_challenge_method,
|
|
470
|
+
scope: authorizeParams?.scope ?? oauth.scope,
|
|
471
|
+
prompt: authorizeParams?.prompt,
|
|
472
|
+
response_mode: authorizeParams?.responseMode,
|
|
473
|
+
login_hint: authorizeParams?.loginHint,
|
|
474
|
+
nonce: authorizeParams?.nonce,
|
|
475
|
+
display: authorizeParams?.display,
|
|
476
|
+
audience: authorizeParams?.audience
|
|
477
|
+
});
|
|
478
|
+
return url.toString();
|
|
479
|
+
};
|
|
480
|
+
var createAuthorizationURL = async (oauth, redirectURI, ctx) => {
|
|
481
|
+
const state = createSecretValue();
|
|
482
|
+
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
483
|
+
const authorization = buildAuthorizationURL(oauth, redirectURI, state, codeChallenge, method);
|
|
484
|
+
const parsed = OAuthAuthorization.safeParse({ ...oauth, redirectURI, state, codeChallenge, codeChallengeMethod: method });
|
|
485
|
+
if (!parsed.success) {
|
|
486
|
+
ctx?.logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
487
|
+
structuredData: {
|
|
488
|
+
scope: oauth?.scope ?? "",
|
|
489
|
+
redirect_uri: redirectURI,
|
|
490
|
+
has_state: Boolean(state),
|
|
491
|
+
has_code_challenge: Boolean(codeChallenge),
|
|
492
|
+
code_challenge_method: method
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "The OAuth provider configuration is invalid.");
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
authorization,
|
|
499
|
+
state,
|
|
500
|
+
codeVerifier,
|
|
501
|
+
method
|
|
502
|
+
};
|
|
503
|
+
};
|
|
315
504
|
|
|
316
505
|
// src/actions/signIn/authorization.ts
|
|
317
|
-
var
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const protocol = headers
|
|
332
|
-
const host = headers
|
|
333
|
-
return
|
|
334
|
-
|
|
335
|
-
|
|
506
|
+
var getTrustedOrigins = async (request, trustedOrigins) => {
|
|
507
|
+
if (!trustedOrigins) return [];
|
|
508
|
+
const raw = typeof trustedOrigins === "function" ? await trustedOrigins(request) : trustedOrigins;
|
|
509
|
+
return Array.isArray(raw) ? raw : typeof raw === "string" ? [raw] : [];
|
|
510
|
+
};
|
|
511
|
+
var getBaseURL2 = async ({
|
|
512
|
+
ctx,
|
|
513
|
+
request,
|
|
514
|
+
headers: headersInit
|
|
515
|
+
}) => {
|
|
516
|
+
const origin = getEnv("BASE_URL") || ctx?.baseURL;
|
|
517
|
+
if (origin && origin !== "/") return origin;
|
|
518
|
+
if (ctx?.trustedProxyHeaders) {
|
|
519
|
+
const headers = headersInit && new Headers(headersInit) || request?.headers;
|
|
520
|
+
const protocol = headers?.get("Forwarded")?.match(/proto=([^;]+)/i)?.[1] ?? headers?.get("X-Forwarded-Proto") ?? "http";
|
|
521
|
+
const host = headers?.get("Host") ?? headers?.get("Forwarded")?.match(/host=([^;]+)/i)?.[1] ?? headers?.get("X-Forwarded-Host") ?? null;
|
|
522
|
+
if (host) return `${protocol}://${host}`;
|
|
523
|
+
throw new AuthInternalError(
|
|
524
|
+
"INVALID_OAUTH_CONFIGURATION",
|
|
525
|
+
"The URL cannot be constructed. Please set the BASE_URL environment variable or provide trusted proxy host headers."
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
return new URL(request?.url ?? "not-found").origin;
|
|
530
|
+
} catch (error) {
|
|
531
|
+
throw new AuthInternalError(
|
|
532
|
+
"INVALID_OAUTH_CONFIGURATION",
|
|
533
|
+
"The URL cannot be constructed. Please set the BASE_URL environment variable or enable trustedProxyHeaders.",
|
|
534
|
+
{ cause: error }
|
|
535
|
+
);
|
|
336
536
|
}
|
|
337
537
|
};
|
|
338
|
-
var
|
|
339
|
-
const
|
|
340
|
-
|
|
538
|
+
var getOriginURL = async (request, context) => {
|
|
539
|
+
const trustedOrigins = await getTrustedOrigins(request, context?.trustedOrigins);
|
|
540
|
+
trustedOrigins.push(new URL(request.url).origin);
|
|
541
|
+
const origin = await getBaseURL2({ request, ctx: context });
|
|
542
|
+
if (!isTrustedOrigin(origin, trustedOrigins)) {
|
|
543
|
+
context?.logger?.log("UNTRUSTED_ORIGIN", { structuredData: { origin } });
|
|
544
|
+
throw new AuthInternalError("UNTRUSTED_ORIGIN", "The constructed origin URL is not trusted.");
|
|
545
|
+
}
|
|
546
|
+
return origin;
|
|
547
|
+
};
|
|
548
|
+
var createRedirectURI = async (request, oauth, context) => {
|
|
549
|
+
const origin = await getOriginURL(request, context);
|
|
550
|
+
return `${origin}${context.basePath}/callback/${oauth}`;
|
|
341
551
|
};
|
|
342
|
-
var
|
|
552
|
+
var createSignInURL = async ({
|
|
553
|
+
request,
|
|
554
|
+
oauth,
|
|
555
|
+
ctx,
|
|
556
|
+
redirectTo
|
|
557
|
+
}) => {
|
|
558
|
+
const origin = await getOriginURL(request, ctx);
|
|
559
|
+
const searchParams = new URLSearchParams();
|
|
560
|
+
if (redirectTo !== void 0) searchParams.set("redirectTo", String(redirectTo));
|
|
561
|
+
return `${origin}${ctx.basePath}/signIn/${oauth}?${searchParams.toString()}`;
|
|
562
|
+
};
|
|
563
|
+
var createRedirectTo = async (request, redirectTo, context) => {
|
|
343
564
|
try {
|
|
344
565
|
const headers = request.headers;
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
566
|
+
const requestOrigin = await getOriginURL(request, context);
|
|
567
|
+
const origins = await getTrustedOrigins(request, context?.trustedOrigins);
|
|
568
|
+
const validateURL = (url) => {
|
|
569
|
+
if (!isRelativeURL(url) && !isValidURL(url)) return "/";
|
|
570
|
+
if (isRelativeURL(url)) return url;
|
|
571
|
+
if (origins.length > 0) {
|
|
572
|
+
if (isTrustedOrigin(url, origins)) {
|
|
573
|
+
const urlOrigin = new URL(url).origin;
|
|
574
|
+
for (const pattern of origins) {
|
|
575
|
+
const regex = patternToRegex(pattern);
|
|
576
|
+
if (regex?.test(urlOrigin)) {
|
|
577
|
+
return isSameOrigin(url, request.url) ? extractPath(url) : url;
|
|
578
|
+
}
|
|
579
|
+
if (isValidURL(pattern) && equals(new URL(pattern).origin, urlOrigin)) return url;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
context?.logger?.log("OPEN_REDIRECT_ATTACK");
|
|
583
|
+
return "/";
|
|
358
584
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if (referer) {
|
|
362
|
-
const refererURL = new URL(sanitizeURL(referer));
|
|
363
|
-
if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
|
|
364
|
-
throw new AuthSecurityError(
|
|
365
|
-
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
366
|
-
"The referer of the request does not match the hosted origin."
|
|
367
|
-
);
|
|
585
|
+
if (isSameOrigin(url, requestOrigin)) {
|
|
586
|
+
return extractPath(url);
|
|
368
587
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
|
|
374
|
-
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
375
|
-
}
|
|
376
|
-
return sanitizeURL(originURL.pathname);
|
|
377
|
-
}
|
|
378
|
-
return "/";
|
|
588
|
+
context?.logger?.log("OPEN_REDIRECT_ATTACK");
|
|
589
|
+
return "/";
|
|
590
|
+
};
|
|
591
|
+
return validateURL(redirectTo ?? headers.get("Referer") ?? headers.get("Origin") ?? "/");
|
|
379
592
|
} catch (error) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
593
|
+
context?.logger?.log("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED");
|
|
594
|
+
return "/";
|
|
384
595
|
}
|
|
385
596
|
};
|
|
386
597
|
|
|
598
|
+
// src/api/signIn.ts
|
|
599
|
+
var signIn = async (oauth, {
|
|
600
|
+
ctx,
|
|
601
|
+
headers: headersInit,
|
|
602
|
+
redirectTo = "/",
|
|
603
|
+
redirect,
|
|
604
|
+
request: requestInit
|
|
605
|
+
}) => {
|
|
606
|
+
const headers = new Headers(headersInit);
|
|
607
|
+
const provider = ctx.oauth[oauth];
|
|
608
|
+
if (!provider) {
|
|
609
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", `The OAuth provider "${oauth}" is not configured.`);
|
|
610
|
+
}
|
|
611
|
+
let request = requestInit;
|
|
612
|
+
if (!request) {
|
|
613
|
+
const origin = await getBaseURL2({ ctx, headers });
|
|
614
|
+
const url = `${origin}${ctx.basePath}/signIn/${oauth}`;
|
|
615
|
+
request = new Request(url, { headers });
|
|
616
|
+
}
|
|
617
|
+
if (redirect === false) {
|
|
618
|
+
const signInURL = await createSignInURL({ request, oauth, ctx, redirectTo });
|
|
619
|
+
return { redirect: false, signInURL };
|
|
620
|
+
}
|
|
621
|
+
const redirectURI = await createRedirectURI(request, oauth, ctx);
|
|
622
|
+
const redirectToValue = await createRedirectTo(request, redirectTo, ctx);
|
|
623
|
+
const { authorization, state, codeVerifier } = await createAuthorizationURL(provider, redirectURI, ctx);
|
|
624
|
+
ctx?.logger?.log("SIGN_IN_INITIATED", {
|
|
625
|
+
structuredData: { oauth_provider: oauth }
|
|
626
|
+
});
|
|
627
|
+
const headersList = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", authorization).setCookie(ctx.cookies.state.name, state, ctx.cookies.state.attributes).setCookie(ctx.cookies.redirectURI.name, redirectURI, ctx.cookies.redirectURI.attributes).setCookie(ctx.cookies.redirectTo.name, redirectToValue, ctx.cookies.redirectTo.attributes).setCookie(ctx.cookies.codeVerifier.name, codeVerifier, ctx.cookies.codeVerifier.attributes).toHeaders();
|
|
628
|
+
return Response.json(
|
|
629
|
+
{ redirect: redirect ?? true, signInURL: authorization },
|
|
630
|
+
{
|
|
631
|
+
status: redirect ?? true ? 302 : 200,
|
|
632
|
+
headers: headersList
|
|
633
|
+
}
|
|
634
|
+
);
|
|
635
|
+
};
|
|
636
|
+
|
|
387
637
|
// src/actions/signIn/signIn.ts
|
|
638
|
+
var import_router3 = require("@aura-stack/router");
|
|
388
639
|
var signInConfig = (oauth) => {
|
|
389
|
-
return (0,
|
|
640
|
+
return (0, import_router3.createEndpointConfig)("/signIn/:oauth", {
|
|
390
641
|
schemas: {
|
|
391
|
-
params:
|
|
392
|
-
oauth:
|
|
642
|
+
params: import_v42.z.object({
|
|
643
|
+
oauth: import_v42.z.enum(
|
|
393
644
|
Object.keys(oauth),
|
|
394
645
|
"The OAuth provider is not supported or invalid."
|
|
395
646
|
)
|
|
396
647
|
}),
|
|
397
|
-
searchParams:
|
|
398
|
-
|
|
648
|
+
searchParams: import_v42.z.object({
|
|
649
|
+
redirect: import_v42.z.stringbool().optional().default(true),
|
|
650
|
+
redirectTo: import_v42.z.string().optional()
|
|
399
651
|
})
|
|
400
652
|
}
|
|
401
653
|
});
|
|
402
654
|
};
|
|
403
655
|
var signInAction = (oauth) => {
|
|
404
|
-
return (0,
|
|
656
|
+
return (0, import_router3.createEndpoint)(
|
|
405
657
|
"GET",
|
|
406
658
|
"/signIn/:oauth",
|
|
407
659
|
async (ctx) => {
|
|
408
660
|
const {
|
|
409
661
|
request,
|
|
410
662
|
params: { oauth: oauth2 },
|
|
411
|
-
searchParams: { redirectTo },
|
|
412
|
-
context
|
|
663
|
+
searchParams: { redirectTo, redirect },
|
|
664
|
+
context
|
|
413
665
|
} = ctx;
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
{
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
);
|
|
666
|
+
const signInResult = await signIn(oauth2, {
|
|
667
|
+
ctx: context,
|
|
668
|
+
headers: request.headers,
|
|
669
|
+
redirect,
|
|
670
|
+
redirectTo,
|
|
671
|
+
request
|
|
672
|
+
});
|
|
673
|
+
if (!redirect) {
|
|
674
|
+
return Response.json(signInResult, { status: 200 });
|
|
675
|
+
}
|
|
676
|
+
return signInResult;
|
|
427
677
|
},
|
|
428
678
|
signInConfig(oauth)
|
|
429
679
|
);
|
|
430
680
|
};
|
|
431
681
|
|
|
432
682
|
// src/actions/callback/callback.ts
|
|
433
|
-
var
|
|
434
|
-
var
|
|
683
|
+
var import_v43 = require("zod/v4");
|
|
684
|
+
var import_router4 = require("@aura-stack/router");
|
|
435
685
|
|
|
436
686
|
// src/request.ts
|
|
437
687
|
var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
|
|
@@ -454,71 +704,127 @@ var getDefaultUserInfo = (profile) => {
|
|
|
454
704
|
image: profile?.image ?? profile?.picture
|
|
455
705
|
};
|
|
456
706
|
};
|
|
457
|
-
var getUserInfo = async (oauthConfig, accessToken) => {
|
|
458
|
-
const
|
|
707
|
+
var getUserInfo = async (oauthConfig, accessToken, logger) => {
|
|
708
|
+
const userInfoConfig = oauthConfig.userInfo;
|
|
709
|
+
const userinfoURL = typeof userInfoConfig === "string" ? userInfoConfig : userInfoConfig.url;
|
|
710
|
+
const extraHeaders = typeof userInfoConfig === "string" ? void 0 : userInfoConfig.headers;
|
|
711
|
+
const method = typeof userInfoConfig === "string" ? "GET" : (userInfoConfig.method ?? "GET").toUpperCase();
|
|
459
712
|
try {
|
|
460
|
-
|
|
461
|
-
|
|
713
|
+
logger?.log("OAUTH_USERINFO_REQUEST_INITIATED", {
|
|
714
|
+
structuredData: {
|
|
715
|
+
endpoint: userinfoURL
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
const response = await fetchAsync(userinfoURL, {
|
|
719
|
+
method,
|
|
462
720
|
headers: {
|
|
721
|
+
"User-Agent": `Aura Auth/${AURA_AUTH_VERSION}`,
|
|
463
722
|
Accept: "application/json",
|
|
464
|
-
Authorization: `Bearer ${accessToken}
|
|
723
|
+
Authorization: `Bearer ${accessToken}`,
|
|
724
|
+
...extraHeaders ?? {}
|
|
465
725
|
}
|
|
466
726
|
});
|
|
727
|
+
if (!response.ok) {
|
|
728
|
+
logger?.log("OAUTH_USERINFO_INVALID_RESPONSE");
|
|
729
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid userinfo response format");
|
|
730
|
+
}
|
|
467
731
|
const json = await response.json();
|
|
468
732
|
const { success, data } = OAuthErrorResponse.safeParse(json);
|
|
469
733
|
if (success) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
734
|
+
logger?.log("OAUTH_USERINFO_ERROR", {
|
|
735
|
+
message: "Error response received from OAuth userinfo endpoint",
|
|
736
|
+
structuredData: {
|
|
737
|
+
error: data.error,
|
|
738
|
+
error_description: data.error_description ?? ""
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "An error was received from the OAuth userinfo endpoint.");
|
|
474
742
|
}
|
|
743
|
+
logger?.log("OAUTH_USERINFO_SUCCESS");
|
|
475
744
|
return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
|
|
476
745
|
} catch (error) {
|
|
477
746
|
if (isOAuthProtocolError(error)) {
|
|
478
747
|
throw error;
|
|
479
748
|
}
|
|
749
|
+
logger?.log("OAUTH_USERINFO_REQUEST_FAILED");
|
|
480
750
|
if (isNativeError(error)) {
|
|
481
|
-
throw new OAuthProtocolError("
|
|
751
|
+
throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information from OAuth provider", "", {
|
|
752
|
+
cause: error
|
|
753
|
+
});
|
|
482
754
|
}
|
|
483
|
-
throw new OAuthProtocolError("
|
|
755
|
+
throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information", "", { cause: error });
|
|
484
756
|
}
|
|
485
757
|
};
|
|
486
758
|
|
|
487
759
|
// src/actions/callback/access-token.ts
|
|
488
|
-
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
|
|
489
|
-
const
|
|
490
|
-
if (!
|
|
491
|
-
|
|
492
|
-
|
|
760
|
+
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier, logger) => {
|
|
761
|
+
const { accessToken, clientId, clientSecret } = oauthConfig;
|
|
762
|
+
if (!clientId || !clientSecret || !redirectURI || !code || !codeVerifier || !accessToken) {
|
|
763
|
+
logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
764
|
+
structuredData: {
|
|
765
|
+
has_client_id: Boolean(clientId),
|
|
766
|
+
has_client_secret: Boolean(clientSecret),
|
|
767
|
+
has_access_token: Boolean(accessToken),
|
|
768
|
+
has_redirect_uri: Boolean(redirectURI),
|
|
769
|
+
has_code: Boolean(code),
|
|
770
|
+
has_code_verifier: Boolean(codeVerifier)
|
|
771
|
+
}
|
|
772
|
+
});
|
|
773
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "The OAuth provider configuration is invalid.");
|
|
493
774
|
}
|
|
494
|
-
const
|
|
775
|
+
const tokenURL = typeof accessToken === "string" ? accessToken : accessToken.url;
|
|
776
|
+
const extraHeaders = typeof accessToken === "string" ? void 0 : accessToken.headers;
|
|
495
777
|
try {
|
|
496
|
-
|
|
778
|
+
logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_INITIATED", {
|
|
779
|
+
structuredData: {
|
|
780
|
+
has_client_id: Boolean(clientId),
|
|
781
|
+
redirect_uri: redirectURI,
|
|
782
|
+
grant_type: "authorization_code"
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
const response = await fetchAsync(tokenURL, {
|
|
497
786
|
method: "POST",
|
|
498
787
|
headers: {
|
|
788
|
+
...extraHeaders ?? {},
|
|
499
789
|
Accept: "application/json",
|
|
500
790
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
501
791
|
},
|
|
502
792
|
body: new URLSearchParams({
|
|
503
793
|
client_id: clientId,
|
|
504
794
|
client_secret: clientSecret,
|
|
505
|
-
code
|
|
506
|
-
redirect_uri:
|
|
795
|
+
code,
|
|
796
|
+
redirect_uri: redirectURI,
|
|
507
797
|
grant_type: "authorization_code",
|
|
508
798
|
code_verifier: codeVerifier
|
|
509
799
|
}).toString()
|
|
510
800
|
});
|
|
801
|
+
if (!response.ok) {
|
|
802
|
+
logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
|
|
803
|
+
throw new OAuthProtocolError("invalid_request", "Invalid access token response");
|
|
804
|
+
}
|
|
511
805
|
const json = await response.json();
|
|
512
806
|
const token = OAuthAccessTokenResponse.safeParse(json);
|
|
513
807
|
if (!token.success) {
|
|
514
808
|
const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
|
|
515
809
|
if (!success) {
|
|
516
|
-
|
|
810
|
+
logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
|
|
811
|
+
throw new OAuthProtocolError("invalid_request", "Invalid access token response format");
|
|
517
812
|
}
|
|
518
|
-
|
|
813
|
+
logger?.log("OAUTH_ACCESS_TOKEN_ERROR", {
|
|
814
|
+
structuredData: {
|
|
815
|
+
error: data.error,
|
|
816
|
+
error_description: data.error_description ?? ""
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
throw new OAuthProtocolError("INVALID_ACCESS_TOKEN", "Failed to retrieve access token");
|
|
519
820
|
}
|
|
821
|
+
logger?.log("OAUTH_ACCESS_TOKEN_SUCCESS");
|
|
520
822
|
return token.data;
|
|
521
823
|
} catch (error) {
|
|
824
|
+
logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_FAILED");
|
|
825
|
+
if (error instanceof Error) {
|
|
826
|
+
throw new OAuthProtocolError("server_error", "Failed to communicate with OAuth provider", "", { cause: error });
|
|
827
|
+
}
|
|
522
828
|
throw error;
|
|
523
829
|
}
|
|
524
830
|
};
|
|
@@ -543,10 +849,11 @@ var setCookie = (cookieName, value, options2) => {
|
|
|
543
849
|
var expiredCookieAttributes = {
|
|
544
850
|
...defaultCookieOptions,
|
|
545
851
|
expires: /* @__PURE__ */ new Date(0),
|
|
546
|
-
maxAge: 0
|
|
852
|
+
maxAge: 0,
|
|
853
|
+
secure: true
|
|
547
854
|
};
|
|
548
855
|
var getCookie = (request, cookieName) => {
|
|
549
|
-
const cookies = request.headers.get("Cookie");
|
|
856
|
+
const cookies = request instanceof Request ? request.headers.get("Cookie") : request.get("Cookie");
|
|
550
857
|
if (!cookies) {
|
|
551
858
|
throw new AuthInternalError("COOKIE_NOT_FOUND", "No cookies found. There is no active session");
|
|
552
859
|
}
|
|
@@ -567,25 +874,38 @@ var createSessionCookie = async (jose, session) => {
|
|
|
567
874
|
|
|
568
875
|
// src/actions/callback/callback.ts
|
|
569
876
|
var callbackConfig = (oauth) => {
|
|
570
|
-
return (0,
|
|
877
|
+
return (0, import_router4.createEndpointConfig)("/callback/:oauth", {
|
|
571
878
|
schemas: {
|
|
572
|
-
params:
|
|
573
|
-
oauth:
|
|
879
|
+
params: import_v43.z.object({
|
|
880
|
+
oauth: import_v43.z.enum(
|
|
574
881
|
Object.keys(oauth),
|
|
575
882
|
"The OAuth provider is not supported or invalid."
|
|
576
883
|
)
|
|
577
884
|
}),
|
|
578
|
-
searchParams:
|
|
579
|
-
code:
|
|
580
|
-
state:
|
|
885
|
+
searchParams: import_v43.z.object({
|
|
886
|
+
code: import_v43.z.string("Missing code parameter in the OAuth authorization response."),
|
|
887
|
+
state: import_v43.z.string("Missing state parameter in the OAuth authorization response.")
|
|
581
888
|
})
|
|
582
889
|
},
|
|
583
|
-
|
|
890
|
+
use: [
|
|
584
891
|
(ctx) => {
|
|
585
|
-
const
|
|
892
|
+
const {
|
|
893
|
+
searchParams,
|
|
894
|
+
context: { logger }
|
|
895
|
+
} = ctx;
|
|
896
|
+
const response = OAuthAuthorizationErrorResponse.safeParse(searchParams);
|
|
586
897
|
if (response.success) {
|
|
587
898
|
const { error, error_description } = response.data;
|
|
588
|
-
|
|
899
|
+
const criticalAuthErrors = ["access_denied", "server_error"];
|
|
900
|
+
const severity = criticalAuthErrors.includes(error.toLowerCase()) ? "critical" : "warning";
|
|
901
|
+
logger?.log("OAUTH_AUTHORIZATION_ERROR", {
|
|
902
|
+
severity,
|
|
903
|
+
structuredData: {
|
|
904
|
+
error,
|
|
905
|
+
error_description: error_description ?? ""
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
throw new OAuthProtocolError(error, error_description || "OAuth Authorization Error");
|
|
589
909
|
}
|
|
590
910
|
return ctx;
|
|
591
911
|
}
|
|
@@ -593,7 +913,7 @@ var callbackConfig = (oauth) => {
|
|
|
593
913
|
});
|
|
594
914
|
};
|
|
595
915
|
var callbackAction = (oauth) => {
|
|
596
|
-
return (0,
|
|
916
|
+
return (0, import_router4.createEndpoint)(
|
|
597
917
|
"GET",
|
|
598
918
|
"/callback/:oauth",
|
|
599
919
|
async (ctx) => {
|
|
@@ -601,31 +921,54 @@ var callbackAction = (oauth) => {
|
|
|
601
921
|
request,
|
|
602
922
|
params: { oauth: oauth2 },
|
|
603
923
|
searchParams: { code, state },
|
|
604
|
-
context
|
|
924
|
+
context
|
|
605
925
|
} = ctx;
|
|
926
|
+
const { oauth: providers, cookies, jose, logger, trustedOrigins } = context;
|
|
606
927
|
const oauthConfig = providers[oauth2];
|
|
607
928
|
const cookieState = getCookie(request, cookies.state.name);
|
|
929
|
+
const codeVerifier = getCookie(request, cookies.codeVerifier.name);
|
|
608
930
|
const cookieRedirectTo = getCookie(request, cookies.redirectTo.name);
|
|
609
931
|
const cookieRedirectURI = getCookie(request, cookies.redirectURI.name);
|
|
610
|
-
|
|
611
|
-
|
|
932
|
+
if (!timingSafeEqual(cookieState, state)) {
|
|
933
|
+
logger?.log("MISMATCHING_STATE", {
|
|
934
|
+
structuredData: {
|
|
935
|
+
oauth_provider: oauth2
|
|
936
|
+
}
|
|
937
|
+
});
|
|
612
938
|
throw new AuthSecurityError(
|
|
613
939
|
"MISMATCHING_STATE",
|
|
614
940
|
"The provided state passed in the OAuth response does not match the stored state."
|
|
615
941
|
);
|
|
616
942
|
}
|
|
617
|
-
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
618
|
-
const
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
943
|
+
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier, logger);
|
|
944
|
+
const origins = await getTrustedOrigins(request, trustedOrigins);
|
|
945
|
+
const requestOrigin = await getOriginURL(request, context);
|
|
946
|
+
if (!isRelativeURL(cookieRedirectTo)) {
|
|
947
|
+
const isValid = origins.length > 0 ? isTrustedOrigin(cookieRedirectTo, origins) : isSameOrigin(cookieRedirectTo, requestOrigin);
|
|
948
|
+
if (!isValid) {
|
|
949
|
+
logger?.log("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", {
|
|
950
|
+
structuredData: {
|
|
951
|
+
redirect_path: cookieRedirectTo,
|
|
952
|
+
provider: oauth2,
|
|
953
|
+
has_trusted_origins: origins.length > 0,
|
|
954
|
+
request_origin: requestOrigin
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
throw new AuthSecurityError(
|
|
958
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
959
|
+
"Invalid redirect path. Potential open redirect attack detected."
|
|
960
|
+
);
|
|
961
|
+
}
|
|
624
962
|
}
|
|
625
|
-
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
963
|
+
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token, logger);
|
|
626
964
|
const sessionCookie = await createSessionCookie(jose, userInfo);
|
|
627
965
|
const csrfToken = await createCSRF(jose);
|
|
628
|
-
|
|
966
|
+
logger?.log("OAUTH_CALLBACK_SUCCESS", {
|
|
967
|
+
structuredData: {
|
|
968
|
+
provider: oauth2
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
const headers = new import_router4.HeadersBuilder(cacheControl).setHeader("Location", cookieRedirectTo).setCookie(cookies.sessionToken.name, sessionCookie, cookies.sessionToken.attributes).setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes).setCookie(cookies.state.name, "", expiredCookieAttributes).setCookie(cookies.redirectURI.name, "", expiredCookieAttributes).setCookie(cookies.redirectTo.name, "", expiredCookieAttributes).setCookie(cookies.codeVerifier.name, "", expiredCookieAttributes).toHeaders();
|
|
629
972
|
return Response.json({ oauth: oauth2 }, { status: 302, headers });
|
|
630
973
|
},
|
|
631
974
|
callbackConfig(oauth)
|
|
@@ -633,74 +976,161 @@ var callbackAction = (oauth) => {
|
|
|
633
976
|
};
|
|
634
977
|
|
|
635
978
|
// src/actions/session/session.ts
|
|
636
|
-
var
|
|
637
|
-
|
|
979
|
+
var import_router5 = require("@aura-stack/router");
|
|
980
|
+
|
|
981
|
+
// src/api/getSession.ts
|
|
982
|
+
var getSession = async ({ ctx, headers }) => {
|
|
983
|
+
try {
|
|
984
|
+
const session = getCookie(new Headers(headers), ctx.cookies.sessionToken.name);
|
|
985
|
+
const decoded = await ctx.jose.decodeJWT(session);
|
|
986
|
+
ctx?.logger?.log("AUTH_SESSION_VALID");
|
|
987
|
+
const { exp, iat, jti, nbf, aud, iss, ...user } = decoded;
|
|
988
|
+
return {
|
|
989
|
+
session: {
|
|
990
|
+
user,
|
|
991
|
+
expires: toISOString(exp * 1e3)
|
|
992
|
+
},
|
|
993
|
+
authenticated: true
|
|
994
|
+
};
|
|
995
|
+
} catch (error) {
|
|
996
|
+
ctx?.logger?.log("AUTH_SESSION_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
997
|
+
return { session: null, authenticated: false };
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
|
|
1001
|
+
// src/actions/session/session.ts
|
|
1002
|
+
var sessionAction = (0, import_router5.createEndpoint)("GET", "/session", async (ctx) => {
|
|
638
1003
|
const {
|
|
639
1004
|
request,
|
|
640
|
-
context: {
|
|
1005
|
+
context: { cookies }
|
|
641
1006
|
} = ctx;
|
|
642
1007
|
try {
|
|
643
|
-
const session =
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
return Response.json(
|
|
1008
|
+
const session = await getSession({ ctx: ctx.context, headers: request.headers });
|
|
1009
|
+
if (!session.authenticated) {
|
|
1010
|
+
throw new AuthInternalError("INVALID_JWT_TOKEN", "Session not authenticated");
|
|
1011
|
+
}
|
|
1012
|
+
return Response.json(session, { headers: secureApiHeaders });
|
|
648
1013
|
} catch (error) {
|
|
649
|
-
const headers = new
|
|
650
|
-
return Response.json({
|
|
1014
|
+
const headers = new import_router5.HeadersBuilder(secureApiHeaders).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
1015
|
+
return Response.json({ session: null, authenticated: false }, { status: 401, headers });
|
|
651
1016
|
}
|
|
652
1017
|
});
|
|
653
1018
|
|
|
654
1019
|
// src/actions/signOut/signOut.ts
|
|
655
|
-
var
|
|
656
|
-
var
|
|
657
|
-
|
|
1020
|
+
var import_v44 = require("zod/v4");
|
|
1021
|
+
var import_router7 = require("@aura-stack/router");
|
|
1022
|
+
|
|
1023
|
+
// src/api/signOut.ts
|
|
1024
|
+
var import_router6 = require("@aura-stack/router");
|
|
1025
|
+
var signOut = async ({
|
|
1026
|
+
ctx,
|
|
1027
|
+
headers: headersInit,
|
|
1028
|
+
redirectTo = "/",
|
|
1029
|
+
skipCSRFCheck = false
|
|
1030
|
+
}) => {
|
|
1031
|
+
const headers = new Headers(headersInit);
|
|
1032
|
+
const header = headers.get("X-CSRF-Token");
|
|
1033
|
+
let session = null;
|
|
1034
|
+
let csrfToken = null;
|
|
1035
|
+
try {
|
|
1036
|
+
session = getCookie(headers, ctx.cookies.sessionToken.name);
|
|
1037
|
+
} catch {
|
|
1038
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1039
|
+
}
|
|
1040
|
+
try {
|
|
1041
|
+
csrfToken = getCookie(headers, ctx.cookies.csrfToken.name);
|
|
1042
|
+
} catch {
|
|
1043
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
1044
|
+
}
|
|
1045
|
+
ctx?.logger?.log("SIGN_OUT_ATTEMPT", {
|
|
1046
|
+
structuredData: {
|
|
1047
|
+
has_session: Boolean(session),
|
|
1048
|
+
has_csrf_token: Boolean(csrfToken),
|
|
1049
|
+
has_csrf_header: Boolean(header),
|
|
1050
|
+
skip_csrf_check: skipCSRFCheck
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
if (!session) {
|
|
1054
|
+
ctx?.logger?.log("SESSION_TOKEN_MISSING");
|
|
1055
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1056
|
+
}
|
|
1057
|
+
if (!skipCSRFCheck) {
|
|
1058
|
+
if (!csrfToken) {
|
|
1059
|
+
ctx?.logger?.log("CSRF_TOKEN_MISSING");
|
|
1060
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
1061
|
+
}
|
|
1062
|
+
if (!header) {
|
|
1063
|
+
ctx?.logger?.log("CSRF_HEADER_MISSING");
|
|
1064
|
+
throw new AuthSecurityError("CSRF_HEADER_MISSING", "The CSRF header is missing.");
|
|
1065
|
+
}
|
|
1066
|
+
try {
|
|
1067
|
+
await verifyCSRF(ctx.jose, csrfToken, header);
|
|
1068
|
+
} catch (error) {
|
|
1069
|
+
ctx?.logger?.log("CSRF_TOKEN_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
1070
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "CSRF token verification failed");
|
|
1071
|
+
}
|
|
1072
|
+
ctx?.logger?.log("SIGN_OUT_CSRF_VERIFIED");
|
|
1073
|
+
} else {
|
|
1074
|
+
try {
|
|
1075
|
+
await ctx.jose.verifyJWS(csrfToken);
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
ctx?.logger?.log("CSRF_TOKEN_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
1078
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "CSRF token verification failed");
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
try {
|
|
1082
|
+
await ctx.jose.decodeJWT(session);
|
|
1083
|
+
ctx?.logger?.log("SIGN_OUT_SUCCESS");
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
ctx?.logger?.log("INVALID_JWT_TOKEN", { structuredData: { error_type: getErrorName(error) } });
|
|
1086
|
+
}
|
|
1087
|
+
const headersList = new import_router6.HeadersBuilder(secureApiHeaders).setHeader("Location", redirectTo).setCookie(ctx.cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(ctx.cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
1088
|
+
return Response.json(
|
|
1089
|
+
{ redirect: Boolean(redirectTo), url: redirectTo },
|
|
1090
|
+
{
|
|
1091
|
+
status: 202,
|
|
1092
|
+
headers: headersList
|
|
1093
|
+
}
|
|
1094
|
+
);
|
|
1095
|
+
};
|
|
1096
|
+
|
|
1097
|
+
// src/actions/signOut/signOut.ts
|
|
1098
|
+
var config = (0, import_router7.createEndpointConfig)({
|
|
658
1099
|
schemas: {
|
|
659
|
-
searchParams:
|
|
660
|
-
token_type_hint:
|
|
661
|
-
redirectTo:
|
|
1100
|
+
searchParams: import_v44.z.object({
|
|
1101
|
+
token_type_hint: import_v44.z.literal("session_token"),
|
|
1102
|
+
redirectTo: import_v44.z.string().optional()
|
|
662
1103
|
})
|
|
663
1104
|
}
|
|
664
1105
|
});
|
|
665
|
-
var signOutAction = (0,
|
|
1106
|
+
var signOutAction = (0, import_router7.createEndpoint)(
|
|
666
1107
|
"POST",
|
|
667
1108
|
"/signOut",
|
|
668
1109
|
async (ctx) => {
|
|
669
1110
|
const {
|
|
670
1111
|
request,
|
|
671
|
-
headers,
|
|
672
1112
|
searchParams: { redirectTo },
|
|
673
|
-
context
|
|
1113
|
+
context
|
|
674
1114
|
} = ctx;
|
|
675
|
-
const
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
680
|
-
}
|
|
681
|
-
if (!csrfToken) {
|
|
682
|
-
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
683
|
-
}
|
|
684
|
-
if (!header) {
|
|
685
|
-
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
|
|
686
|
-
}
|
|
687
|
-
await verifyCSRF(jose, csrfToken, header);
|
|
688
|
-
await jose.decodeJWT(session);
|
|
689
|
-
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
690
|
-
const location = createRedirectTo(
|
|
691
|
-
new Request(normalizedOriginPath, {
|
|
692
|
-
headers: headers.toHeaders()
|
|
1115
|
+
const baseURL = getBaseURL(request);
|
|
1116
|
+
const location = await createRedirectTo(
|
|
1117
|
+
new Request(baseURL, {
|
|
1118
|
+
headers: request.headers
|
|
693
1119
|
}),
|
|
694
|
-
redirectTo
|
|
1120
|
+
redirectTo,
|
|
1121
|
+
context
|
|
695
1122
|
);
|
|
696
|
-
|
|
697
|
-
|
|
1123
|
+
return await signOut({
|
|
1124
|
+
ctx: context,
|
|
1125
|
+
headers: request.headers,
|
|
1126
|
+
redirectTo: location
|
|
1127
|
+
});
|
|
698
1128
|
},
|
|
699
1129
|
config
|
|
700
1130
|
);
|
|
701
1131
|
|
|
702
1132
|
// src/actions/csrfToken/csrfToken.ts
|
|
703
|
-
var
|
|
1133
|
+
var import_router8 = require("@aura-stack/router");
|
|
704
1134
|
var getCSRFToken = (request, cookieName) => {
|
|
705
1135
|
try {
|
|
706
1136
|
return getCookie(request, cookieName);
|
|
@@ -708,14 +1138,16 @@ var getCSRFToken = (request, cookieName) => {
|
|
|
708
1138
|
return void 0;
|
|
709
1139
|
}
|
|
710
1140
|
};
|
|
711
|
-
var csrfTokenAction = (0,
|
|
1141
|
+
var csrfTokenAction = (0, import_router8.createEndpoint)("GET", "/csrfToken", async (ctx) => {
|
|
712
1142
|
const {
|
|
713
1143
|
request,
|
|
714
|
-
context: { jose, cookies }
|
|
1144
|
+
context: { jose, cookies, logger }
|
|
715
1145
|
} = ctx;
|
|
716
1146
|
const token = getCSRFToken(request, cookies.csrfToken.name);
|
|
1147
|
+
logger?.log("CSRF_TOKEN_REQUESTED", { structuredData: { has_token: Boolean(token) } });
|
|
717
1148
|
const csrfToken = await createCSRF(jose, token);
|
|
718
|
-
|
|
1149
|
+
logger?.log("CSRF_TOKEN_ISSUED", { structuredData: { issued: Boolean(csrfToken) } });
|
|
1150
|
+
const headers = new Headers(secureApiHeaders);
|
|
719
1151
|
headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
|
|
720
1152
|
return Response.json({ csrfToken }, { headers });
|
|
721
1153
|
});
|