@aura-stack/auth 0.1.0 → 0.2.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 +3 -4
- package/dist/@types/router.d.d.ts +3 -4
- package/dist/@types/utility.d.ts +1 -5
- package/dist/actions/callback/access-token.cjs +51 -41
- package/dist/actions/callback/access-token.d.ts +3 -4
- package/dist/actions/callback/access-token.js +4 -3
- package/dist/actions/callback/callback.cjs +115 -210
- package/dist/actions/callback/callback.d.ts +3 -4
- package/dist/actions/callback/callback.js +9 -10
- package/dist/actions/callback/userinfo.cjs +35 -22
- package/dist/actions/callback/userinfo.d.ts +3 -4
- package/dist/actions/callback/userinfo.js +6 -5
- package/dist/actions/csrfToken/csrfToken.cjs +34 -103
- package/dist/actions/csrfToken/csrfToken.js +6 -6
- package/dist/actions/index.cjs +234 -391
- package/dist/actions/index.d.ts +3 -4
- package/dist/actions/index.js +16 -17
- package/dist/actions/session/session.cjs +25 -109
- package/dist/actions/session/session.js +4 -5
- package/dist/actions/signIn/authorization.cjs +64 -55
- package/dist/actions/signIn/authorization.d.ts +3 -4
- package/dist/actions/signIn/authorization.js +5 -5
- package/dist/actions/signIn/signIn.cjs +84 -206
- package/dist/actions/signIn/signIn.d.ts +3 -4
- package/dist/actions/signIn/signIn.js +7 -9
- package/dist/actions/signOut/signOut.cjs +88 -234
- package/dist/actions/signOut/signOut.js +8 -9
- package/dist/assert.cjs +5 -0
- package/dist/assert.d.ts +9 -1
- package/dist/assert.js +3 -1
- package/dist/chunk-2RXNXMCZ.js +55 -0
- package/dist/{chunk-UJJ7R56J.js → chunk-4V4JNXVF.js} +13 -10
- package/dist/chunk-6R2YZ4AC.js +22 -0
- package/dist/{chunk-VFTYH33W.js → chunk-7H3OR6UU.js} +29 -9
- package/dist/{chunk-256KIVJL.js → chunk-CXLATHS5.js} +53 -9
- package/dist/{chunk-6SM22VVJ.js → chunk-EIL2FPSS.js} +5 -1
- package/dist/chunk-IMICRJ5U.js +197 -0
- package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
- package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
- package/dist/chunk-NEVKX6K2.js +70 -0
- package/dist/{chunk-XXJKNKGQ.js → chunk-PTJUYB33.js} +9 -13
- package/dist/chunk-QDO2KSRJ.js +35 -0
- package/dist/{chunk-CAKJT3KS.js → chunk-QEZL7EYN.js} +21 -17
- package/dist/chunk-RRLIF4PQ.js +55 -0
- package/dist/chunk-TLE4PXY3.js +39 -0
- package/dist/chunk-UEH3LVON.js +97 -0
- package/dist/{chunk-HMRKN75I.js → chunk-WD7AUHQ5.js} +12 -7
- package/dist/{chunk-RLT4RFKV.js → chunk-ZLR3LI6X.js} +19 -9
- package/dist/cookie.cjs +140 -99
- package/dist/cookie.d.ts +33 -43
- package/dist/cookie.js +10 -17
- package/dist/errors.cjs +85 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.js +18 -0
- package/dist/{index-DpfbvTZ_.d.ts → index-EqsoyjrF.d.ts} +139 -57
- package/dist/index.cjs +427 -389
- package/dist/index.d.ts +4 -5
- package/dist/index.js +37 -26
- package/dist/jose.cjs +23 -12
- package/dist/jose.d.ts +4 -1
- package/dist/jose.js +5 -4
- package/dist/oauth/bitbucket.d.ts +3 -4
- package/dist/oauth/discord.cjs +0 -1
- package/dist/oauth/discord.d.ts +3 -4
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/figma.d.ts +3 -4
- package/dist/oauth/github.d.ts +3 -4
- package/dist/oauth/gitlab.d.ts +3 -4
- package/dist/oauth/index.cjs +132 -6
- package/dist/oauth/index.d.ts +3 -4
- package/dist/oauth/index.js +12 -5
- package/dist/oauth/spotify.d.ts +3 -4
- package/dist/{response.cjs → oauth/strava.cjs} +21 -9
- package/dist/oauth/strava.d.ts +6 -0
- package/dist/oauth/strava.js +6 -0
- package/dist/oauth/x.d.ts +3 -4
- package/dist/schemas.cjs +11 -5
- package/dist/schemas.d.ts +70 -67
- package/dist/schemas.js +3 -1
- package/dist/secure.cjs +27 -19
- package/dist/secure.d.ts +3 -4
- 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 +5 -6
- 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
|
@@ -42,68 +42,53 @@ module.exports = __toCommonJS(actions_exports);
|
|
|
42
42
|
var import_zod = __toESM(require("zod"), 1);
|
|
43
43
|
var import_router2 = require("@aura-stack/router");
|
|
44
44
|
|
|
45
|
-
// src/response.ts
|
|
46
|
-
var AuraResponse = class extends Response {
|
|
47
|
-
static json(body, init) {
|
|
48
|
-
return Response.json(body, init);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
|
|
52
45
|
// src/secure.ts
|
|
53
46
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
54
47
|
|
|
55
48
|
// src/utils.ts
|
|
56
49
|
var import_router = require("@aura-stack/router");
|
|
57
50
|
|
|
58
|
-
// src/
|
|
59
|
-
var
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
// src/errors.ts
|
|
52
|
+
var OAuthProtocolError = class extends Error {
|
|
53
|
+
type = "OAUTH_PROTOCOL_ERROR";
|
|
54
|
+
error;
|
|
55
|
+
errorURI;
|
|
56
|
+
constructor(error, description, errorURI, options2) {
|
|
57
|
+
super(description, options2);
|
|
58
|
+
this.error = error;
|
|
59
|
+
this.errorURI = errorURI;
|
|
60
|
+
this.name = new.target.name;
|
|
61
|
+
Error.captureStackTrace(this, new.target);
|
|
64
62
|
}
|
|
65
63
|
};
|
|
66
|
-
var
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
var AuthInternalError = class extends Error {
|
|
65
|
+
type = "AUTH_INTERNAL_ERROR";
|
|
66
|
+
code;
|
|
67
|
+
constructor(code, message, options2) {
|
|
68
|
+
super(message, options2);
|
|
69
|
+
this.code = code;
|
|
70
|
+
this.name = new.target.name;
|
|
71
|
+
Error.captureStackTrace(this, new.target);
|
|
70
72
|
}
|
|
71
73
|
};
|
|
72
|
-
var
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
var AuthSecurityError = class extends Error {
|
|
75
|
+
type = "AUTH_SECURITY_ERROR";
|
|
76
|
+
code;
|
|
77
|
+
constructor(code, message, options2) {
|
|
78
|
+
super(message, options2);
|
|
79
|
+
this.code = code;
|
|
80
|
+
this.name = new.target.name;
|
|
81
|
+
Error.captureStackTrace(this, new.target);
|
|
76
82
|
}
|
|
77
83
|
};
|
|
78
|
-
var
|
|
79
|
-
return error instanceof
|
|
84
|
+
var isNativeError = (error) => {
|
|
85
|
+
return error instanceof Error;
|
|
80
86
|
};
|
|
81
|
-
var
|
|
82
|
-
|
|
83
|
-
if (isAuthError(error)) {
|
|
84
|
-
throw error;
|
|
85
|
-
}
|
|
86
|
-
throw new AuthError("invalid_request", error.message ?? message);
|
|
87
|
-
}
|
|
87
|
+
var isOAuthProtocolError = (error) => {
|
|
88
|
+
return error instanceof OAuthProtocolError;
|
|
88
89
|
};
|
|
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
|
-
}
|
|
90
|
+
var isAuthSecurityError = (error) => {
|
|
91
|
+
return error instanceof AuthSecurityError;
|
|
107
92
|
};
|
|
108
93
|
|
|
109
94
|
// src/utils.ts
|
|
@@ -123,9 +108,9 @@ var equals = (a, b) => {
|
|
|
123
108
|
if (a === null || b === null || a === void 0 || b === void 0) return false;
|
|
124
109
|
return a === b;
|
|
125
110
|
};
|
|
126
|
-
var sanitizeURL = (
|
|
111
|
+
var sanitizeURL = (url) => {
|
|
127
112
|
try {
|
|
128
|
-
let decodedURL = decodeURIComponent(
|
|
113
|
+
let decodedURL = decodeURIComponent(url).trim();
|
|
129
114
|
const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
|
|
130
115
|
let protocol = "";
|
|
131
116
|
let rest = decodedURL;
|
|
@@ -153,7 +138,7 @@ var sanitizeURL = (url2) => {
|
|
|
153
138
|
}
|
|
154
139
|
return sanitized;
|
|
155
140
|
} catch {
|
|
156
|
-
return
|
|
141
|
+
return url.trim();
|
|
157
142
|
}
|
|
158
143
|
};
|
|
159
144
|
var isValidRelativePath = (path) => {
|
|
@@ -166,10 +151,10 @@ var isValidRelativePath = (path) => {
|
|
|
166
151
|
};
|
|
167
152
|
var getNormalizedOriginPath = (path) => {
|
|
168
153
|
try {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return `${
|
|
154
|
+
const url = new URL(path);
|
|
155
|
+
url.hash = "";
|
|
156
|
+
url.search = "";
|
|
157
|
+
return `${url.origin}${url.pathname}`;
|
|
173
158
|
} catch {
|
|
174
159
|
return sanitizeURL(path);
|
|
175
160
|
}
|
|
@@ -177,6 +162,31 @@ var getNormalizedOriginPath = (path) => {
|
|
|
177
162
|
var toISOString = (date) => {
|
|
178
163
|
return new Date(date).toISOString();
|
|
179
164
|
};
|
|
165
|
+
var formatZodError = (error) => {
|
|
166
|
+
if (!error.issues || error.issues.length === 0) {
|
|
167
|
+
return {};
|
|
168
|
+
}
|
|
169
|
+
return error.issues.reduce((previous, issue) => {
|
|
170
|
+
const key = issue.path.join(".");
|
|
171
|
+
return {
|
|
172
|
+
...previous,
|
|
173
|
+
[key]: {
|
|
174
|
+
code: issue.code,
|
|
175
|
+
message: issue.message
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
}, {});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// src/assert.ts
|
|
182
|
+
var isValidURL = (value) => {
|
|
183
|
+
if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
|
|
184
|
+
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
185
|
+
return regex.test(value);
|
|
186
|
+
};
|
|
187
|
+
var isJWTPayloadWithToken = (payload) => {
|
|
188
|
+
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
189
|
+
};
|
|
180
190
|
|
|
181
191
|
// src/secure.ts
|
|
182
192
|
var generateSecure = (length = 32) => {
|
|
@@ -205,166 +215,35 @@ var createCSRF = async (jose, csrfCookie) => {
|
|
|
205
215
|
};
|
|
206
216
|
var verifyCSRF = async (jose, cookie, header) => {
|
|
207
217
|
try {
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
218
|
+
const cookiePayload = await jose.verifyJWS(cookie);
|
|
219
|
+
const headerPayload = await jose.verifyJWS(header);
|
|
220
|
+
if (!isJWTPayloadWithToken(cookiePayload)) {
|
|
221
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
222
|
+
}
|
|
223
|
+
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
224
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
225
|
+
}
|
|
226
|
+
const cookieBuffer = Buffer.from(cookiePayload.token);
|
|
227
|
+
const headerBuffer = Buffer.from(headerPayload.token);
|
|
212
228
|
if (!equals(headerBuffer.length, cookieBuffer.length)) {
|
|
213
|
-
throw new
|
|
229
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
214
230
|
}
|
|
215
231
|
if (!import_node_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
|
|
216
|
-
throw new
|
|
232
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
217
233
|
}
|
|
218
234
|
return true;
|
|
219
235
|
} 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
|
-
};
|
|
236
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
340
237
|
}
|
|
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
238
|
};
|
|
360
239
|
|
|
361
240
|
// src/schemas.ts
|
|
362
241
|
var import_v4 = require("zod/v4");
|
|
363
242
|
var OAuthProviderConfigSchema = (0, import_v4.object)({
|
|
364
|
-
authorizeURL: (0, import_v4.
|
|
365
|
-
accessToken: (0, import_v4.
|
|
243
|
+
authorizeURL: (0, import_v4.httpUrl)(),
|
|
244
|
+
accessToken: (0, import_v4.httpUrl)(),
|
|
366
245
|
scope: (0, import_v4.string)().optional(),
|
|
367
|
-
userInfo: (0, import_v4.
|
|
246
|
+
userInfo: (0, import_v4.httpUrl)(),
|
|
368
247
|
responseType: (0, import_v4.enum)(["code", "token", "id_token"]),
|
|
369
248
|
clientId: (0, import_v4.string)(),
|
|
370
249
|
clientSecret: (0, import_v4.string)()
|
|
@@ -376,8 +255,8 @@ var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
|
376
255
|
codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
|
|
377
256
|
});
|
|
378
257
|
var OAuthAuthorizationResponse = (0, import_v4.object)({
|
|
379
|
-
state: (0, import_v4.string)(),
|
|
380
|
-
code: (0, import_v4.string)()
|
|
258
|
+
state: (0, import_v4.string)("Missing state parameter in the OAuth authorization response."),
|
|
259
|
+
code: (0, import_v4.string)("Missing code parameter in the OAuth authorization response.")
|
|
381
260
|
});
|
|
382
261
|
var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
383
262
|
error: (0, import_v4.enum)([
|
|
@@ -421,12 +300,17 @@ var OAuthErrorResponse = (0, import_v4.object)({
|
|
|
421
300
|
error: (0, import_v4.string)(),
|
|
422
301
|
error_description: (0, import_v4.string)().optional()
|
|
423
302
|
});
|
|
303
|
+
var OAuthEnvSchema = (0, import_v4.object)({
|
|
304
|
+
clientId: import_v4.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
305
|
+
clientSecret: import_v4.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
306
|
+
});
|
|
424
307
|
|
|
425
308
|
// src/actions/signIn/authorization.ts
|
|
426
309
|
var createAuthorizationURL = (oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod) => {
|
|
427
310
|
const parsed = OAuthAuthorization.safeParse({ ...oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod });
|
|
428
311
|
if (!parsed.success) {
|
|
429
|
-
|
|
312
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
313
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
430
314
|
}
|
|
431
315
|
const { authorizeURL, ...options2 } = parsed.data;
|
|
432
316
|
const { userInfo, accessToken, clientSecret, ...required } = options2;
|
|
@@ -444,8 +328,8 @@ var getOriginURL = (request, trustedProxyHeaders) => {
|
|
|
444
328
|
}
|
|
445
329
|
};
|
|
446
330
|
var createRedirectURI = (request, oauth, basePath, trustedProxyHeaders) => {
|
|
447
|
-
const
|
|
448
|
-
return `${
|
|
331
|
+
const url = getOriginURL(request, trustedProxyHeaders);
|
|
332
|
+
return `${url.origin}${basePath}/callback/${oauth}`;
|
|
449
333
|
};
|
|
450
334
|
var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
451
335
|
try {
|
|
@@ -459,15 +343,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
459
343
|
}
|
|
460
344
|
const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
|
|
461
345
|
if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
|
|
462
|
-
throw new
|
|
346
|
+
throw new AuthSecurityError(
|
|
347
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
348
|
+
"The redirectTo parameter does not match the hosted origin."
|
|
349
|
+
);
|
|
463
350
|
}
|
|
464
351
|
return sanitizeURL(redirectToURL.pathname);
|
|
465
352
|
}
|
|
466
353
|
if (referer) {
|
|
467
354
|
const refererURL = new URL(sanitizeURL(referer));
|
|
468
355
|
if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
|
|
469
|
-
throw new
|
|
470
|
-
|
|
356
|
+
throw new AuthSecurityError(
|
|
357
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
471
358
|
"The referer of the request does not match the hosted origin."
|
|
472
359
|
);
|
|
473
360
|
}
|
|
@@ -476,16 +363,16 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
476
363
|
if (origin) {
|
|
477
364
|
const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
|
|
478
365
|
if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
|
|
479
|
-
throw new
|
|
366
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
480
367
|
}
|
|
481
368
|
return sanitizeURL(originURL.pathname);
|
|
482
369
|
}
|
|
483
370
|
return "/";
|
|
484
371
|
} catch (error) {
|
|
485
|
-
if (
|
|
372
|
+
if (isAuthSecurityError(error)) {
|
|
486
373
|
throw error;
|
|
487
374
|
}
|
|
488
|
-
throw new
|
|
375
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
489
376
|
}
|
|
490
377
|
};
|
|
491
378
|
|
|
@@ -494,7 +381,7 @@ var signInConfig = (oauth) => {
|
|
|
494
381
|
return (0, import_router2.createEndpointConfig)("/signIn/:oauth", {
|
|
495
382
|
schemas: {
|
|
496
383
|
params: import_zod.default.object({
|
|
497
|
-
oauth: import_zod.default.enum(Object.keys(oauth)),
|
|
384
|
+
oauth: import_zod.default.enum(Object.keys(oauth), "The OAuth provider is not supported or invalid."),
|
|
498
385
|
redirectTo: import_zod.default.string().optional()
|
|
499
386
|
})
|
|
500
387
|
}
|
|
@@ -507,52 +394,23 @@ var signInAction = (oauth) => {
|
|
|
507
394
|
async (ctx) => {
|
|
508
395
|
const {
|
|
509
396
|
request,
|
|
397
|
+
headers: headersBuilder,
|
|
510
398
|
params: { oauth: oauth2, redirectTo },
|
|
511
399
|
context: { oauth: providers, cookies, trustedProxyHeaders, basePath }
|
|
512
400
|
} = 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
|
-
);
|
|
401
|
+
const state = generateSecure();
|
|
402
|
+
const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
|
|
403
|
+
const redirectToValue = createRedirectTo(request, redirectTo, trustedProxyHeaders);
|
|
404
|
+
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
405
|
+
const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
|
|
406
|
+
const headers = headersBuilder.setHeader("Location", authorization).setCookie(cookies.state.name, state, cookies.state.attributes).setCookie(cookies.redirect_uri.name, redirectURI, cookies.redirect_uri.attributes).setCookie(cookies.redirect_to.name, redirectToValue, cookies.redirect_to.attributes).setCookie(cookies.code_verifier.name, codeVerifier, cookies.code_verifier.attributes).toHeaders();
|
|
407
|
+
return Response.json(
|
|
408
|
+
{ oauth: oauth2 },
|
|
409
|
+
{
|
|
410
|
+
status: 302,
|
|
411
|
+
headers
|
|
547
412
|
}
|
|
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
|
-
}
|
|
413
|
+
);
|
|
556
414
|
},
|
|
557
415
|
signInConfig(oauth)
|
|
558
416
|
);
|
|
@@ -593,11 +451,20 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
593
451
|
const json = await response.json();
|
|
594
452
|
const { success, data } = OAuthErrorResponse.safeParse(json);
|
|
595
453
|
if (success) {
|
|
596
|
-
throw new
|
|
454
|
+
throw new OAuthProtocolError(
|
|
455
|
+
data.error,
|
|
456
|
+
data?.error_description ?? "An error occurred while fetching user information."
|
|
457
|
+
);
|
|
597
458
|
}
|
|
598
459
|
return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
|
|
599
460
|
} catch (error) {
|
|
600
|
-
|
|
461
|
+
if (isOAuthProtocolError(error)) {
|
|
462
|
+
throw error;
|
|
463
|
+
}
|
|
464
|
+
if (isNativeError(error)) {
|
|
465
|
+
throw new OAuthProtocolError("invalid_request", error.message, "", { cause: error });
|
|
466
|
+
}
|
|
467
|
+
throw new OAuthProtocolError("invalid_request", "Failed to fetch user information.", "", { cause: error });
|
|
601
468
|
}
|
|
602
469
|
};
|
|
603
470
|
|
|
@@ -605,7 +472,8 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
605
472
|
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
|
|
606
473
|
const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
|
|
607
474
|
if (!parsed.success) {
|
|
608
|
-
|
|
475
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
476
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
609
477
|
}
|
|
610
478
|
const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
|
|
611
479
|
try {
|
|
@@ -629,13 +497,55 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
|
|
|
629
497
|
if (!token.success) {
|
|
630
498
|
const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
|
|
631
499
|
if (!success) {
|
|
632
|
-
throw new
|
|
500
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
|
|
633
501
|
}
|
|
634
|
-
throw new
|
|
502
|
+
throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
|
|
635
503
|
}
|
|
636
504
|
return token.data;
|
|
637
505
|
} catch (error) {
|
|
638
|
-
throw
|
|
506
|
+
throw error;
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// src/cookie.ts
|
|
511
|
+
var import_cookie = require("@aura-stack/router/cookie");
|
|
512
|
+
var defaultCookieOptions = {
|
|
513
|
+
httpOnly: true,
|
|
514
|
+
sameSite: "lax",
|
|
515
|
+
path: "/",
|
|
516
|
+
maxAge: 60 * 60 * 24 * 15
|
|
517
|
+
};
|
|
518
|
+
var oauthCookieOptions = {
|
|
519
|
+
httpOnly: true,
|
|
520
|
+
maxAge: 5 * 60,
|
|
521
|
+
sameSite: "lax",
|
|
522
|
+
expires: new Date(Date.now() + 5 * 60 * 1e3)
|
|
523
|
+
};
|
|
524
|
+
var setCookie = (cookieName, value, options2) => {
|
|
525
|
+
return (0, import_cookie.serialize)(cookieName, value, options2);
|
|
526
|
+
};
|
|
527
|
+
var expiredCookieAttributes = {
|
|
528
|
+
...defaultCookieOptions,
|
|
529
|
+
expires: /* @__PURE__ */ new Date(0),
|
|
530
|
+
maxAge: 0
|
|
531
|
+
};
|
|
532
|
+
var getCookie = (request, cookieName) => {
|
|
533
|
+
const cookies = request.headers.get("Cookie");
|
|
534
|
+
if (!cookies) {
|
|
535
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", "No cookies found. There is no active session");
|
|
536
|
+
}
|
|
537
|
+
const value = (0, import_cookie.parse)(cookies)[cookieName];
|
|
538
|
+
if (!value) {
|
|
539
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", `Cookie "${cookieName}" not found. There is no active session`);
|
|
540
|
+
}
|
|
541
|
+
return value;
|
|
542
|
+
};
|
|
543
|
+
var createSessionCookie = async (jose, session) => {
|
|
544
|
+
try {
|
|
545
|
+
const encoded = await jose.encodeJWT(session);
|
|
546
|
+
return encoded;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
throw new AuthInternalError("INVALID_JWT_TOKEN", "Failed to create session cookie", { cause: error });
|
|
639
549
|
}
|
|
640
550
|
};
|
|
641
551
|
|
|
@@ -645,7 +555,7 @@ var callbackConfig = (oauth) => {
|
|
|
645
555
|
schemas: {
|
|
646
556
|
searchParams: OAuthAuthorizationResponse,
|
|
647
557
|
params: import_zod2.default.object({
|
|
648
|
-
oauth: import_zod2.default.enum(Object.keys(oauth))
|
|
558
|
+
oauth: import_zod2.default.enum(Object.keys(oauth), "The OAuth provider is not supported or invalid.")
|
|
649
559
|
})
|
|
650
560
|
},
|
|
651
561
|
middlewares: [
|
|
@@ -653,7 +563,7 @@ var callbackConfig = (oauth) => {
|
|
|
653
563
|
const response = OAuthAuthorizationErrorResponse.safeParse(ctx.searchParams);
|
|
654
564
|
if (response.success) {
|
|
655
565
|
const { error, error_description } = response.data;
|
|
656
|
-
throw new
|
|
566
|
+
throw new OAuthProtocolError(error, error_description ?? "OAuth Authorization Error");
|
|
657
567
|
}
|
|
658
568
|
return ctx;
|
|
659
569
|
}
|
|
@@ -669,66 +579,32 @@ var callbackAction = (oauth) => {
|
|
|
669
579
|
request,
|
|
670
580
|
params: { oauth: oauth2 },
|
|
671
581
|
searchParams: { code, state },
|
|
672
|
-
context: { oauth: providers, cookies, jose
|
|
582
|
+
context: { oauth: providers, cookies, jose }
|
|
673
583
|
} = 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
|
-
)
|
|
584
|
+
const oauthConfig = providers[oauth2];
|
|
585
|
+
const cookieState = getCookie(request, cookies.state.name);
|
|
586
|
+
const cookieRedirectTo = getCookie(request, cookies.redirect_to.name);
|
|
587
|
+
const cookieRedirectURI = getCookie(request, cookies.redirect_uri.name);
|
|
588
|
+
const codeVerifier = getCookie(request, cookies.code_verifier.name);
|
|
589
|
+
if (!equals(cookieState, state)) {
|
|
590
|
+
throw new AuthSecurityError(
|
|
591
|
+
"MISMATCHING_STATE",
|
|
592
|
+
"The provided state passed in the OAuth response does not match the stored state."
|
|
708
593
|
);
|
|
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 }
|
|
594
|
+
}
|
|
595
|
+
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
596
|
+
const sanitized = sanitizeURL(cookieRedirectTo);
|
|
597
|
+
if (!isValidRelativePath(sanitized)) {
|
|
598
|
+
throw new AuthSecurityError(
|
|
599
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
600
|
+
"Invalid redirect path. Potential open redirect attack detected."
|
|
730
601
|
);
|
|
731
602
|
}
|
|
603
|
+
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
604
|
+
const sessionCookie = await createSessionCookie(jose, userInfo);
|
|
605
|
+
const csrfToken = await createCSRF(jose);
|
|
606
|
+
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.redirect_uri.name, "", expiredCookieAttributes).setCookie(cookies.redirect_to.name, "", expiredCookieAttributes).setCookie(cookies.code_verifier.name, "", expiredCookieAttributes).toHeaders();
|
|
607
|
+
return Response.json({ oauth: oauth2 }, { status: 302, headers });
|
|
732
608
|
},
|
|
733
609
|
callbackConfig(oauth)
|
|
734
610
|
);
|
|
@@ -739,19 +615,16 @@ var import_router4 = require("@aura-stack/router");
|
|
|
739
615
|
var sessionAction = (0, import_router4.createEndpoint)("GET", "/session", async (ctx) => {
|
|
740
616
|
const {
|
|
741
617
|
request,
|
|
742
|
-
context: {
|
|
618
|
+
context: { jose, cookies }
|
|
743
619
|
} = ctx;
|
|
744
|
-
const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
|
|
745
620
|
try {
|
|
746
|
-
const session = getCookie(request,
|
|
621
|
+
const session = getCookie(request, cookies.sessionToken.name);
|
|
747
622
|
const decoded = await jose.decodeJWT(session);
|
|
748
623
|
const { exp, iat, jti, nbf, ...user } = decoded;
|
|
749
624
|
const headers = new Headers(cacheControl);
|
|
750
625
|
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);
|
|
626
|
+
} catch (error) {
|
|
627
|
+
const headers = new import_router4.HeadersBuilder(cacheControl).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
755
628
|
return Response.json({ authenticated: false, message: "Unauthorized" }, { status: 401, headers });
|
|
756
629
|
}
|
|
757
630
|
});
|
|
@@ -775,83 +648,53 @@ var signOutAction = (0, import_router5.createEndpoint)(
|
|
|
775
648
|
request,
|
|
776
649
|
headers,
|
|
777
650
|
searchParams: { redirectTo },
|
|
778
|
-
context: {
|
|
651
|
+
context: { jose, cookies }
|
|
779
652
|
} = ctx;
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
if (!header || !session || !csrfToken) {
|
|
789
|
-
throw new Error("Missing CSRF token or session token");
|
|
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
|
-
);
|
|
653
|
+
const session = headers.getCookie(cookies.sessionToken.name);
|
|
654
|
+
const csrfToken = headers.getCookie(cookies.csrfToken.name);
|
|
655
|
+
const header = headers.getHeader("X-CSRF-Token");
|
|
656
|
+
if (!session) {
|
|
657
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
658
|
+
}
|
|
659
|
+
if (!csrfToken) {
|
|
660
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
838
661
|
}
|
|
662
|
+
if (!header) {
|
|
663
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
|
|
664
|
+
}
|
|
665
|
+
await verifyCSRF(jose, csrfToken, header);
|
|
666
|
+
await jose.decodeJWT(session);
|
|
667
|
+
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
668
|
+
const location = createRedirectTo(
|
|
669
|
+
new Request(normalizedOriginPath, {
|
|
670
|
+
headers: headers.toHeaders()
|
|
671
|
+
}),
|
|
672
|
+
redirectTo
|
|
673
|
+
);
|
|
674
|
+
const headersList = new import_router5.HeadersBuilder(cacheControl).setHeader("Location", location).setCookie(cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
675
|
+
return Response.json({ message: "Signed out successfully" }, { status: import_router5.statusCode.ACCEPTED, headers: headersList });
|
|
839
676
|
},
|
|
840
677
|
config
|
|
841
678
|
);
|
|
842
679
|
|
|
843
680
|
// src/actions/csrfToken/csrfToken.ts
|
|
844
681
|
var import_router6 = require("@aura-stack/router");
|
|
682
|
+
var getCSRFToken = (request, cookieName) => {
|
|
683
|
+
try {
|
|
684
|
+
return getCookie(request, cookieName);
|
|
685
|
+
} catch {
|
|
686
|
+
return void 0;
|
|
687
|
+
}
|
|
688
|
+
};
|
|
845
689
|
var csrfTokenAction = (0, import_router6.createEndpoint)("GET", "/csrfToken", async (ctx) => {
|
|
846
690
|
const {
|
|
847
691
|
request,
|
|
848
|
-
context: {
|
|
692
|
+
context: { jose, cookies }
|
|
849
693
|
} = ctx;
|
|
850
|
-
const
|
|
851
|
-
const
|
|
852
|
-
const csrfToken = await createCSRF(jose, existingCSRFToken);
|
|
694
|
+
const token = getCSRFToken(request, cookies.csrfToken.name);
|
|
695
|
+
const csrfToken = await createCSRF(jose, token);
|
|
853
696
|
const headers = new Headers(cacheControl);
|
|
854
|
-
headers.
|
|
697
|
+
headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
|
|
855
698
|
return Response.json({ csrfToken }, { headers });
|
|
856
699
|
});
|
|
857
700
|
// Annotate the CommonJS export names for ESM import in node:
|