@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/index.cjs
CHANGED
|
@@ -39,55 +39,50 @@ var import_router7 = require("@aura-stack/router");
|
|
|
39
39
|
// src/utils.ts
|
|
40
40
|
var import_router = require("@aura-stack/router");
|
|
41
41
|
|
|
42
|
-
// src/
|
|
43
|
-
var
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
42
|
+
// src/errors.ts
|
|
43
|
+
var OAuthProtocolError = class extends Error {
|
|
44
|
+
type = "OAUTH_PROTOCOL_ERROR";
|
|
45
|
+
error;
|
|
46
|
+
errorURI;
|
|
47
|
+
constructor(error, description, errorURI, options2) {
|
|
48
|
+
super(description, options2);
|
|
49
|
+
this.error = error;
|
|
50
|
+
this.errorURI = errorURI;
|
|
51
|
+
this.name = new.target.name;
|
|
52
|
+
Error.captureStackTrace(this, new.target);
|
|
48
53
|
}
|
|
49
54
|
};
|
|
50
|
-
var
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
var AuthInternalError = class extends Error {
|
|
56
|
+
type = "AUTH_INTERNAL_ERROR";
|
|
57
|
+
code;
|
|
58
|
+
constructor(code, message, options2) {
|
|
59
|
+
super(message, options2);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.name = new.target.name;
|
|
62
|
+
Error.captureStackTrace(this, new.target);
|
|
54
63
|
}
|
|
55
64
|
};
|
|
56
|
-
var
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
var AuthSecurityError = class extends Error {
|
|
66
|
+
type = "AUTH_SECURITY_ERROR";
|
|
67
|
+
code;
|
|
68
|
+
constructor(code, message, options2) {
|
|
69
|
+
super(message, options2);
|
|
70
|
+
this.code = code;
|
|
71
|
+
this.name = new.target.name;
|
|
72
|
+
Error.captureStackTrace(this, new.target);
|
|
60
73
|
}
|
|
61
74
|
};
|
|
62
|
-
var
|
|
63
|
-
return error instanceof
|
|
75
|
+
var isNativeError = (error) => {
|
|
76
|
+
return error instanceof Error;
|
|
64
77
|
};
|
|
65
|
-
var
|
|
66
|
-
|
|
67
|
-
if (isAuthError(error)) {
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
throw new AuthError("invalid_request", error.message ?? message);
|
|
71
|
-
}
|
|
78
|
+
var isOAuthProtocolError = (error) => {
|
|
79
|
+
return error instanceof OAuthProtocolError;
|
|
72
80
|
};
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
UNSUPPORTED_RESPONSE_TYPE: "unsupported_response_type",
|
|
79
|
-
INVALID_SCOPE: "invalid_scope",
|
|
80
|
-
SERVER_ERROR: "server_error",
|
|
81
|
-
TEMPORARILY_UNAVAILABLE: "temporarily_unavailable"
|
|
82
|
-
},
|
|
83
|
-
ACCESS_TOKEN: {
|
|
84
|
-
INVALID_REQUEST: "invalid_request",
|
|
85
|
-
INVALID_CLIENT: "invalid_client",
|
|
86
|
-
INVALID_GRANT: "invalid_grant",
|
|
87
|
-
UNAUTHORIZED_CLIENT: "unauthorized_client",
|
|
88
|
-
UNSUPPORTED_GRANT_TYPE: "unsupported_grant_type",
|
|
89
|
-
INVALID_SCOPE: "invalid_scope"
|
|
90
|
-
}
|
|
81
|
+
var isAuthInternalError = (error) => {
|
|
82
|
+
return error instanceof AuthInternalError;
|
|
83
|
+
};
|
|
84
|
+
var isAuthSecurityError = (error) => {
|
|
85
|
+
return error instanceof AuthSecurityError;
|
|
91
86
|
};
|
|
92
87
|
|
|
93
88
|
// src/utils.ts
|
|
@@ -107,9 +102,9 @@ var equals = (a, b) => {
|
|
|
107
102
|
if (a === null || b === null || a === void 0 || b === void 0) return false;
|
|
108
103
|
return a === b;
|
|
109
104
|
};
|
|
110
|
-
var sanitizeURL = (
|
|
105
|
+
var sanitizeURL = (url) => {
|
|
111
106
|
try {
|
|
112
|
-
let decodedURL = decodeURIComponent(
|
|
107
|
+
let decodedURL = decodeURIComponent(url).trim();
|
|
113
108
|
const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
|
|
114
109
|
let protocol = "";
|
|
115
110
|
let rest = decodedURL;
|
|
@@ -137,7 +132,7 @@ var sanitizeURL = (url2) => {
|
|
|
137
132
|
}
|
|
138
133
|
return sanitized;
|
|
139
134
|
} catch {
|
|
140
|
-
return
|
|
135
|
+
return url.trim();
|
|
141
136
|
}
|
|
142
137
|
};
|
|
143
138
|
var isValidRelativePath = (path) => {
|
|
@@ -151,20 +146,42 @@ var isValidRelativePath = (path) => {
|
|
|
151
146
|
var onErrorHandler = (error) => {
|
|
152
147
|
if ((0, import_router.isRouterError)(error)) {
|
|
153
148
|
const { message, status, statusText } = error;
|
|
154
|
-
return Response.json({
|
|
149
|
+
return Response.json({ type: "ROUTER_ERROR", code: "ROUTER_INTERNAL_ERROR", message }, { status, statusText });
|
|
155
150
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
return Response.json({ error: type, error_description: message }, { status: 400 });
|
|
151
|
+
if ((0, import_router.isInvalidZodSchemaError)(error)) {
|
|
152
|
+
return Response.json({ type: "ROUTER_ERROR", code: "INVALID_REQUEST", message: error.errors }, { status: 422 });
|
|
159
153
|
}
|
|
160
|
-
|
|
154
|
+
if (isOAuthProtocolError(error)) {
|
|
155
|
+
const { error: errorCode, message, type, errorURI } = error;
|
|
156
|
+
return Response.json(
|
|
157
|
+
{
|
|
158
|
+
type,
|
|
159
|
+
error: errorCode,
|
|
160
|
+
error_description: message,
|
|
161
|
+
error_uri: errorURI
|
|
162
|
+
},
|
|
163
|
+
{ status: 400 }
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
if (isAuthInternalError(error) || isAuthSecurityError(error)) {
|
|
167
|
+
const { type, code, message } = error;
|
|
168
|
+
return Response.json(
|
|
169
|
+
{
|
|
170
|
+
type,
|
|
171
|
+
code,
|
|
172
|
+
message
|
|
173
|
+
},
|
|
174
|
+
{ status: 400 }
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return Response.json({ type: "SERVER_ERROR", code: "server_error", message: "An unexpected error occurred" }, { status: 500 });
|
|
161
178
|
};
|
|
162
179
|
var getNormalizedOriginPath = (path) => {
|
|
163
180
|
try {
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return `${
|
|
181
|
+
const url = new URL(path);
|
|
182
|
+
url.hash = "";
|
|
183
|
+
url.search = "";
|
|
184
|
+
return `${url.origin}${url.pathname}`;
|
|
168
185
|
} catch {
|
|
169
186
|
return sanitizeURL(path);
|
|
170
187
|
}
|
|
@@ -172,6 +189,24 @@ var getNormalizedOriginPath = (path) => {
|
|
|
172
189
|
var toISOString = (date) => {
|
|
173
190
|
return new Date(date).toISOString();
|
|
174
191
|
};
|
|
192
|
+
var useSecureCookies = (request, trustedProxyHeaders) => {
|
|
193
|
+
return trustedProxyHeaders ? request.url.startsWith("https://") || request.headers.get("X-Forwarded-Proto") === "https" || (request.headers.get("Forwarded")?.includes("proto=https") ?? false) : request.url.startsWith("https://");
|
|
194
|
+
};
|
|
195
|
+
var formatZodError = (error) => {
|
|
196
|
+
if (!error.issues || error.issues.length === 0) {
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
199
|
+
return error.issues.reduce((previous, issue) => {
|
|
200
|
+
const key = issue.path.join(".");
|
|
201
|
+
return {
|
|
202
|
+
...previous,
|
|
203
|
+
[key]: {
|
|
204
|
+
code: issue.code,
|
|
205
|
+
message: issue.message
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}, {});
|
|
209
|
+
};
|
|
175
210
|
|
|
176
211
|
// src/jose.ts
|
|
177
212
|
var import_config = require("dotenv/config");
|
|
@@ -179,6 +214,18 @@ var import_jose = require("@aura-stack/jose");
|
|
|
179
214
|
|
|
180
215
|
// src/secure.ts
|
|
181
216
|
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
217
|
+
|
|
218
|
+
// src/assert.ts
|
|
219
|
+
var isValidURL = (value) => {
|
|
220
|
+
if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
|
|
221
|
+
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
222
|
+
return regex.test(value);
|
|
223
|
+
};
|
|
224
|
+
var isJWTPayloadWithToken = (payload) => {
|
|
225
|
+
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// src/secure.ts
|
|
182
229
|
var generateSecure = (length = 32) => {
|
|
183
230
|
return import_node_crypto.default.randomBytes(length).toString("base64url");
|
|
184
231
|
};
|
|
@@ -205,19 +252,25 @@ var createCSRF = async (jose, csrfCookie) => {
|
|
|
205
252
|
};
|
|
206
253
|
var verifyCSRF = async (jose, cookie, header) => {
|
|
207
254
|
try {
|
|
208
|
-
const
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
255
|
+
const cookiePayload = await jose.verifyJWS(cookie);
|
|
256
|
+
const headerPayload = await jose.verifyJWS(header);
|
|
257
|
+
if (!isJWTPayloadWithToken(cookiePayload)) {
|
|
258
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
259
|
+
}
|
|
260
|
+
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
261
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
262
|
+
}
|
|
263
|
+
const cookieBuffer = Buffer.from(cookiePayload.token);
|
|
264
|
+
const headerBuffer = Buffer.from(headerPayload.token);
|
|
212
265
|
if (!equals(headerBuffer.length, cookieBuffer.length)) {
|
|
213
|
-
throw new
|
|
266
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
214
267
|
}
|
|
215
268
|
if (!import_node_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
|
|
216
|
-
throw new
|
|
269
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
217
270
|
}
|
|
218
271
|
return true;
|
|
219
272
|
} catch {
|
|
220
|
-
throw new
|
|
273
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
221
274
|
}
|
|
222
275
|
};
|
|
223
276
|
var createDerivedSalt = (secret) => {
|
|
@@ -226,38 +279,33 @@ var createDerivedSalt = (secret) => {
|
|
|
226
279
|
|
|
227
280
|
// src/jose.ts
|
|
228
281
|
var createJoseInstance = (secret) => {
|
|
229
|
-
|
|
282
|
+
const env = process.env;
|
|
283
|
+
secret ??= env.AURA_AUTH_SECRET ?? env.AUTH_SECRET;
|
|
230
284
|
if (!secret) {
|
|
231
|
-
throw new
|
|
285
|
+
throw new AuthInternalError(
|
|
286
|
+
"JOSE_INITIALIZATION_FAILED",
|
|
287
|
+
"AURA_AUTH_SECRET environment variable is not set and no secret was provided."
|
|
288
|
+
);
|
|
232
289
|
}
|
|
233
|
-
const salt =
|
|
234
|
-
const { derivedKey:
|
|
290
|
+
const salt = env.AURA_AUTH_SALT ?? env.AUTH_SALT ?? createDerivedSalt(secret);
|
|
291
|
+
const { derivedKey: derivedSigningKey } = (0, import_jose.createDeriveKey)(secret, salt, "signing");
|
|
292
|
+
const { derivedKey: derivedEncryptionKey } = (0, import_jose.createDeriveKey)(secret, salt, "encryption");
|
|
235
293
|
const { derivedKey: derivedCsrfTokenKey } = (0, import_jose.createDeriveKey)(secret, salt, "csrfToken");
|
|
236
|
-
const { decodeJWT, encodeJWT } = (0, import_jose.createJWT)(
|
|
294
|
+
const { decodeJWT, encodeJWT } = (0, import_jose.createJWT)({ jws: derivedSigningKey, jwe: derivedEncryptionKey });
|
|
237
295
|
const { signJWS, verifyJWS } = (0, import_jose.createJWS)(derivedCsrfTokenKey);
|
|
296
|
+
const { encryptJWE, decryptJWE } = (0, import_jose.createJWE)(derivedEncryptionKey);
|
|
238
297
|
return {
|
|
239
298
|
decodeJWT,
|
|
240
299
|
encodeJWT,
|
|
241
300
|
signJWS,
|
|
242
|
-
verifyJWS
|
|
301
|
+
verifyJWS,
|
|
302
|
+
encryptJWE,
|
|
303
|
+
decryptJWE
|
|
243
304
|
};
|
|
244
305
|
};
|
|
245
306
|
|
|
246
307
|
// src/cookie.ts
|
|
247
|
-
var import_cookie = require("cookie");
|
|
248
|
-
|
|
249
|
-
// src/assert.ts
|
|
250
|
-
var isRequest = (value) => {
|
|
251
|
-
return typeof Request !== "undefined" && value instanceof Request;
|
|
252
|
-
};
|
|
253
|
-
var isValidURL = (value) => {
|
|
254
|
-
if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
|
|
255
|
-
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
256
|
-
return regex.test(value);
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
// src/cookie.ts
|
|
260
|
-
var import_cookie2 = require("cookie");
|
|
308
|
+
var import_cookie = require("@aura-stack/router/cookie");
|
|
261
309
|
var COOKIE_NAME = "aura-auth";
|
|
262
310
|
var defaultCookieOptions = {
|
|
263
311
|
httpOnly: true,
|
|
@@ -265,123 +313,162 @@ var defaultCookieOptions = {
|
|
|
265
313
|
path: "/",
|
|
266
314
|
maxAge: 60 * 60 * 24 * 15
|
|
267
315
|
};
|
|
268
|
-
var defaultCookieConfig = {
|
|
269
|
-
strategy: "standard",
|
|
270
|
-
name: COOKIE_NAME,
|
|
271
|
-
options: defaultCookieOptions
|
|
272
|
-
};
|
|
273
316
|
var defaultStandardCookieConfig = {
|
|
274
317
|
secure: false,
|
|
275
|
-
httpOnly: true
|
|
276
|
-
prefix: ""
|
|
318
|
+
httpOnly: true
|
|
277
319
|
};
|
|
278
320
|
var defaultSecureCookieConfig = {
|
|
279
321
|
secure: true,
|
|
280
|
-
|
|
322
|
+
httpOnly: true
|
|
281
323
|
};
|
|
282
324
|
var defaultHostCookieConfig = {
|
|
283
325
|
secure: true,
|
|
284
|
-
|
|
326
|
+
httpOnly: true,
|
|
285
327
|
path: "/",
|
|
286
328
|
domain: void 0
|
|
287
329
|
};
|
|
288
|
-
var
|
|
330
|
+
var oauthCookieOptions = {
|
|
331
|
+
httpOnly: true,
|
|
332
|
+
maxAge: 5 * 60,
|
|
333
|
+
sameSite: "lax",
|
|
334
|
+
expires: new Date(Date.now() + 5 * 60 * 1e3)
|
|
335
|
+
};
|
|
336
|
+
var setCookie = (cookieName, value, options2) => {
|
|
337
|
+
return (0, import_cookie.serialize)(cookieName, value, options2);
|
|
338
|
+
};
|
|
339
|
+
var expiredCookieAttributes = {
|
|
289
340
|
...defaultCookieOptions,
|
|
290
341
|
expires: /* @__PURE__ */ new Date(0),
|
|
291
342
|
maxAge: 0
|
|
292
343
|
};
|
|
293
|
-
var
|
|
294
|
-
|
|
295
|
-
name: options2?.name ?? COOKIE_NAME,
|
|
296
|
-
prefix: options2?.prefix ?? (options2?.secure ? "__Secure-" : ""),
|
|
297
|
-
...defaultCookieOptions,
|
|
298
|
-
...options2
|
|
299
|
-
};
|
|
300
|
-
};
|
|
301
|
-
var setCookie = (cookieName, value, options2) => {
|
|
302
|
-
const { prefix, name } = defineDefaultCookieOptions(options2);
|
|
303
|
-
const cookieNameWithPrefix = `${prefix}${name}.${cookieName}`;
|
|
304
|
-
return (0, import_cookie.serialize)(cookieNameWithPrefix, value, {
|
|
305
|
-
...defaultCookieOptions,
|
|
306
|
-
...options2
|
|
307
|
-
});
|
|
308
|
-
};
|
|
309
|
-
var getCookie = (petition, cookie, options2, optional = false) => {
|
|
310
|
-
const cookies = isRequest(petition) ? petition.headers.get("Cookie") : petition.headers.getSetCookie().join("; ");
|
|
344
|
+
var getCookie = (request, cookieName) => {
|
|
345
|
+
const cookies = request.headers.get("Cookie");
|
|
311
346
|
if (!cookies) {
|
|
312
|
-
|
|
313
|
-
return "";
|
|
314
|
-
}
|
|
315
|
-
throw new AuthError("invalid_request", "No cookies found. There is no active session");
|
|
347
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", "No cookies found. There is no active session");
|
|
316
348
|
}
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (value === void 0) {
|
|
321
|
-
if (optional) {
|
|
322
|
-
return "";
|
|
323
|
-
}
|
|
324
|
-
throw new AuthError("invalid_request", `Cookie "${cookie}" not found. There is no active session`);
|
|
349
|
+
const value = (0, import_cookie.parse)(cookies)[cookieName];
|
|
350
|
+
if (!value) {
|
|
351
|
+
throw new AuthInternalError("COOKIE_NOT_FOUND", `Cookie "${cookieName}" not found. There is no active session`);
|
|
325
352
|
}
|
|
326
353
|
return value;
|
|
327
354
|
};
|
|
328
|
-
var createSessionCookie = async (
|
|
355
|
+
var createSessionCookie = async (jose, session) => {
|
|
329
356
|
try {
|
|
330
357
|
const encoded = await jose.encodeJWT(session);
|
|
331
|
-
return
|
|
358
|
+
return encoded;
|
|
332
359
|
} catch (error) {
|
|
333
|
-
throw new
|
|
360
|
+
throw new AuthInternalError("INVALID_JWT_TOKEN", "Failed to create session cookie", { cause: error });
|
|
334
361
|
}
|
|
335
362
|
};
|
|
336
|
-
var
|
|
337
|
-
|
|
338
|
-
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://");
|
|
339
|
-
if (!cookieOptions.options?.httpOnly) {
|
|
363
|
+
var defineSecureCookieOptions = (useSecure, attributes, strategy) => {
|
|
364
|
+
if (!attributes.httpOnly) {
|
|
340
365
|
console.warn(
|
|
341
366
|
"[WARNING]: Cookie is configured without HttpOnly. This allows JavaScript access via document.cookie and increases XSS risk."
|
|
342
367
|
);
|
|
343
368
|
}
|
|
344
|
-
if (
|
|
369
|
+
if (attributes.domain === "*") {
|
|
370
|
+
attributes.domain = void 0;
|
|
345
371
|
console.warn("[WARNING]: Cookie 'Domain' is set to '*', which is insecure. Avoid wildcard domains.");
|
|
346
372
|
}
|
|
347
|
-
if (!
|
|
348
|
-
|
|
349
|
-
if (options2?.secure) {
|
|
373
|
+
if (!useSecure) {
|
|
374
|
+
if (attributes.secure) {
|
|
350
375
|
console.warn(
|
|
351
376
|
"[WARNING]: The 'Secure' attribute will be disabled for this cookie. Serve over HTTPS to enforce Secure cookies."
|
|
352
377
|
);
|
|
353
378
|
}
|
|
354
|
-
if (
|
|
355
|
-
|
|
379
|
+
if (attributes.sameSite == "none") {
|
|
380
|
+
attributes.sameSite = "lax";
|
|
381
|
+
console.warn("[WARNING]: SameSite=None requires Secure attribute. Changing SameSite to 'Lax'.");
|
|
356
382
|
}
|
|
357
383
|
if (process.env.NODE_ENV === "production") {
|
|
358
384
|
console.warn("[WARNING]: In production, ensure cookies are served over HTTPS to maintain security.");
|
|
359
385
|
}
|
|
386
|
+
if (strategy === "host") {
|
|
387
|
+
console.warn("[WARNING]: __Host- cookies require a secure context. Falling back to standard cookie settings.");
|
|
388
|
+
}
|
|
360
389
|
return {
|
|
361
390
|
...defaultCookieOptions,
|
|
362
|
-
...
|
|
363
|
-
|
|
364
|
-
...defaultStandardCookieConfig,
|
|
365
|
-
name
|
|
391
|
+
...attributes,
|
|
392
|
+
...defaultStandardCookieConfig
|
|
366
393
|
};
|
|
367
394
|
}
|
|
368
|
-
return
|
|
395
|
+
return strategy === "host" ? {
|
|
369
396
|
...defaultCookieOptions,
|
|
370
|
-
...
|
|
371
|
-
...defaultHostCookieConfig
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
var oauthCookie = (options2) => {
|
|
397
|
+
...attributes,
|
|
398
|
+
...defaultHostCookieConfig
|
|
399
|
+
} : { ...defaultCookieOptions, ...attributes, ...defaultSecureCookieConfig };
|
|
400
|
+
};
|
|
401
|
+
var createCookieStore = (useSecure, prefix, overrides) => {
|
|
402
|
+
prefix ??= COOKIE_NAME;
|
|
403
|
+
const securePrefix = useSecure ? "__Secure-" : "";
|
|
404
|
+
const hostPrefix = useSecure ? "__Host-" : "";
|
|
379
405
|
return {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
406
|
+
sessionToken: {
|
|
407
|
+
name: `${securePrefix}${prefix}.${overrides?.sessionToken?.name ?? "sessionToken"}`,
|
|
408
|
+
attributes: defineSecureCookieOptions(
|
|
409
|
+
useSecure,
|
|
410
|
+
{
|
|
411
|
+
...defaultCookieOptions,
|
|
412
|
+
...overrides?.sessionToken?.attributes
|
|
413
|
+
},
|
|
414
|
+
overrides?.sessionToken?.attributes?.strategy ?? "secure"
|
|
415
|
+
)
|
|
416
|
+
},
|
|
417
|
+
state: {
|
|
418
|
+
name: `${securePrefix}${prefix}.${overrides?.state?.name ?? "state"}`,
|
|
419
|
+
attributes: defineSecureCookieOptions(
|
|
420
|
+
useSecure,
|
|
421
|
+
{
|
|
422
|
+
...oauthCookieOptions,
|
|
423
|
+
...overrides?.state?.attributes
|
|
424
|
+
},
|
|
425
|
+
overrides?.state?.attributes?.strategy ?? "secure"
|
|
426
|
+
)
|
|
427
|
+
},
|
|
428
|
+
csrfToken: {
|
|
429
|
+
name: `${hostPrefix}${prefix}.${overrides?.csrfToken?.name ?? "csrfToken"}`,
|
|
430
|
+
attributes: defineSecureCookieOptions(
|
|
431
|
+
useSecure,
|
|
432
|
+
{
|
|
433
|
+
...overrides?.csrfToken?.attributes,
|
|
434
|
+
...defaultHostCookieConfig
|
|
435
|
+
},
|
|
436
|
+
overrides?.csrfToken?.attributes?.strategy ?? "host"
|
|
437
|
+
)
|
|
438
|
+
},
|
|
439
|
+
redirect_to: {
|
|
440
|
+
name: `${securePrefix}${prefix}.${overrides?.redirect_to?.name ?? "redirect_to"}`,
|
|
441
|
+
attributes: defineSecureCookieOptions(
|
|
442
|
+
useSecure,
|
|
443
|
+
{
|
|
444
|
+
...oauthCookieOptions,
|
|
445
|
+
...overrides?.redirect_to?.attributes
|
|
446
|
+
},
|
|
447
|
+
overrides?.redirect_to?.attributes?.strategy ?? "secure"
|
|
448
|
+
)
|
|
449
|
+
},
|
|
450
|
+
redirect_uri: {
|
|
451
|
+
name: `${securePrefix}${prefix}.${overrides?.redirect_uri?.name ?? "redirect_uri"}`,
|
|
452
|
+
attributes: defineSecureCookieOptions(
|
|
453
|
+
useSecure,
|
|
454
|
+
{
|
|
455
|
+
...oauthCookieOptions,
|
|
456
|
+
...overrides?.redirect_uri?.attributes
|
|
457
|
+
},
|
|
458
|
+
overrides?.redirect_uri?.attributes?.strategy ?? "secure"
|
|
459
|
+
)
|
|
460
|
+
},
|
|
461
|
+
code_verifier: {
|
|
462
|
+
name: `${securePrefix}${prefix}.${overrides?.code_verifier?.name ?? "code_verifier"}`,
|
|
463
|
+
attributes: defineSecureCookieOptions(
|
|
464
|
+
useSecure,
|
|
465
|
+
{
|
|
466
|
+
...oauthCookieOptions,
|
|
467
|
+
...overrides?.code_verifier?.attributes
|
|
468
|
+
},
|
|
469
|
+
overrides?.code_verifier?.attributes?.strategy ?? "secure"
|
|
470
|
+
)
|
|
471
|
+
}
|
|
385
472
|
};
|
|
386
473
|
};
|
|
387
474
|
|
|
@@ -453,7 +540,6 @@ var discord = {
|
|
|
453
540
|
}
|
|
454
541
|
return {
|
|
455
542
|
sub: profile.id,
|
|
456
|
-
// https://discord.com/developers/docs/change-log#display-names
|
|
457
543
|
name: profile.global_name ?? profile.username,
|
|
458
544
|
email: profile.email ?? "",
|
|
459
545
|
image
|
|
@@ -518,59 +604,32 @@ var x = {
|
|
|
518
604
|
}
|
|
519
605
|
};
|
|
520
606
|
|
|
521
|
-
// src/oauth/
|
|
522
|
-
var
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
var defineOAuthEnvironment = (oauth) => {
|
|
532
|
-
const env = process.env;
|
|
533
|
-
return {
|
|
534
|
-
clientId: env[`AURA_AUTH_${oauth.toUpperCase()}_CLIENT_ID`],
|
|
535
|
-
clientSecret: env[`AURA_AUTH_${oauth.toUpperCase()}_CLIENT_SECRET`]
|
|
536
|
-
};
|
|
537
|
-
};
|
|
538
|
-
var defineOAuthProviderConfig = (config2) => {
|
|
539
|
-
if (typeof config2 === "string") {
|
|
540
|
-
const definition = defineOAuthEnvironment(config2);
|
|
541
|
-
const oauthConfig = builtInOAuthProviders[config2];
|
|
607
|
+
// src/oauth/strava.ts
|
|
608
|
+
var strava = {
|
|
609
|
+
id: "strava",
|
|
610
|
+
name: "Strava",
|
|
611
|
+
authorizeURL: "https://www.strava.com/oauth/authorize",
|
|
612
|
+
accessToken: "https://www.strava.com/oauth/token",
|
|
613
|
+
userInfo: "https://www.strava.com/api/v3/athlete",
|
|
614
|
+
scope: "read",
|
|
615
|
+
responseType: "code",
|
|
616
|
+
profile(profile) {
|
|
542
617
|
return {
|
|
543
|
-
|
|
544
|
-
|
|
618
|
+
sub: profile.id.toString(),
|
|
619
|
+
name: `${profile.firstname} ${profile.lastname}`,
|
|
620
|
+
image: profile.profile,
|
|
621
|
+
email: ""
|
|
545
622
|
};
|
|
546
623
|
}
|
|
547
|
-
return config2;
|
|
548
|
-
};
|
|
549
|
-
var createBuiltInOAuthProviders = (oauth = []) => {
|
|
550
|
-
return oauth.reduce((previous, config2) => {
|
|
551
|
-
const oauthConfig = defineOAuthProviderConfig(config2);
|
|
552
|
-
return { ...previous, [oauthConfig.id]: oauthConfig };
|
|
553
|
-
}, {});
|
|
554
|
-
};
|
|
555
|
-
|
|
556
|
-
// src/actions/signIn/signIn.ts
|
|
557
|
-
var import_zod = __toESM(require("zod"), 1);
|
|
558
|
-
var import_router2 = require("@aura-stack/router");
|
|
559
|
-
|
|
560
|
-
// src/response.ts
|
|
561
|
-
var AuraResponse = class extends Response {
|
|
562
|
-
static json(body, init) {
|
|
563
|
-
return Response.json(body, init);
|
|
564
|
-
}
|
|
565
624
|
};
|
|
566
625
|
|
|
567
626
|
// src/schemas.ts
|
|
568
627
|
var import_v4 = require("zod/v4");
|
|
569
628
|
var OAuthProviderConfigSchema = (0, import_v4.object)({
|
|
570
|
-
authorizeURL: (0, import_v4.
|
|
571
|
-
accessToken: (0, import_v4.
|
|
629
|
+
authorizeURL: (0, import_v4.httpUrl)(),
|
|
630
|
+
accessToken: (0, import_v4.httpUrl)(),
|
|
572
631
|
scope: (0, import_v4.string)().optional(),
|
|
573
|
-
userInfo: (0, import_v4.
|
|
632
|
+
userInfo: (0, import_v4.httpUrl)(),
|
|
574
633
|
responseType: (0, import_v4.enum)(["code", "token", "id_token"]),
|
|
575
634
|
clientId: (0, import_v4.string)(),
|
|
576
635
|
clientSecret: (0, import_v4.string)()
|
|
@@ -582,8 +641,8 @@ var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
|
582
641
|
codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
|
|
583
642
|
});
|
|
584
643
|
var OAuthAuthorizationResponse = (0, import_v4.object)({
|
|
585
|
-
state: (0, import_v4.string)(),
|
|
586
|
-
code: (0, import_v4.string)()
|
|
644
|
+
state: (0, import_v4.string)("Missing state parameter in the OAuth authorization response."),
|
|
645
|
+
code: (0, import_v4.string)("Missing code parameter in the OAuth authorization response.")
|
|
587
646
|
});
|
|
588
647
|
var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
589
648
|
error: (0, import_v4.enum)([
|
|
@@ -627,12 +686,64 @@ var OAuthErrorResponse = (0, import_v4.object)({
|
|
|
627
686
|
error: (0, import_v4.string)(),
|
|
628
687
|
error_description: (0, import_v4.string)().optional()
|
|
629
688
|
});
|
|
689
|
+
var OAuthEnvSchema = (0, import_v4.object)({
|
|
690
|
+
clientId: import_v4.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
691
|
+
clientSecret: import_v4.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// src/oauth/index.ts
|
|
695
|
+
var builtInOAuthProviders = {
|
|
696
|
+
github,
|
|
697
|
+
bitbucket,
|
|
698
|
+
figma,
|
|
699
|
+
discord,
|
|
700
|
+
gitlab,
|
|
701
|
+
spotify,
|
|
702
|
+
x,
|
|
703
|
+
strava
|
|
704
|
+
};
|
|
705
|
+
var defineOAuthEnvironment = (oauth) => {
|
|
706
|
+
const env = process.env;
|
|
707
|
+
const clientIdSuffix = `${oauth.toUpperCase()}_CLIENT_ID`;
|
|
708
|
+
const clientSecretSuffix = `${oauth.toUpperCase()}_CLIENT_SECRET`;
|
|
709
|
+
const loadEnvs = OAuthEnvSchema.safeParse({
|
|
710
|
+
clientId: env[`AURA_AUTH_${clientIdSuffix}`] ?? env[`AUTH_${clientIdSuffix}`] ?? env[`${clientIdSuffix}`],
|
|
711
|
+
clientSecret: env[`AURA_AUTH_${clientSecretSuffix}`] ?? env[`AUTH_${clientSecretSuffix}`] ?? env[`${clientSecretSuffix}`]
|
|
712
|
+
});
|
|
713
|
+
if (!loadEnvs.success) {
|
|
714
|
+
const msg = JSON.stringify(formatZodError(loadEnvs.error), null, 2);
|
|
715
|
+
throw new AuthInternalError("INVALID_ENVIRONMENT_CONFIGURATION", msg);
|
|
716
|
+
}
|
|
717
|
+
return loadEnvs.data;
|
|
718
|
+
};
|
|
719
|
+
var defineOAuthProviderConfig = (config2) => {
|
|
720
|
+
if (typeof config2 === "string") {
|
|
721
|
+
const definition = defineOAuthEnvironment(config2);
|
|
722
|
+
const oauthConfig = builtInOAuthProviders[config2];
|
|
723
|
+
return {
|
|
724
|
+
...oauthConfig,
|
|
725
|
+
...definition
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
return config2;
|
|
729
|
+
};
|
|
730
|
+
var createBuiltInOAuthProviders = (oauth = []) => {
|
|
731
|
+
return oauth.reduce((previous, config2) => {
|
|
732
|
+
const oauthConfig = defineOAuthProviderConfig(config2);
|
|
733
|
+
return { ...previous, [oauthConfig.id]: oauthConfig };
|
|
734
|
+
}, {});
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
// src/actions/signIn/signIn.ts
|
|
738
|
+
var import_zod = __toESM(require("zod"), 1);
|
|
739
|
+
var import_router2 = require("@aura-stack/router");
|
|
630
740
|
|
|
631
741
|
// src/actions/signIn/authorization.ts
|
|
632
742
|
var createAuthorizationURL = (oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod) => {
|
|
633
743
|
const parsed = OAuthAuthorization.safeParse({ ...oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod });
|
|
634
744
|
if (!parsed.success) {
|
|
635
|
-
|
|
745
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
746
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
636
747
|
}
|
|
637
748
|
const { authorizeURL, ...options2 } = parsed.data;
|
|
638
749
|
const { userInfo, accessToken, clientSecret, ...required } = options2;
|
|
@@ -650,8 +761,8 @@ var getOriginURL = (request, trustedProxyHeaders) => {
|
|
|
650
761
|
}
|
|
651
762
|
};
|
|
652
763
|
var createRedirectURI = (request, oauth, basePath, trustedProxyHeaders) => {
|
|
653
|
-
const
|
|
654
|
-
return `${
|
|
764
|
+
const url = getOriginURL(request, trustedProxyHeaders);
|
|
765
|
+
return `${url.origin}${basePath}/callback/${oauth}`;
|
|
655
766
|
};
|
|
656
767
|
var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
657
768
|
try {
|
|
@@ -665,15 +776,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
665
776
|
}
|
|
666
777
|
const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
|
|
667
778
|
if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
|
|
668
|
-
throw new
|
|
779
|
+
throw new AuthSecurityError(
|
|
780
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
781
|
+
"The redirectTo parameter does not match the hosted origin."
|
|
782
|
+
);
|
|
669
783
|
}
|
|
670
784
|
return sanitizeURL(redirectToURL.pathname);
|
|
671
785
|
}
|
|
672
786
|
if (referer) {
|
|
673
787
|
const refererURL = new URL(sanitizeURL(referer));
|
|
674
788
|
if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
|
|
675
|
-
throw new
|
|
676
|
-
|
|
789
|
+
throw new AuthSecurityError(
|
|
790
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
677
791
|
"The referer of the request does not match the hosted origin."
|
|
678
792
|
);
|
|
679
793
|
}
|
|
@@ -682,16 +796,16 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
682
796
|
if (origin) {
|
|
683
797
|
const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
|
|
684
798
|
if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
|
|
685
|
-
throw new
|
|
799
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
686
800
|
}
|
|
687
801
|
return sanitizeURL(originURL.pathname);
|
|
688
802
|
}
|
|
689
803
|
return "/";
|
|
690
804
|
} catch (error) {
|
|
691
|
-
if (
|
|
805
|
+
if (isAuthSecurityError(error)) {
|
|
692
806
|
throw error;
|
|
693
807
|
}
|
|
694
|
-
throw new
|
|
808
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
695
809
|
}
|
|
696
810
|
};
|
|
697
811
|
|
|
@@ -700,7 +814,7 @@ var signInConfig = (oauth) => {
|
|
|
700
814
|
return (0, import_router2.createEndpointConfig)("/signIn/:oauth", {
|
|
701
815
|
schemas: {
|
|
702
816
|
params: import_zod.default.object({
|
|
703
|
-
oauth: import_zod.default.enum(Object.keys(oauth)),
|
|
817
|
+
oauth: import_zod.default.enum(Object.keys(oauth), "The OAuth provider is not supported or invalid."),
|
|
704
818
|
redirectTo: import_zod.default.string().optional()
|
|
705
819
|
})
|
|
706
820
|
}
|
|
@@ -713,52 +827,23 @@ var signInAction = (oauth) => {
|
|
|
713
827
|
async (ctx) => {
|
|
714
828
|
const {
|
|
715
829
|
request,
|
|
830
|
+
headers: headersBuilder,
|
|
716
831
|
params: { oauth: oauth2, redirectTo },
|
|
717
832
|
context: { oauth: providers, cookies, trustedProxyHeaders, basePath }
|
|
718
833
|
} = ctx;
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
731
|
-
const codeVerifierCookie = setCookie("code_verifier", codeVerifier, oauthCookie(cookieOptions));
|
|
732
|
-
const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
|
|
733
|
-
const headers = new Headers();
|
|
734
|
-
headers.set("Location", authorization);
|
|
735
|
-
headers.append("Set-Cookie", stateCookie);
|
|
736
|
-
headers.append("Set-Cookie", redirectURICookie);
|
|
737
|
-
headers.append("Set-Cookie", redirectToCookie);
|
|
738
|
-
headers.append("Set-Cookie", codeVerifierCookie);
|
|
739
|
-
return Response.json(
|
|
740
|
-
{ oauth: oauth2 },
|
|
741
|
-
{
|
|
742
|
-
status: 302,
|
|
743
|
-
headers
|
|
744
|
-
}
|
|
745
|
-
);
|
|
746
|
-
} catch (error) {
|
|
747
|
-
if (isAuthError(error)) {
|
|
748
|
-
const { type, message } = error;
|
|
749
|
-
return AuraResponse.json(
|
|
750
|
-
{ error: type, error_description: message },
|
|
751
|
-
{ status: import_router2.statusCode.BAD_REQUEST }
|
|
752
|
-
);
|
|
834
|
+
const state = generateSecure();
|
|
835
|
+
const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
|
|
836
|
+
const redirectToValue = createRedirectTo(request, redirectTo, trustedProxyHeaders);
|
|
837
|
+
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
838
|
+
const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
|
|
839
|
+
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();
|
|
840
|
+
return Response.json(
|
|
841
|
+
{ oauth: oauth2 },
|
|
842
|
+
{
|
|
843
|
+
status: 302,
|
|
844
|
+
headers
|
|
753
845
|
}
|
|
754
|
-
|
|
755
|
-
{
|
|
756
|
-
error: ERROR_RESPONSE.AUTHORIZATION.SERVER_ERROR,
|
|
757
|
-
error_description: "An unexpected error occurred"
|
|
758
|
-
},
|
|
759
|
-
{ status: import_router2.statusCode.INTERNAL_SERVER_ERROR }
|
|
760
|
-
);
|
|
761
|
-
}
|
|
846
|
+
);
|
|
762
847
|
},
|
|
763
848
|
signInConfig(oauth)
|
|
764
849
|
);
|
|
@@ -799,11 +884,20 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
799
884
|
const json = await response.json();
|
|
800
885
|
const { success, data } = OAuthErrorResponse.safeParse(json);
|
|
801
886
|
if (success) {
|
|
802
|
-
throw new
|
|
887
|
+
throw new OAuthProtocolError(
|
|
888
|
+
data.error,
|
|
889
|
+
data?.error_description ?? "An error occurred while fetching user information."
|
|
890
|
+
);
|
|
803
891
|
}
|
|
804
892
|
return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
|
|
805
893
|
} catch (error) {
|
|
806
|
-
|
|
894
|
+
if (isOAuthProtocolError(error)) {
|
|
895
|
+
throw error;
|
|
896
|
+
}
|
|
897
|
+
if (isNativeError(error)) {
|
|
898
|
+
throw new OAuthProtocolError("invalid_request", error.message, "", { cause: error });
|
|
899
|
+
}
|
|
900
|
+
throw new OAuthProtocolError("invalid_request", "Failed to fetch user information.", "", { cause: error });
|
|
807
901
|
}
|
|
808
902
|
};
|
|
809
903
|
|
|
@@ -811,7 +905,8 @@ var getUserInfo = async (oauthConfig, accessToken) => {
|
|
|
811
905
|
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
|
|
812
906
|
const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
|
|
813
907
|
if (!parsed.success) {
|
|
814
|
-
|
|
908
|
+
const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
|
|
909
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
|
|
815
910
|
}
|
|
816
911
|
const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
|
|
817
912
|
try {
|
|
@@ -835,13 +930,13 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
|
|
|
835
930
|
if (!token.success) {
|
|
836
931
|
const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
|
|
837
932
|
if (!success) {
|
|
838
|
-
throw new
|
|
933
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
|
|
839
934
|
}
|
|
840
|
-
throw new
|
|
935
|
+
throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
|
|
841
936
|
}
|
|
842
937
|
return token.data;
|
|
843
938
|
} catch (error) {
|
|
844
|
-
throw
|
|
939
|
+
throw error;
|
|
845
940
|
}
|
|
846
941
|
};
|
|
847
942
|
|
|
@@ -851,7 +946,7 @@ var callbackConfig = (oauth) => {
|
|
|
851
946
|
schemas: {
|
|
852
947
|
searchParams: OAuthAuthorizationResponse,
|
|
853
948
|
params: import_zod2.default.object({
|
|
854
|
-
oauth: import_zod2.default.enum(Object.keys(oauth))
|
|
949
|
+
oauth: import_zod2.default.enum(Object.keys(oauth), "The OAuth provider is not supported or invalid.")
|
|
855
950
|
})
|
|
856
951
|
},
|
|
857
952
|
middlewares: [
|
|
@@ -859,7 +954,7 @@ var callbackConfig = (oauth) => {
|
|
|
859
954
|
const response = OAuthAuthorizationErrorResponse.safeParse(ctx.searchParams);
|
|
860
955
|
if (response.success) {
|
|
861
956
|
const { error, error_description } = response.data;
|
|
862
|
-
throw new
|
|
957
|
+
throw new OAuthProtocolError(error, error_description ?? "OAuth Authorization Error");
|
|
863
958
|
}
|
|
864
959
|
return ctx;
|
|
865
960
|
}
|
|
@@ -875,66 +970,32 @@ var callbackAction = (oauth) => {
|
|
|
875
970
|
request,
|
|
876
971
|
params: { oauth: oauth2 },
|
|
877
972
|
searchParams: { code, state },
|
|
878
|
-
context: { oauth: providers, cookies, jose
|
|
973
|
+
context: { oauth: providers, cookies, jose }
|
|
879
974
|
} = ctx;
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
}
|
|
890
|
-
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
891
|
-
const sanitized = sanitizeURL(cookieRedirectTo);
|
|
892
|
-
if (!isValidRelativePath(sanitized)) {
|
|
893
|
-
throw new AuthError(
|
|
894
|
-
ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST,
|
|
895
|
-
"Invalid redirect path. Potential open redirect attack detected."
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
const headers = new Headers(cacheControl);
|
|
899
|
-
headers.set("Location", sanitized);
|
|
900
|
-
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
901
|
-
const sessionCookie = await createSessionCookie(userInfo, cookieOptions, jose);
|
|
902
|
-
const csrfToken = await createCSRF(jose);
|
|
903
|
-
const csrfCookie = setCookie(
|
|
904
|
-
"csrfToken",
|
|
905
|
-
csrfToken,
|
|
906
|
-
secureCookieOptions(
|
|
907
|
-
request,
|
|
908
|
-
{
|
|
909
|
-
...cookies,
|
|
910
|
-
strategy: "host"
|
|
911
|
-
},
|
|
912
|
-
trustedProxyHeaders
|
|
913
|
-
)
|
|
975
|
+
const oauthConfig = providers[oauth2];
|
|
976
|
+
const cookieState = getCookie(request, cookies.state.name);
|
|
977
|
+
const cookieRedirectTo = getCookie(request, cookies.redirect_to.name);
|
|
978
|
+
const cookieRedirectURI = getCookie(request, cookies.redirect_uri.name);
|
|
979
|
+
const codeVerifier = getCookie(request, cookies.code_verifier.name);
|
|
980
|
+
if (!equals(cookieState, state)) {
|
|
981
|
+
throw new AuthSecurityError(
|
|
982
|
+
"MISMATCHING_STATE",
|
|
983
|
+
"The provided state passed in the OAuth response does not match the stored state."
|
|
914
984
|
);
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
} catch (error) {
|
|
923
|
-
if (isAuthError(error)) {
|
|
924
|
-
const { type, message } = error;
|
|
925
|
-
return AuraResponse.json(
|
|
926
|
-
{ error: type, error_description: message },
|
|
927
|
-
{ status: import_router3.statusCode.BAD_REQUEST }
|
|
928
|
-
);
|
|
929
|
-
}
|
|
930
|
-
return AuraResponse.json(
|
|
931
|
-
{
|
|
932
|
-
error: ERROR_RESPONSE.ACCESS_TOKEN.INVALID_CLIENT,
|
|
933
|
-
error_description: "An unexpected error occurred"
|
|
934
|
-
},
|
|
935
|
-
{ status: import_router3.statusCode.INTERNAL_SERVER_ERROR }
|
|
985
|
+
}
|
|
986
|
+
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
987
|
+
const sanitized = sanitizeURL(cookieRedirectTo);
|
|
988
|
+
if (!isValidRelativePath(sanitized)) {
|
|
989
|
+
throw new AuthSecurityError(
|
|
990
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
991
|
+
"Invalid redirect path. Potential open redirect attack detected."
|
|
936
992
|
);
|
|
937
993
|
}
|
|
994
|
+
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
995
|
+
const sessionCookie = await createSessionCookie(jose, userInfo);
|
|
996
|
+
const csrfToken = await createCSRF(jose);
|
|
997
|
+
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();
|
|
998
|
+
return Response.json({ oauth: oauth2 }, { status: 302, headers });
|
|
938
999
|
},
|
|
939
1000
|
callbackConfig(oauth)
|
|
940
1001
|
);
|
|
@@ -945,19 +1006,16 @@ var import_router4 = require("@aura-stack/router");
|
|
|
945
1006
|
var sessionAction = (0, import_router4.createEndpoint)("GET", "/session", async (ctx) => {
|
|
946
1007
|
const {
|
|
947
1008
|
request,
|
|
948
|
-
context: {
|
|
1009
|
+
context: { jose, cookies }
|
|
949
1010
|
} = ctx;
|
|
950
|
-
const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
|
|
951
1011
|
try {
|
|
952
|
-
const session = getCookie(request,
|
|
1012
|
+
const session = getCookie(request, cookies.sessionToken.name);
|
|
953
1013
|
const decoded = await jose.decodeJWT(session);
|
|
954
1014
|
const { exp, iat, jti, nbf, ...user } = decoded;
|
|
955
1015
|
const headers = new Headers(cacheControl);
|
|
956
1016
|
return Response.json({ user, expires: toISOString(exp * 1e3) }, { headers });
|
|
957
|
-
} catch {
|
|
958
|
-
const headers = new
|
|
959
|
-
const sessionCookie = expireCookie("sessionToken", cookieOptions);
|
|
960
|
-
headers.set("Set-Cookie", sessionCookie);
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
const headers = new import_router4.HeadersBuilder(cacheControl).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
961
1019
|
return Response.json({ authenticated: false, message: "Unauthorized" }, { status: 401, headers });
|
|
962
1020
|
}
|
|
963
1021
|
});
|
|
@@ -981,98 +1039,78 @@ var signOutAction = (0, import_router5.createEndpoint)(
|
|
|
981
1039
|
request,
|
|
982
1040
|
headers,
|
|
983
1041
|
searchParams: { redirectTo },
|
|
984
|
-
context: {
|
|
1042
|
+
context: { jose, cookies }
|
|
985
1043
|
} = ctx;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
await verifyCSRF(jose, csrfToken, header);
|
|
998
|
-
await jose.decodeJWT(session);
|
|
999
|
-
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
1000
|
-
const location = createRedirectTo(
|
|
1001
|
-
new Request(normalizedOriginPath, {
|
|
1002
|
-
headers
|
|
1003
|
-
}),
|
|
1004
|
-
redirectTo
|
|
1005
|
-
);
|
|
1006
|
-
const responseHeaders = new Headers(cacheControl);
|
|
1007
|
-
responseHeaders.append("Set-Cookie", expireCookie("sessionToken", cookiesOptions));
|
|
1008
|
-
responseHeaders.append(
|
|
1009
|
-
"Set-Cookie",
|
|
1010
|
-
expireCookie("csrfToken", { ...cookiesOptions, prefix: cookiesOptions.secure ? "__Host-" : "" })
|
|
1011
|
-
);
|
|
1012
|
-
responseHeaders.append("Location", location);
|
|
1013
|
-
return Response.json(
|
|
1014
|
-
{ message: "Signed out successfully" },
|
|
1015
|
-
{ status: import_router5.statusCode.ACCEPTED, headers: responseHeaders }
|
|
1016
|
-
);
|
|
1017
|
-
} catch (error) {
|
|
1018
|
-
if (error instanceof InvalidCsrfTokenError) {
|
|
1019
|
-
return AuraResponse.json(
|
|
1020
|
-
{
|
|
1021
|
-
error: "invalid_csrf_token",
|
|
1022
|
-
error_description: "The provided CSRF token is invalid or has expired"
|
|
1023
|
-
},
|
|
1024
|
-
{ status: import_router5.statusCode.UNAUTHORIZED }
|
|
1025
|
-
);
|
|
1026
|
-
}
|
|
1027
|
-
if (error instanceof InvalidRedirectToError) {
|
|
1028
|
-
const { type, message } = error;
|
|
1029
|
-
return AuraResponse.json(
|
|
1030
|
-
{
|
|
1031
|
-
error: type,
|
|
1032
|
-
error_description: message
|
|
1033
|
-
},
|
|
1034
|
-
{ status: import_router5.statusCode.BAD_REQUEST }
|
|
1035
|
-
);
|
|
1036
|
-
}
|
|
1037
|
-
return AuraResponse.json(
|
|
1038
|
-
{
|
|
1039
|
-
error: "invalid_session_token",
|
|
1040
|
-
error_description: "The provided sessionToken is invalid or has already expired"
|
|
1041
|
-
},
|
|
1042
|
-
{ status: import_router5.statusCode.UNAUTHORIZED }
|
|
1043
|
-
);
|
|
1044
|
+
const session = headers.getCookie(cookies.sessionToken.name);
|
|
1045
|
+
const csrfToken = headers.getCookie(cookies.csrfToken.name);
|
|
1046
|
+
const header = headers.getHeader("X-CSRF-Token");
|
|
1047
|
+
if (!session) {
|
|
1048
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1049
|
+
}
|
|
1050
|
+
if (!csrfToken) {
|
|
1051
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
1052
|
+
}
|
|
1053
|
+
if (!header) {
|
|
1054
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
|
|
1044
1055
|
}
|
|
1056
|
+
await verifyCSRF(jose, csrfToken, header);
|
|
1057
|
+
await jose.decodeJWT(session);
|
|
1058
|
+
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
1059
|
+
const location = createRedirectTo(
|
|
1060
|
+
new Request(normalizedOriginPath, {
|
|
1061
|
+
headers: headers.toHeaders()
|
|
1062
|
+
}),
|
|
1063
|
+
redirectTo
|
|
1064
|
+
);
|
|
1065
|
+
const headersList = new import_router5.HeadersBuilder(cacheControl).setHeader("Location", location).setCookie(cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
1066
|
+
return Response.json({ message: "Signed out successfully" }, { status: import_router5.statusCode.ACCEPTED, headers: headersList });
|
|
1045
1067
|
},
|
|
1046
1068
|
config
|
|
1047
1069
|
);
|
|
1048
1070
|
|
|
1049
1071
|
// src/actions/csrfToken/csrfToken.ts
|
|
1050
1072
|
var import_router6 = require("@aura-stack/router");
|
|
1073
|
+
var getCSRFToken = (request, cookieName) => {
|
|
1074
|
+
try {
|
|
1075
|
+
return getCookie(request, cookieName);
|
|
1076
|
+
} catch {
|
|
1077
|
+
return void 0;
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1051
1080
|
var csrfTokenAction = (0, import_router6.createEndpoint)("GET", "/csrfToken", async (ctx) => {
|
|
1052
1081
|
const {
|
|
1053
1082
|
request,
|
|
1054
|
-
context: {
|
|
1083
|
+
context: { jose, cookies }
|
|
1055
1084
|
} = ctx;
|
|
1056
|
-
const
|
|
1057
|
-
const
|
|
1058
|
-
const csrfToken = await createCSRF(jose, existingCSRFToken);
|
|
1085
|
+
const token = getCSRFToken(request, cookies.csrfToken.name);
|
|
1086
|
+
const csrfToken = await createCSRF(jose, token);
|
|
1059
1087
|
const headers = new Headers(cacheControl);
|
|
1060
|
-
headers.
|
|
1088
|
+
headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
|
|
1061
1089
|
return Response.json({ csrfToken }, { headers });
|
|
1062
1090
|
});
|
|
1063
1091
|
|
|
1064
1092
|
// src/index.ts
|
|
1065
1093
|
var createInternalConfig = (authConfig) => {
|
|
1094
|
+
const useSecure = authConfig?.trustedProxyHeaders ?? false;
|
|
1066
1095
|
return {
|
|
1067
1096
|
basePath: authConfig?.basePath ?? "/auth",
|
|
1068
1097
|
onError: onErrorHandler,
|
|
1069
1098
|
context: {
|
|
1070
1099
|
oauth: createBuiltInOAuthProviders(authConfig?.oauth),
|
|
1071
|
-
cookies: authConfig?.cookies ??
|
|
1100
|
+
cookies: createCookieStore(useSecure, authConfig?.cookies?.prefix, authConfig?.cookies?.overrides ?? {}),
|
|
1072
1101
|
jose: createJoseInstance(authConfig?.secret),
|
|
1102
|
+
secret: authConfig?.secret,
|
|
1073
1103
|
basePath: authConfig?.basePath ?? "/auth",
|
|
1074
|
-
trustedProxyHeaders:
|
|
1075
|
-
}
|
|
1104
|
+
trustedProxyHeaders: useSecure
|
|
1105
|
+
},
|
|
1106
|
+
middlewares: [
|
|
1107
|
+
(ctx) => {
|
|
1108
|
+
const useSecure2 = useSecureCookies(ctx.request, ctx.context.trustedProxyHeaders);
|
|
1109
|
+
const cookies = createCookieStore(useSecure2, authConfig?.cookies?.prefix, authConfig?.cookies?.overrides ?? {});
|
|
1110
|
+
ctx.context.cookies = cookies;
|
|
1111
|
+
return ctx;
|
|
1112
|
+
}
|
|
1113
|
+
]
|
|
1076
1114
|
};
|
|
1077
1115
|
};
|
|
1078
1116
|
var createAuth = (authConfig) => {
|