@aura-stack/auth 0.1.0 → 0.2.0-rc.1
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 +5 -5
- package/dist/@types/router.d.d.ts +5 -5
- package/dist/@types/utility.d.ts +1 -5
- package/dist/actions/callback/access-token.cjs +95 -74
- package/dist/actions/callback/access-token.d.ts +7 -7
- package/dist/actions/callback/access-token.js +5 -3
- package/dist/actions/callback/callback.cjs +171 -249
- package/dist/actions/callback/callback.d.ts +6 -6
- package/dist/actions/callback/callback.js +10 -10
- package/dist/actions/callback/userinfo.cjs +81 -57
- package/dist/actions/callback/userinfo.d.ts +5 -5
- package/dist/actions/callback/userinfo.js +7 -5
- package/dist/actions/csrfToken/csrfToken.cjs +36 -105
- package/dist/actions/csrfToken/csrfToken.js +6 -6
- package/dist/actions/index.cjs +309 -444
- package/dist/actions/index.d.ts +5 -5
- package/dist/actions/index.js +19 -19
- package/dist/actions/session/session.cjs +25 -109
- package/dist/actions/session/session.js +4 -5
- package/dist/actions/signIn/authorization.cjs +96 -87
- package/dist/actions/signIn/authorization.d.ts +5 -5
- package/dist/actions/signIn/authorization.js +5 -5
- package/dist/actions/signIn/signIn.cjs +133 -242
- package/dist/actions/signIn/signIn.d.ts +6 -6
- package/dist/actions/signIn/signIn.js +8 -9
- package/dist/actions/signOut/signOut.cjs +136 -282
- package/dist/actions/signOut/signOut.js +8 -9
- package/dist/assert.cjs +5 -0
- package/dist/assert.d.ts +10 -1
- package/dist/assert.js +3 -1
- package/dist/chunk-3EUWD5BB.js +63 -0
- package/dist/chunk-6R2YZ4AC.js +22 -0
- package/dist/chunk-A3N4PVAT.js +70 -0
- package/dist/chunk-B737EUJV.js +22 -0
- package/dist/{chunk-256KIVJL.js → chunk-CXLATHS5.js} +53 -9
- package/dist/{chunk-6SM22VVJ.js → chunk-EIL2FPSS.js} +5 -1
- package/dist/{chunk-VFTYH33W.js → chunk-EMKJA2GJ.js} +36 -8
- package/dist/{chunk-UJJ7R56J.js → chunk-GA2SMTJO.js} +16 -10
- package/dist/chunk-HP34YGGJ.js +22 -0
- package/dist/chunk-HT4YLL7N.js +35 -0
- package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
- package/dist/{chunk-RLT4RFKV.js → chunk-IVET23KF.js} +21 -8
- package/dist/{chunk-XXJKNKGQ.js → chunk-JVFTCTTE.js} +9 -13
- package/dist/chunk-KSWLO5ZU.js +102 -0
- package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
- package/dist/{chunk-CAKJT3KS.js → chunk-N4SX7TZT.js} +21 -17
- package/dist/chunk-RRLIF4PQ.js +55 -0
- package/dist/chunk-TLE4PXY3.js +39 -0
- package/dist/chunk-W6LG7BFW.js +197 -0
- package/dist/{chunk-HMRKN75I.js → chunk-YRCB5FLE.js} +14 -9
- package/dist/chunk-ZNCZVF6U.js +14 -0
- package/dist/cookie.cjs +140 -99
- package/dist/cookie.d.ts +35 -44
- package/dist/cookie.js +10 -17
- package/dist/errors.cjs +85 -0
- package/dist/errors.d.ts +49 -0
- package/dist/errors.js +18 -0
- package/dist/{index-DpfbvTZ_.d.ts → index-DkaLJFn8.d.ts} +192 -61
- package/dist/index.cjs +543 -443
- package/dist/index.d.ts +6 -6
- package/dist/index.js +42 -28
- package/dist/jose.cjs +25 -14
- package/dist/jose.d.ts +4 -1
- package/dist/jose.js +5 -4
- package/dist/oauth/bitbucket.d.ts +5 -5
- package/dist/oauth/discord.cjs +0 -1
- package/dist/oauth/discord.d.ts +5 -5
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/figma.d.ts +5 -5
- package/dist/oauth/github.d.ts +5 -5
- package/dist/oauth/gitlab.d.ts +5 -5
- package/dist/oauth/index.cjs +176 -6
- package/dist/oauth/index.d.ts +5 -5
- package/dist/oauth/index.js +19 -4
- package/dist/oauth/mailchimp.cjs +46 -0
- package/dist/oauth/mailchimp.d.ts +7 -0
- package/dist/oauth/mailchimp.js +6 -0
- package/dist/oauth/pinterest.cjs +46 -0
- package/dist/oauth/pinterest.d.ts +7 -0
- package/dist/oauth/pinterest.js +6 -0
- package/dist/oauth/spotify.d.ts +5 -5
- package/dist/oauth/strava.cjs +46 -0
- package/dist/oauth/strava.d.ts +7 -0
- package/dist/oauth/strava.js +6 -0
- package/dist/oauth/x.d.ts +5 -5
- package/dist/{response.cjs → request.cjs} +14 -10
- package/dist/request.d.ts +13 -0
- package/dist/request.js +6 -0
- package/dist/schemas.cjs +43 -37
- package/dist/schemas.d.ts +67 -64
- package/dist/schemas.js +3 -1
- package/dist/secure.cjs +32 -24
- package/dist/secure.d.ts +5 -5
- package/dist/secure.js +4 -3
- package/dist/utils.cjs +90 -15
- package/dist/utils.d.ts +11 -2
- package/dist/utils.js +8 -4
- package/package.json +8 -7
- package/dist/chunk-FJUDBLCP.js +0 -59
- package/dist/chunk-HGJ4TXY4.js +0 -137
- package/dist/chunk-JAPMIE6S.js +0 -10
- package/dist/chunk-LLR722CL.js +0 -96
- package/dist/chunk-SJPDVKUS.js +0 -112
- package/dist/chunk-SMQO5WD7.js +0 -30
- package/dist/chunk-UTDLUEEG.js +0 -31
- package/dist/chunk-ZV4BH47P.js +0 -154
- package/dist/error.cjs +0 -88
- package/dist/error.d.ts +0 -62
- package/dist/error.js +0 -16
- package/dist/response.d.ts +0 -10
- package/dist/response.js +0 -6
package/dist/actions/index.cjs
CHANGED
|
@@ -39,71 +39,64 @@ __export(actions_exports, {
|
|
|
39
39
|
module.exports = __toCommonJS(actions_exports);
|
|
40
40
|
|
|
41
41
|
// src/actions/signIn/signIn.ts
|
|
42
|
-
var
|
|
42
|
+
var import_zod2 = require("zod");
|
|
43
43
|
var import_router2 = require("@aura-stack/router");
|
|
44
44
|
|
|
45
|
-
// src/
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
// src/headers.ts
|
|
46
|
+
var cacheControl = {
|
|
47
|
+
"Cache-Control": "no-store",
|
|
48
|
+
Pragma: "no-cache",
|
|
49
|
+
Expires: "0",
|
|
50
|
+
Vary: "Cookie"
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
// src/secure.ts
|
|
53
|
-
var
|
|
54
|
+
var import_crypto = __toESM(require("crypto"), 1);
|
|
54
55
|
|
|
55
56
|
// src/utils.ts
|
|
56
57
|
var import_router = require("@aura-stack/router");
|
|
57
58
|
|
|
58
|
-
// src/
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
// src/errors.ts
|
|
60
|
+
var OAuthProtocolError = class extends Error {
|
|
61
|
+
type = "OAUTH_PROTOCOL_ERROR";
|
|
62
|
+
error;
|
|
63
|
+
errorURI;
|
|
64
|
+
constructor(error, description, errorURI, options2) {
|
|
65
|
+
super(description, options2);
|
|
66
|
+
this.error = error;
|
|
67
|
+
this.errorURI = errorURI;
|
|
68
|
+
this.name = new.target.name;
|
|
69
|
+
Error.captureStackTrace(this, new.target);
|
|
64
70
|
}
|
|
65
71
|
};
|
|
66
|
-
var
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
var AuthInternalError = class extends Error {
|
|
73
|
+
type = "AUTH_INTERNAL_ERROR";
|
|
74
|
+
code;
|
|
75
|
+
constructor(code, message, options2) {
|
|
76
|
+
super(message, options2);
|
|
77
|
+
this.code = code;
|
|
78
|
+
this.name = new.target.name;
|
|
79
|
+
Error.captureStackTrace(this, new.target);
|
|
70
80
|
}
|
|
71
81
|
};
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
var AuthSecurityError = class extends Error {
|
|
83
|
+
type = "AUTH_SECURITY_ERROR";
|
|
84
|
+
code;
|
|
85
|
+
constructor(code, message, options2) {
|
|
86
|
+
super(message, options2);
|
|
87
|
+
this.code = code;
|
|
88
|
+
this.name = new.target.name;
|
|
89
|
+
Error.captureStackTrace(this, new.target);
|
|
76
90
|
}
|
|
77
91
|
};
|
|
78
|
-
var
|
|
79
|
-
return error instanceof
|
|
92
|
+
var isNativeError = (error) => {
|
|
93
|
+
return error instanceof Error;
|
|
80
94
|
};
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
if (isAuthError(error)) {
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
86
|
-
throw new AuthError("invalid_request", error.message ?? message);
|
|
87
|
-
}
|
|
95
|
+
var isOAuthProtocolError = (error) => {
|
|
96
|
+
return error instanceof OAuthProtocolError;
|
|
88
97
|
};
|
|
89
|
-
var
|
|
90
|
-
|
|
91
|
-
INVALID_REQUEST: "invalid_request",
|
|
92
|
-
UNAUTHORIZED_CLIENT: "unauthorized_client",
|
|
93
|
-
ACCESS_DENIED: "access_denied",
|
|
94
|
-
UNSUPPORTED_RESPONSE_TYPE: "unsupported_response_type",
|
|
95
|
-
INVALID_SCOPE: "invalid_scope",
|
|
96
|
-
SERVER_ERROR: "server_error",
|
|
97
|
-
TEMPORARILY_UNAVAILABLE: "temporarily_unavailable"
|
|
98
|
-
},
|
|
99
|
-
ACCESS_TOKEN: {
|
|
100
|
-
INVALID_REQUEST: "invalid_request",
|
|
101
|
-
INVALID_CLIENT: "invalid_client",
|
|
102
|
-
INVALID_GRANT: "invalid_grant",
|
|
103
|
-
UNAUTHORIZED_CLIENT: "unauthorized_client",
|
|
104
|
-
UNSUPPORTED_GRANT_TYPE: "unsupported_grant_type",
|
|
105
|
-
INVALID_SCOPE: "invalid_scope"
|
|
106
|
-
}
|
|
98
|
+
var isAuthSecurityError = (error) => {
|
|
99
|
+
return error instanceof AuthSecurityError;
|
|
107
100
|
};
|
|
108
101
|
|
|
109
102
|
// src/utils.ts
|
|
@@ -123,9 +116,9 @@ var equals = (a, b) => {
|
|
|
123
116
|
if (a === null || b === null || a === void 0 || b === void 0) return false;
|
|
124
117
|
return a === b;
|
|
125
118
|
};
|
|
126
|
-
var sanitizeURL = (
|
|
119
|
+
var sanitizeURL = (url) => {
|
|
127
120
|
try {
|
|
128
|
-
let decodedURL = decodeURIComponent(
|
|
121
|
+
let decodedURL = decodeURIComponent(url).trim();
|
|
129
122
|
const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
|
|
130
123
|
let protocol = "";
|
|
131
124
|
let rest = decodedURL;
|
|
@@ -153,7 +146,7 @@ var sanitizeURL = (url2) => {
|
|
|
153
146
|
}
|
|
154
147
|
return sanitized;
|
|
155
148
|
} catch {
|
|
156
|
-
return
|
|
149
|
+
return url.trim();
|
|
157
150
|
}
|
|
158
151
|
};
|
|
159
152
|
var isValidRelativePath = (path) => {
|
|
@@ -166,10 +159,10 @@ var isValidRelativePath = (path) => {
|
|
|
166
159
|
};
|
|
167
160
|
var getNormalizedOriginPath = (path) => {
|
|
168
161
|
try {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return `${
|
|
162
|
+
const url = new URL(path);
|
|
163
|
+
url.hash = "";
|
|
164
|
+
url.search = "";
|
|
165
|
+
return `${url.origin}${url.pathname}`;
|
|
173
166
|
} catch {
|
|
174
167
|
return sanitizeURL(path);
|
|
175
168
|
}
|
|
@@ -177,13 +170,38 @@ var getNormalizedOriginPath = (path) => {
|
|
|
177
170
|
var toISOString = (date) => {
|
|
178
171
|
return new Date(date).toISOString();
|
|
179
172
|
};
|
|
173
|
+
var formatZodError = (error) => {
|
|
174
|
+
if (!error.issues || error.issues.length === 0) {
|
|
175
|
+
return {};
|
|
176
|
+
}
|
|
177
|
+
return error.issues.reduce((previous, issue) => {
|
|
178
|
+
const key = issue.path.join(".");
|
|
179
|
+
return {
|
|
180
|
+
...previous,
|
|
181
|
+
[key]: {
|
|
182
|
+
code: issue.code,
|
|
183
|
+
message: issue.message
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}, {});
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// src/assert.ts
|
|
190
|
+
var isValidURL = (value) => {
|
|
191
|
+
if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
|
|
192
|
+
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
193
|
+
return regex.test(value);
|
|
194
|
+
};
|
|
195
|
+
var isJWTPayloadWithToken = (payload) => {
|
|
196
|
+
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
197
|
+
};
|
|
180
198
|
|
|
181
199
|
// src/secure.ts
|
|
182
200
|
var generateSecure = (length = 32) => {
|
|
183
|
-
return
|
|
201
|
+
return import_crypto.default.randomBytes(length).toString("base64url");
|
|
184
202
|
};
|
|
185
203
|
var createHash = (data, base = "hex") => {
|
|
186
|
-
return
|
|
204
|
+
return import_crypto.default.createHash("sha256").update(data).digest().toString(base);
|
|
187
205
|
};
|
|
188
206
|
var createPKCE = async (verifier) => {
|
|
189
207
|
const codeVerifier = verifier ?? generateSecure(86);
|
|
@@ -205,182 +223,51 @@ var createCSRF = async (jose, csrfCookie) => {
|
|
|
205
223
|
};
|
|
206
224
|
var verifyCSRF = async (jose, cookie, header) => {
|
|
207
225
|
try {
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
226
|
+
const cookiePayload = await jose.verifyJWS(cookie);
|
|
227
|
+
const headerPayload = await jose.verifyJWS(header);
|
|
228
|
+
if (!isJWTPayloadWithToken(cookiePayload)) {
|
|
229
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
230
|
+
}
|
|
231
|
+
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
232
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
233
|
+
}
|
|
234
|
+
const cookieBuffer = Buffer.from(cookiePayload.token);
|
|
235
|
+
const headerBuffer = Buffer.from(headerPayload.token);
|
|
212
236
|
if (!equals(headerBuffer.length, cookieBuffer.length)) {
|
|
213
|
-
throw new
|
|
237
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
214
238
|
}
|
|
215
|
-
if (!
|
|
216
|
-
throw new
|
|
239
|
+
if (!import_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
|
|
240
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
217
241
|
}
|
|
218
242
|
return true;
|
|
219
243
|
} catch {
|
|
220
|
-
throw new
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
// src/cookie.ts
|
|
225
|
-
var import_cookie = require("cookie");
|
|
226
|
-
|
|
227
|
-
// src/assert.ts
|
|
228
|
-
var isRequest = (value) => {
|
|
229
|
-
return typeof Request !== "undefined" && value instanceof Request;
|
|
230
|
-
};
|
|
231
|
-
var isValidURL = (value) => {
|
|
232
|
-
if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
|
|
233
|
-
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
234
|
-
return regex.test(value);
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// src/cookie.ts
|
|
238
|
-
var import_cookie2 = require("cookie");
|
|
239
|
-
var COOKIE_NAME = "aura-auth";
|
|
240
|
-
var defaultCookieOptions = {
|
|
241
|
-
httpOnly: true,
|
|
242
|
-
sameSite: "lax",
|
|
243
|
-
path: "/",
|
|
244
|
-
maxAge: 60 * 60 * 24 * 15
|
|
245
|
-
};
|
|
246
|
-
var defaultStandardCookieConfig = {
|
|
247
|
-
secure: false,
|
|
248
|
-
httpOnly: true,
|
|
249
|
-
prefix: ""
|
|
250
|
-
};
|
|
251
|
-
var defaultSecureCookieConfig = {
|
|
252
|
-
secure: true,
|
|
253
|
-
prefix: "__Secure-"
|
|
254
|
-
};
|
|
255
|
-
var defaultHostCookieConfig = {
|
|
256
|
-
secure: true,
|
|
257
|
-
prefix: "__Host-",
|
|
258
|
-
path: "/",
|
|
259
|
-
domain: void 0
|
|
260
|
-
};
|
|
261
|
-
var expiredCookieOptions = {
|
|
262
|
-
...defaultCookieOptions,
|
|
263
|
-
expires: /* @__PURE__ */ new Date(0),
|
|
264
|
-
maxAge: 0
|
|
265
|
-
};
|
|
266
|
-
var defineDefaultCookieOptions = (options2) => {
|
|
267
|
-
return {
|
|
268
|
-
name: options2?.name ?? COOKIE_NAME,
|
|
269
|
-
prefix: options2?.prefix ?? (options2?.secure ? "__Secure-" : ""),
|
|
270
|
-
...defaultCookieOptions,
|
|
271
|
-
...options2
|
|
272
|
-
};
|
|
273
|
-
};
|
|
274
|
-
var setCookie = (cookieName, value, options2) => {
|
|
275
|
-
const { prefix, name } = defineDefaultCookieOptions(options2);
|
|
276
|
-
const cookieNameWithPrefix = `${prefix}${name}.${cookieName}`;
|
|
277
|
-
return (0, import_cookie.serialize)(cookieNameWithPrefix, value, {
|
|
278
|
-
...defaultCookieOptions,
|
|
279
|
-
...options2
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
|
-
var getCookie = (petition, cookie, options2, optional = false) => {
|
|
283
|
-
const cookies = isRequest(petition) ? petition.headers.get("Cookie") : petition.headers.getSetCookie().join("; ");
|
|
284
|
-
if (!cookies) {
|
|
285
|
-
if (optional) {
|
|
286
|
-
return "";
|
|
287
|
-
}
|
|
288
|
-
throw new AuthError("invalid_request", "No cookies found. There is no active session");
|
|
289
|
-
}
|
|
290
|
-
const { name, prefix } = defineDefaultCookieOptions(options2);
|
|
291
|
-
const parsedCookies = (0, import_cookie.parse)(cookies);
|
|
292
|
-
const value = parsedCookies[`${prefix}${name}.${cookie}`];
|
|
293
|
-
if (value === void 0) {
|
|
294
|
-
if (optional) {
|
|
295
|
-
return "";
|
|
296
|
-
}
|
|
297
|
-
throw new AuthError("invalid_request", `Cookie "${cookie}" not found. There is no active session`);
|
|
298
|
-
}
|
|
299
|
-
return value;
|
|
300
|
-
};
|
|
301
|
-
var createSessionCookie = async (session, cookieOptions, jose) => {
|
|
302
|
-
try {
|
|
303
|
-
const encoded = await jose.encodeJWT(session);
|
|
304
|
-
return setCookie("sessionToken", encoded, cookieOptions);
|
|
305
|
-
} catch (error) {
|
|
306
|
-
throw new AuthError("server_error", "Failed to create session cookie", { cause: error });
|
|
307
|
-
}
|
|
308
|
-
};
|
|
309
|
-
var secureCookieOptions = (request, cookieOptions, trustedProxyHeaders) => {
|
|
310
|
-
const name = cookieOptions.name ?? COOKIE_NAME;
|
|
311
|
-
const isSecure = trustedProxyHeaders ? request.url.startsWith("https://") || request.headers.get("X-Forwarded-Proto") === "https" || request.headers.get("Forwarded")?.includes("proto=https") : request.url.startsWith("https://");
|
|
312
|
-
if (!cookieOptions.options?.httpOnly) {
|
|
313
|
-
console.warn(
|
|
314
|
-
"[WARNING]: Cookie is configured without HttpOnly. This allows JavaScript access via document.cookie and increases XSS risk."
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
if (cookieOptions.options?.domain === "*") {
|
|
318
|
-
console.warn("[WARNING]: Cookie 'Domain' is set to '*', which is insecure. Avoid wildcard domains.");
|
|
319
|
-
}
|
|
320
|
-
if (!isSecure) {
|
|
321
|
-
const options2 = cookieOptions.options;
|
|
322
|
-
if (options2?.secure) {
|
|
323
|
-
console.warn(
|
|
324
|
-
"[WARNING]: The 'Secure' attribute will be disabled for this cookie. Serve over HTTPS to enforce Secure cookies."
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
if (options2?.sameSite == "none") {
|
|
328
|
-
console.warn("[WARNING]: SameSite=None without a secure connection can be blocked by browsers.");
|
|
329
|
-
}
|
|
330
|
-
if (process.env.NODE_ENV === "production") {
|
|
331
|
-
console.warn("[WARNING]: In production, ensure cookies are served over HTTPS to maintain security.");
|
|
332
|
-
}
|
|
333
|
-
return {
|
|
334
|
-
...defaultCookieOptions,
|
|
335
|
-
...cookieOptions.options,
|
|
336
|
-
sameSite: options2?.sameSite === "none" ? "lax" : options2?.sameSite ?? "lax",
|
|
337
|
-
...defaultStandardCookieConfig,
|
|
338
|
-
name
|
|
339
|
-
};
|
|
244
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
340
245
|
}
|
|
341
|
-
return cookieOptions.strategy === "host" ? {
|
|
342
|
-
...defaultCookieOptions,
|
|
343
|
-
...cookieOptions.options,
|
|
344
|
-
...defaultHostCookieConfig,
|
|
345
|
-
name
|
|
346
|
-
} : { ...defaultCookieOptions, ...cookieOptions.options, ...defaultSecureCookieConfig, name };
|
|
347
|
-
};
|
|
348
|
-
var expireCookie = (name, options2) => {
|
|
349
|
-
return setCookie(name, "", { ...options2, ...expiredCookieOptions });
|
|
350
|
-
};
|
|
351
|
-
var oauthCookie = (options2) => {
|
|
352
|
-
return {
|
|
353
|
-
...options2,
|
|
354
|
-
secure: options2.secure,
|
|
355
|
-
httpOnly: options2.httpOnly,
|
|
356
|
-
maxAge: 5 * 60,
|
|
357
|
-
expires: new Date(Date.now() + 5 * 60 * 1e3)
|
|
358
|
-
};
|
|
359
246
|
};
|
|
360
247
|
|
|
361
248
|
// src/schemas.ts
|
|
362
|
-
var
|
|
363
|
-
var OAuthProviderConfigSchema = (0,
|
|
364
|
-
authorizeURL: (0,
|
|
365
|
-
accessToken: (0,
|
|
366
|
-
scope: (0,
|
|
367
|
-
userInfo: (0,
|
|
368
|
-
responseType: (0,
|
|
369
|
-
clientId: (0,
|
|
370
|
-
clientSecret: (0,
|
|
249
|
+
var import_zod = require("zod");
|
|
250
|
+
var OAuthProviderConfigSchema = (0, import_zod.object)({
|
|
251
|
+
authorizeURL: (0, import_zod.string)().url(),
|
|
252
|
+
accessToken: (0, import_zod.string)().url(),
|
|
253
|
+
scope: (0, import_zod.string)().optional(),
|
|
254
|
+
userInfo: (0, import_zod.string)().url(),
|
|
255
|
+
responseType: (0, import_zod.enum)(["code", "token", "id_token"]),
|
|
256
|
+
clientId: (0, import_zod.string)(),
|
|
257
|
+
clientSecret: (0, import_zod.string)()
|
|
371
258
|
});
|
|
372
259
|
var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
373
|
-
redirectURI: (0,
|
|
374
|
-
state: (0,
|
|
375
|
-
codeChallenge: (0,
|
|
376
|
-
codeChallengeMethod: (0,
|
|
260
|
+
redirectURI: (0, import_zod.string)(),
|
|
261
|
+
state: (0, import_zod.string)(),
|
|
262
|
+
codeChallenge: (0, import_zod.string)(),
|
|
263
|
+
codeChallengeMethod: (0, import_zod.enum)(["plain", "S256"])
|
|
377
264
|
});
|
|
378
|
-
var OAuthAuthorizationResponse = (0,
|
|
379
|
-
state: (0,
|
|
380
|
-
code: (0,
|
|
265
|
+
var OAuthAuthorizationResponse = (0, import_zod.object)({
|
|
266
|
+
state: (0, import_zod.string)({ message: "Missing state parameter in the OAuth authorization response." }),
|
|
267
|
+
code: (0, import_zod.string)({ message: "Missing code parameter in the OAuth authorization response." })
|
|
381
268
|
});
|
|
382
|
-
var OAuthAuthorizationErrorResponse = (0,
|
|
383
|
-
error: (0,
|
|
269
|
+
var OAuthAuthorizationErrorResponse = (0, import_zod.object)({
|
|
270
|
+
error: (0, import_zod.enum)([
|
|
384
271
|
"invalid_request",
|
|
385
272
|
"unauthorized_client",
|
|
386
273
|
"access_denied",
|
|
@@ -389,24 +276,24 @@ var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
|
389
276
|
"server_error",
|
|
390
277
|
"temporarily_unavailable"
|
|
391
278
|
]),
|
|
392
|
-
error_description: (0,
|
|
393
|
-
error_uri: (0,
|
|
394
|
-
state: (0,
|
|
279
|
+
error_description: (0, import_zod.string)().optional(),
|
|
280
|
+
error_uri: (0, import_zod.string)().optional(),
|
|
281
|
+
state: (0, import_zod.string)()
|
|
395
282
|
});
|
|
396
283
|
var OAuthAccessToken = OAuthProviderConfigSchema.extend({
|
|
397
|
-
redirectURI: (0,
|
|
398
|
-
code: (0,
|
|
399
|
-
codeVerifier: (0,
|
|
284
|
+
redirectURI: (0, import_zod.string)(),
|
|
285
|
+
code: (0, import_zod.string)(),
|
|
286
|
+
codeVerifier: (0, import_zod.string)().min(43).max(128)
|
|
400
287
|
});
|
|
401
|
-
var OAuthAccessTokenResponse = (0,
|
|
402
|
-
access_token: (0,
|
|
403
|
-
token_type: (0,
|
|
404
|
-
expires_in: (0,
|
|
405
|
-
refresh_token: (0,
|
|
406
|
-
scope: (0,
|
|
288
|
+
var OAuthAccessTokenResponse = (0, import_zod.object)({
|
|
289
|
+
access_token: (0, import_zod.string)(),
|
|
290
|
+
token_type: (0, import_zod.string)().optional(),
|
|
291
|
+
expires_in: (0, import_zod.number)().optional(),
|
|
292
|
+
refresh_token: (0, import_zod.string)().optional(),
|
|
293
|
+
scope: (0, import_zod.string)().optional().or((0, import_zod.null)())
|
|
407
294
|
});
|
|
408
|
-
var OAuthAccessTokenErrorResponse = (0,
|
|
409
|
-
error: (0,
|
|
295
|
+
var OAuthAccessTokenErrorResponse = (0, import_zod.object)({
|
|
296
|
+
error: (0, import_zod.enum)([
|
|
410
297
|
"invalid_request",
|
|
411
298
|
"invalid_client",
|
|
412
299
|
"invalid_grant",
|
|
@@ -414,19 +301,24 @@ var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
|
|
|
414
301
|
"unsupported_grant_type",
|
|
415
302
|
"invalid_scope"
|
|
416
303
|
]),
|
|
417
|
-
error_description: (0,
|
|
418
|
-
error_uri: (0,
|
|
304
|
+
error_description: (0, import_zod.string)().optional(),
|
|
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()
|
|
419
310
|
});
|
|
420
|
-
var
|
|
421
|
-
|
|
422
|
-
|
|
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.")
|
|
423
314
|
});
|
|
424
315
|
|
|
425
316
|
// src/actions/signIn/authorization.ts
|
|
426
317
|
var createAuthorizationURL = (oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod) => {
|
|
427
318
|
const parsed = OAuthAuthorization.safeParse({ ...oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod });
|
|
428
319
|
if (!parsed.success) {
|
|
429
|
-
|
|
320
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
321
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
430
322
|
}
|
|
431
323
|
const { authorizeURL, ...options2 } = parsed.data;
|
|
432
324
|
const { userInfo, accessToken, clientSecret, ...required } = options2;
|
|
@@ -444,8 +336,8 @@ var getOriginURL = (request, trustedProxyHeaders) => {
|
|
|
444
336
|
}
|
|
445
337
|
};
|
|
446
338
|
var createRedirectURI = (request, oauth, basePath, trustedProxyHeaders) => {
|
|
447
|
-
const
|
|
448
|
-
return `${
|
|
339
|
+
const url = getOriginURL(request, trustedProxyHeaders);
|
|
340
|
+
return `${url.origin}${basePath}/callback/${oauth}`;
|
|
449
341
|
};
|
|
450
342
|
var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
451
343
|
try {
|
|
@@ -459,15 +351,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
459
351
|
}
|
|
460
352
|
const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
|
|
461
353
|
if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
|
|
462
|
-
throw new
|
|
354
|
+
throw new AuthSecurityError(
|
|
355
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
356
|
+
"The redirectTo parameter does not match the hosted origin."
|
|
357
|
+
);
|
|
463
358
|
}
|
|
464
359
|
return sanitizeURL(redirectToURL.pathname);
|
|
465
360
|
}
|
|
466
361
|
if (referer) {
|
|
467
362
|
const refererURL = new URL(sanitizeURL(referer));
|
|
468
363
|
if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
|
|
469
|
-
throw new
|
|
470
|
-
|
|
364
|
+
throw new AuthSecurityError(
|
|
365
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
471
366
|
"The referer of the request does not match the hosted origin."
|
|
472
367
|
);
|
|
473
368
|
}
|
|
@@ -476,16 +371,16 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
476
371
|
if (origin) {
|
|
477
372
|
const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
|
|
478
373
|
if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
|
|
479
|
-
throw new
|
|
374
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
480
375
|
}
|
|
481
376
|
return sanitizeURL(originURL.pathname);
|
|
482
377
|
}
|
|
483
378
|
return "/";
|
|
484
379
|
} catch (error) {
|
|
485
|
-
if (
|
|
380
|
+
if (isAuthSecurityError(error)) {
|
|
486
381
|
throw error;
|
|
487
382
|
}
|
|
488
|
-
throw new
|
|
383
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
489
384
|
}
|
|
490
385
|
};
|
|
491
386
|
|
|
@@ -493,9 +388,14 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
493
388
|
var signInConfig = (oauth) => {
|
|
494
389
|
return (0, import_router2.createEndpointConfig)("/signIn/:oauth", {
|
|
495
390
|
schemas: {
|
|
496
|
-
params:
|
|
497
|
-
oauth:
|
|
498
|
-
|
|
391
|
+
params: import_zod2.z.object({
|
|
392
|
+
oauth: import_zod2.z.enum(
|
|
393
|
+
Object.keys(oauth),
|
|
394
|
+
"The OAuth provider is not supported or invalid."
|
|
395
|
+
)
|
|
396
|
+
}),
|
|
397
|
+
searchParams: import_zod2.z.object({
|
|
398
|
+
redirectTo: import_zod2.z.string().optional()
|
|
499
399
|
})
|
|
500
400
|
}
|
|
501
401
|
});
|
|
@@ -507,67 +407,41 @@ var signInAction = (oauth) => {
|
|
|
507
407
|
async (ctx) => {
|
|
508
408
|
const {
|
|
509
409
|
request,
|
|
510
|
-
params: { oauth: oauth2
|
|
410
|
+
params: { oauth: oauth2 },
|
|
411
|
+
searchParams: { redirectTo },
|
|
511
412
|
context: { oauth: providers, cookies, trustedProxyHeaders, basePath }
|
|
512
413
|
} = ctx;
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
525
|
-
const codeVerifierCookie = setCookie("code_verifier", codeVerifier, oauthCookie(cookieOptions));
|
|
526
|
-
const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
|
|
527
|
-
const headers = new Headers();
|
|
528
|
-
headers.set("Location", authorization);
|
|
529
|
-
headers.append("Set-Cookie", stateCookie);
|
|
530
|
-
headers.append("Set-Cookie", redirectURICookie);
|
|
531
|
-
headers.append("Set-Cookie", redirectToCookie);
|
|
532
|
-
headers.append("Set-Cookie", codeVerifierCookie);
|
|
533
|
-
return Response.json(
|
|
534
|
-
{ oauth: oauth2 },
|
|
535
|
-
{
|
|
536
|
-
status: 302,
|
|
537
|
-
headers
|
|
538
|
-
}
|
|
539
|
-
);
|
|
540
|
-
} catch (error) {
|
|
541
|
-
if (isAuthError(error)) {
|
|
542
|
-
const { type, message } = error;
|
|
543
|
-
return AuraResponse.json(
|
|
544
|
-
{ error: type, error_description: message },
|
|
545
|
-
{ status: import_router2.statusCode.BAD_REQUEST }
|
|
546
|
-
);
|
|
414
|
+
const state = generateSecure();
|
|
415
|
+
const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
|
|
416
|
+
const redirectToValue = createRedirectTo(request, redirectTo, trustedProxyHeaders);
|
|
417
|
+
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
418
|
+
const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
|
|
419
|
+
const headers = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", authorization).setCookie(cookies.state.name, state, cookies.state.attributes).setCookie(cookies.redirectURI.name, redirectURI, cookies.redirectURI.attributes).setCookie(cookies.redirectTo.name, redirectToValue, cookies.redirectTo.attributes).setCookie(cookies.codeVerifier.name, codeVerifier, cookies.codeVerifier.attributes).toHeaders();
|
|
420
|
+
return Response.json(
|
|
421
|
+
{ oauth: oauth2 },
|
|
422
|
+
{
|
|
423
|
+
status: 302,
|
|
424
|
+
headers
|
|
547
425
|
}
|
|
548
|
-
|
|
549
|
-
{
|
|
550
|
-
error: ERROR_RESPONSE.AUTHORIZATION.SERVER_ERROR,
|
|
551
|
-
error_description: "An unexpected error occurred"
|
|
552
|
-
},
|
|
553
|
-
{ status: import_router2.statusCode.INTERNAL_SERVER_ERROR }
|
|
554
|
-
);
|
|
555
|
-
}
|
|
426
|
+
);
|
|
556
427
|
},
|
|
557
428
|
signInConfig(oauth)
|
|
558
429
|
);
|
|
559
430
|
};
|
|
560
431
|
|
|
561
432
|
// src/actions/callback/callback.ts
|
|
562
|
-
var
|
|
433
|
+
var import_zod3 = require("zod");
|
|
563
434
|
var import_router3 = require("@aura-stack/router");
|
|
564
435
|
|
|
565
|
-
// src/
|
|
566
|
-
var
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
436
|
+
// src/request.ts
|
|
437
|
+
var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
|
|
438
|
+
const controller = new AbortController();
|
|
439
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
440
|
+
const response = await fetch(url, {
|
|
441
|
+
...options2,
|
|
442
|
+
signal: controller.signal
|
|
443
|
+
}).finally(() => clearTimeout(timeoutId));
|
|
444
|
+
return response;
|
|
571
445
|
};
|
|
572
446
|
|
|
573
447
|
// src/actions/callback/userinfo.ts
|
|
@@ -583,7 +457,7 @@ var getDefaultUserInfo = (profile) => {
|
|
|
583
457
|
var getUserInfo = async (oauthConfig, accessToken) => {
|
|
584
458
|
const userinfoEndpoint = oauthConfig.userInfo;
|
|
585
459
|
try {
|
|
586
|
-
const response = await
|
|
460
|
+
const response = await fetchAsync(userinfoEndpoint, {
|
|
587
461
|
method: "GET",
|
|
588
462
|
headers: {
|
|
589
463
|
Accept: "application/json",
|
|
@@ -593,11 +467,20 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
593
467
|
const json = await response.json();
|
|
594
468
|
const { success, data } = OAuthErrorResponse.safeParse(json);
|
|
595
469
|
if (success) {
|
|
596
|
-
throw new
|
|
470
|
+
throw new OAuthProtocolError(
|
|
471
|
+
data.error,
|
|
472
|
+
data?.error_description ?? "An error occurred while fetching user information."
|
|
473
|
+
);
|
|
597
474
|
}
|
|
598
475
|
return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
|
|
599
476
|
} catch (error) {
|
|
600
|
-
|
|
477
|
+
if (isOAuthProtocolError(error)) {
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
if (isNativeError(error)) {
|
|
481
|
+
throw new OAuthProtocolError("invalid_request", error.message, "", { cause: error });
|
|
482
|
+
}
|
|
483
|
+
throw new OAuthProtocolError("invalid_request", "Failed to fetch user information.", "", { cause: error });
|
|
601
484
|
}
|
|
602
485
|
};
|
|
603
486
|
|
|
@@ -605,11 +488,12 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
605
488
|
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
|
|
606
489
|
const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
|
|
607
490
|
if (!parsed.success) {
|
|
608
|
-
|
|
491
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
492
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
609
493
|
}
|
|
610
494
|
const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
|
|
611
495
|
try {
|
|
612
|
-
const response = await
|
|
496
|
+
const response = await fetchAsync(accessToken, {
|
|
613
497
|
method: "POST",
|
|
614
498
|
headers: {
|
|
615
499
|
Accept: "application/json",
|
|
@@ -629,13 +513,55 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
|
|
|
629
513
|
if (!token.success) {
|
|
630
514
|
const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
|
|
631
515
|
if (!success) {
|
|
632
|
-
throw new
|
|
516
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
|
|
633
517
|
}
|
|
634
|
-
throw new
|
|
518
|
+
throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
|
|
635
519
|
}
|
|
636
520
|
return token.data;
|
|
637
521
|
} catch (error) {
|
|
638
|
-
throw
|
|
522
|
+
throw error;
|
|
523
|
+
}
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// src/cookie.ts
|
|
527
|
+
var import_cookie = require("@aura-stack/router/cookie");
|
|
528
|
+
var defaultCookieOptions = {
|
|
529
|
+
httpOnly: true,
|
|
530
|
+
sameSite: "lax",
|
|
531
|
+
path: "/",
|
|
532
|
+
maxAge: 60 * 60 * 24 * 15
|
|
533
|
+
};
|
|
534
|
+
var oauthCookieOptions = {
|
|
535
|
+
httpOnly: true,
|
|
536
|
+
maxAge: 5 * 60,
|
|
537
|
+
sameSite: "lax",
|
|
538
|
+
expires: new Date(Date.now() + 5 * 60 * 1e3)
|
|
539
|
+
};
|
|
540
|
+
var setCookie = (cookieName, value, options2) => {
|
|
541
|
+
return (0, import_cookie.serialize)(cookieName, value, options2);
|
|
542
|
+
};
|
|
543
|
+
var expiredCookieAttributes = {
|
|
544
|
+
...defaultCookieOptions,
|
|
545
|
+
expires: /* @__PURE__ */ new Date(0),
|
|
546
|
+
maxAge: 0
|
|
547
|
+
};
|
|
548
|
+
var getCookie = (request, cookieName) => {
|
|
549
|
+
const cookies = request.headers.get("Cookie");
|
|
550
|
+
if (!cookies) {
|
|
551
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", "No cookies found. There is no active session");
|
|
552
|
+
}
|
|
553
|
+
const value = (0, import_cookie.parse)(cookies)[cookieName];
|
|
554
|
+
if (!value) {
|
|
555
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", `Cookie "${cookieName}" not found. There is no active session`);
|
|
556
|
+
}
|
|
557
|
+
return value;
|
|
558
|
+
};
|
|
559
|
+
var createSessionCookie = async (jose, session) => {
|
|
560
|
+
try {
|
|
561
|
+
const encoded = await jose.encodeJWT(session);
|
|
562
|
+
return encoded;
|
|
563
|
+
} catch (error) {
|
|
564
|
+
throw new AuthInternalError("INVALID_JWT_TOKEN", "Failed to create session cookie", { cause: error });
|
|
639
565
|
}
|
|
640
566
|
};
|
|
641
567
|
|
|
@@ -643,9 +569,15 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
|
|
|
643
569
|
var callbackConfig = (oauth) => {
|
|
644
570
|
return (0, import_router3.createEndpointConfig)("/callback/:oauth", {
|
|
645
571
|
schemas: {
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
572
|
+
params: import_zod3.z.object({
|
|
573
|
+
oauth: import_zod3.z.enum(
|
|
574
|
+
Object.keys(oauth),
|
|
575
|
+
"The OAuth provider is not supported or invalid."
|
|
576
|
+
)
|
|
577
|
+
}),
|
|
578
|
+
searchParams: import_zod3.z.object({
|
|
579
|
+
code: import_zod3.z.string("Missing code parameter in the OAuth authorization response."),
|
|
580
|
+
state: import_zod3.z.string("Missing state parameter in the OAuth authorization response.")
|
|
649
581
|
})
|
|
650
582
|
},
|
|
651
583
|
middlewares: [
|
|
@@ -653,7 +585,7 @@ var callbackConfig = (oauth) => {
|
|
|
653
585
|
const response = OAuthAuthorizationErrorResponse.safeParse(ctx.searchParams);
|
|
654
586
|
if (response.success) {
|
|
655
587
|
const { error, error_description } = response.data;
|
|
656
|
-
throw new
|
|
588
|
+
throw new OAuthProtocolError(error, error_description ?? "OAuth Authorization Error");
|
|
657
589
|
}
|
|
658
590
|
return ctx;
|
|
659
591
|
}
|
|
@@ -669,66 +601,32 @@ var callbackAction = (oauth) => {
|
|
|
669
601
|
request,
|
|
670
602
|
params: { oauth: oauth2 },
|
|
671
603
|
searchParams: { code, state },
|
|
672
|
-
context: { oauth: providers, cookies, jose
|
|
604
|
+
context: { oauth: providers, cookies, jose }
|
|
673
605
|
} = ctx;
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
685
|
-
const sanitized = sanitizeURL(cookieRedirectTo);
|
|
686
|
-
if (!isValidRelativePath(sanitized)) {
|
|
687
|
-
throw new AuthError(
|
|
688
|
-
ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST,
|
|
689
|
-
"Invalid redirect path. Potential open redirect attack detected."
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
const headers = new Headers(cacheControl);
|
|
693
|
-
headers.set("Location", sanitized);
|
|
694
|
-
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
695
|
-
const sessionCookie = await createSessionCookie(userInfo, cookieOptions, jose);
|
|
696
|
-
const csrfToken = await createCSRF(jose);
|
|
697
|
-
const csrfCookie = setCookie(
|
|
698
|
-
"csrfToken",
|
|
699
|
-
csrfToken,
|
|
700
|
-
secureCookieOptions(
|
|
701
|
-
request,
|
|
702
|
-
{
|
|
703
|
-
...cookies,
|
|
704
|
-
strategy: "host"
|
|
705
|
-
},
|
|
706
|
-
trustedProxyHeaders
|
|
707
|
-
)
|
|
606
|
+
const oauthConfig = providers[oauth2];
|
|
607
|
+
const cookieState = getCookie(request, cookies.state.name);
|
|
608
|
+
const cookieRedirectTo = getCookie(request, cookies.redirectTo.name);
|
|
609
|
+
const cookieRedirectURI = getCookie(request, cookies.redirectURI.name);
|
|
610
|
+
const codeVerifier = getCookie(request, cookies.codeVerifier.name);
|
|
611
|
+
if (!equals(cookieState, state)) {
|
|
612
|
+
throw new AuthSecurityError(
|
|
613
|
+
"MISMATCHING_STATE",
|
|
614
|
+
"The provided state passed in the OAuth response does not match the stored state."
|
|
708
615
|
);
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
} catch (error) {
|
|
717
|
-
if (isAuthError(error)) {
|
|
718
|
-
const { type, message } = error;
|
|
719
|
-
return AuraResponse.json(
|
|
720
|
-
{ error: type, error_description: message },
|
|
721
|
-
{ status: import_router3.statusCode.BAD_REQUEST }
|
|
722
|
-
);
|
|
723
|
-
}
|
|
724
|
-
return AuraResponse.json(
|
|
725
|
-
{
|
|
726
|
-
error: ERROR_RESPONSE.ACCESS_TOKEN.INVALID_CLIENT,
|
|
727
|
-
error_description: "An unexpected error occurred"
|
|
728
|
-
},
|
|
729
|
-
{ status: import_router3.statusCode.INTERNAL_SERVER_ERROR }
|
|
616
|
+
}
|
|
617
|
+
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
618
|
+
const sanitized = sanitizeURL(cookieRedirectTo);
|
|
619
|
+
if (!isValidRelativePath(sanitized)) {
|
|
620
|
+
throw new AuthSecurityError(
|
|
621
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
622
|
+
"Invalid redirect path. Potential open redirect attack detected."
|
|
730
623
|
);
|
|
731
624
|
}
|
|
625
|
+
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
626
|
+
const sessionCookie = await createSessionCookie(jose, userInfo);
|
|
627
|
+
const csrfToken = await createCSRF(jose);
|
|
628
|
+
const headers = new import_router3.HeadersBuilder(cacheControl).setHeader("Location", sanitized).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
|
+
return Response.json({ oauth: oauth2 }, { status: 302, headers });
|
|
732
630
|
},
|
|
733
631
|
callbackConfig(oauth)
|
|
734
632
|
);
|
|
@@ -739,31 +637,28 @@ var import_router4 = require("@aura-stack/router");
|
|
|
739
637
|
var sessionAction = (0, import_router4.createEndpoint)("GET", "/session", async (ctx) => {
|
|
740
638
|
const {
|
|
741
639
|
request,
|
|
742
|
-
context: {
|
|
640
|
+
context: { jose, cookies }
|
|
743
641
|
} = ctx;
|
|
744
|
-
const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
|
|
745
642
|
try {
|
|
746
|
-
const session = getCookie(request,
|
|
643
|
+
const session = getCookie(request, cookies.sessionToken.name);
|
|
747
644
|
const decoded = await jose.decodeJWT(session);
|
|
748
645
|
const { exp, iat, jti, nbf, ...user } = decoded;
|
|
749
646
|
const headers = new Headers(cacheControl);
|
|
750
647
|
return Response.json({ user, expires: toISOString(exp * 1e3) }, { headers });
|
|
751
|
-
} catch {
|
|
752
|
-
const headers = new
|
|
753
|
-
const sessionCookie = expireCookie("sessionToken", cookieOptions);
|
|
754
|
-
headers.set("Set-Cookie", sessionCookie);
|
|
648
|
+
} catch (error) {
|
|
649
|
+
const headers = new import_router4.HeadersBuilder(cacheControl).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
755
650
|
return Response.json({ authenticated: false, message: "Unauthorized" }, { status: 401, headers });
|
|
756
651
|
}
|
|
757
652
|
});
|
|
758
653
|
|
|
759
654
|
// src/actions/signOut/signOut.ts
|
|
760
|
-
var
|
|
655
|
+
var import_zod4 = require("zod");
|
|
761
656
|
var import_router5 = require("@aura-stack/router");
|
|
762
657
|
var config = (0, import_router5.createEndpointConfig)({
|
|
763
658
|
schemas: {
|
|
764
|
-
searchParams:
|
|
765
|
-
token_type_hint:
|
|
766
|
-
redirectTo:
|
|
659
|
+
searchParams: import_zod4.z.object({
|
|
660
|
+
token_type_hint: import_zod4.z.literal("session_token"),
|
|
661
|
+
redirectTo: import_zod4.z.string().optional()
|
|
767
662
|
})
|
|
768
663
|
}
|
|
769
664
|
});
|
|
@@ -775,83 +670,53 @@ var signOutAction = (0, import_router5.createEndpoint)(
|
|
|
775
670
|
request,
|
|
776
671
|
headers,
|
|
777
672
|
searchParams: { redirectTo },
|
|
778
|
-
context: {
|
|
673
|
+
context: { jose, cookies }
|
|
779
674
|
} = ctx;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
await verifyCSRF(jose, csrfToken, header);
|
|
792
|
-
await jose.decodeJWT(session);
|
|
793
|
-
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
794
|
-
const location = createRedirectTo(
|
|
795
|
-
new Request(normalizedOriginPath, {
|
|
796
|
-
headers
|
|
797
|
-
}),
|
|
798
|
-
redirectTo
|
|
799
|
-
);
|
|
800
|
-
const responseHeaders = new Headers(cacheControl);
|
|
801
|
-
responseHeaders.append("Set-Cookie", expireCookie("sessionToken", cookiesOptions));
|
|
802
|
-
responseHeaders.append(
|
|
803
|
-
"Set-Cookie",
|
|
804
|
-
expireCookie("csrfToken", { ...cookiesOptions, prefix: cookiesOptions.secure ? "__Host-" : "" })
|
|
805
|
-
);
|
|
806
|
-
responseHeaders.append("Location", location);
|
|
807
|
-
return Response.json(
|
|
808
|
-
{ message: "Signed out successfully" },
|
|
809
|
-
{ status: import_router5.statusCode.ACCEPTED, headers: responseHeaders }
|
|
810
|
-
);
|
|
811
|
-
} catch (error) {
|
|
812
|
-
if (error instanceof InvalidCsrfTokenError) {
|
|
813
|
-
return AuraResponse.json(
|
|
814
|
-
{
|
|
815
|
-
error: "invalid_csrf_token",
|
|
816
|
-
error_description: "The provided CSRF token is invalid or has expired"
|
|
817
|
-
},
|
|
818
|
-
{ status: import_router5.statusCode.UNAUTHORIZED }
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
if (error instanceof InvalidRedirectToError) {
|
|
822
|
-
const { type, message } = error;
|
|
823
|
-
return AuraResponse.json(
|
|
824
|
-
{
|
|
825
|
-
error: type,
|
|
826
|
-
error_description: message
|
|
827
|
-
},
|
|
828
|
-
{ status: import_router5.statusCode.BAD_REQUEST }
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
return AuraResponse.json(
|
|
832
|
-
{
|
|
833
|
-
error: "invalid_session_token",
|
|
834
|
-
error_description: "The provided sessionToken is invalid or has already expired"
|
|
835
|
-
},
|
|
836
|
-
{ status: import_router5.statusCode.UNAUTHORIZED }
|
|
837
|
-
);
|
|
675
|
+
const session = headers.getCookie(cookies.sessionToken.name);
|
|
676
|
+
const csrfToken = headers.getCookie(cookies.csrfToken.name);
|
|
677
|
+
const header = headers.getHeader("X-CSRF-Token");
|
|
678
|
+
if (!session) {
|
|
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.");
|
|
838
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()
|
|
693
|
+
}),
|
|
694
|
+
redirectTo
|
|
695
|
+
);
|
|
696
|
+
const headersList = new import_router5.HeadersBuilder(cacheControl).setHeader("Location", location).setCookie(cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
697
|
+
return Response.json({ message: "Signed out successfully" }, { status: import_router5.statusCode.ACCEPTED, headers: headersList });
|
|
839
698
|
},
|
|
840
699
|
config
|
|
841
700
|
);
|
|
842
701
|
|
|
843
702
|
// src/actions/csrfToken/csrfToken.ts
|
|
844
703
|
var import_router6 = require("@aura-stack/router");
|
|
704
|
+
var getCSRFToken = (request, cookieName) => {
|
|
705
|
+
try {
|
|
706
|
+
return getCookie(request, cookieName);
|
|
707
|
+
} catch {
|
|
708
|
+
return void 0;
|
|
709
|
+
}
|
|
710
|
+
};
|
|
845
711
|
var csrfTokenAction = (0, import_router6.createEndpoint)("GET", "/csrfToken", async (ctx) => {
|
|
846
712
|
const {
|
|
847
713
|
request,
|
|
848
|
-
context: {
|
|
714
|
+
context: { jose, cookies }
|
|
849
715
|
} = ctx;
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
const csrfToken = await createCSRF(jose, existingCSRFToken);
|
|
716
|
+
const token = getCSRFToken(request, cookies.csrfToken.name);
|
|
717
|
+
const csrfToken = await createCSRF(jose, token);
|
|
853
718
|
const headers = new Headers(cacheControl);
|
|
854
|
-
headers.
|
|
719
|
+
headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
|
|
855
720
|
return Response.json({ csrfToken }, { headers });
|
|
856
721
|
});
|
|
857
722
|
// Annotate the CommonJS export names for ESM import in node:
|