@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.
Files changed (99) hide show
  1. package/dist/@types/index.d.ts +3 -4
  2. package/dist/@types/router.d.d.ts +3 -4
  3. package/dist/@types/utility.d.ts +1 -5
  4. package/dist/actions/callback/access-token.cjs +51 -41
  5. package/dist/actions/callback/access-token.d.ts +3 -4
  6. package/dist/actions/callback/access-token.js +4 -3
  7. package/dist/actions/callback/callback.cjs +115 -210
  8. package/dist/actions/callback/callback.d.ts +3 -4
  9. package/dist/actions/callback/callback.js +9 -10
  10. package/dist/actions/callback/userinfo.cjs +35 -22
  11. package/dist/actions/callback/userinfo.d.ts +3 -4
  12. package/dist/actions/callback/userinfo.js +6 -5
  13. package/dist/actions/csrfToken/csrfToken.cjs +34 -103
  14. package/dist/actions/csrfToken/csrfToken.js +6 -6
  15. package/dist/actions/index.cjs +234 -391
  16. package/dist/actions/index.d.ts +3 -4
  17. package/dist/actions/index.js +16 -17
  18. package/dist/actions/session/session.cjs +25 -109
  19. package/dist/actions/session/session.js +4 -5
  20. package/dist/actions/signIn/authorization.cjs +64 -55
  21. package/dist/actions/signIn/authorization.d.ts +3 -4
  22. package/dist/actions/signIn/authorization.js +5 -5
  23. package/dist/actions/signIn/signIn.cjs +84 -206
  24. package/dist/actions/signIn/signIn.d.ts +3 -4
  25. package/dist/actions/signIn/signIn.js +7 -9
  26. package/dist/actions/signOut/signOut.cjs +88 -234
  27. package/dist/actions/signOut/signOut.js +8 -9
  28. package/dist/assert.cjs +5 -0
  29. package/dist/assert.d.ts +9 -1
  30. package/dist/assert.js +3 -1
  31. package/dist/chunk-2RXNXMCZ.js +55 -0
  32. package/dist/{chunk-UJJ7R56J.js → chunk-4V4JNXVF.js} +13 -10
  33. package/dist/chunk-6R2YZ4AC.js +22 -0
  34. package/dist/{chunk-VFTYH33W.js → chunk-7H3OR6UU.js} +29 -9
  35. package/dist/{chunk-256KIVJL.js → chunk-CXLATHS5.js} +53 -9
  36. package/dist/{chunk-6SM22VVJ.js → chunk-EIL2FPSS.js} +5 -1
  37. package/dist/chunk-IMICRJ5U.js +197 -0
  38. package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
  39. package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
  40. package/dist/chunk-NEVKX6K2.js +70 -0
  41. package/dist/{chunk-XXJKNKGQ.js → chunk-PTJUYB33.js} +9 -13
  42. package/dist/chunk-QDO2KSRJ.js +35 -0
  43. package/dist/{chunk-CAKJT3KS.js → chunk-QEZL7EYN.js} +21 -17
  44. package/dist/chunk-RRLIF4PQ.js +55 -0
  45. package/dist/chunk-TLE4PXY3.js +39 -0
  46. package/dist/chunk-UEH3LVON.js +97 -0
  47. package/dist/{chunk-HMRKN75I.js → chunk-WD7AUHQ5.js} +12 -7
  48. package/dist/{chunk-RLT4RFKV.js → chunk-ZLR3LI6X.js} +19 -9
  49. package/dist/cookie.cjs +140 -99
  50. package/dist/cookie.d.ts +33 -43
  51. package/dist/cookie.js +10 -17
  52. package/dist/errors.cjs +85 -0
  53. package/dist/errors.d.ts +48 -0
  54. package/dist/errors.js +18 -0
  55. package/dist/{index-DpfbvTZ_.d.ts → index-EqsoyjrF.d.ts} +139 -57
  56. package/dist/index.cjs +427 -389
  57. package/dist/index.d.ts +4 -5
  58. package/dist/index.js +37 -26
  59. package/dist/jose.cjs +23 -12
  60. package/dist/jose.d.ts +4 -1
  61. package/dist/jose.js +5 -4
  62. package/dist/oauth/bitbucket.d.ts +3 -4
  63. package/dist/oauth/discord.cjs +0 -1
  64. package/dist/oauth/discord.d.ts +3 -4
  65. package/dist/oauth/discord.js +1 -1
  66. package/dist/oauth/figma.d.ts +3 -4
  67. package/dist/oauth/github.d.ts +3 -4
  68. package/dist/oauth/gitlab.d.ts +3 -4
  69. package/dist/oauth/index.cjs +132 -6
  70. package/dist/oauth/index.d.ts +3 -4
  71. package/dist/oauth/index.js +12 -5
  72. package/dist/oauth/spotify.d.ts +3 -4
  73. package/dist/{response.cjs → oauth/strava.cjs} +21 -9
  74. package/dist/oauth/strava.d.ts +6 -0
  75. package/dist/oauth/strava.js +6 -0
  76. package/dist/oauth/x.d.ts +3 -4
  77. package/dist/schemas.cjs +11 -5
  78. package/dist/schemas.d.ts +70 -67
  79. package/dist/schemas.js +3 -1
  80. package/dist/secure.cjs +27 -19
  81. package/dist/secure.d.ts +3 -4
  82. package/dist/secure.js +4 -3
  83. package/dist/utils.cjs +90 -15
  84. package/dist/utils.d.ts +11 -2
  85. package/dist/utils.js +8 -4
  86. package/package.json +5 -6
  87. package/dist/chunk-FJUDBLCP.js +0 -59
  88. package/dist/chunk-HGJ4TXY4.js +0 -137
  89. package/dist/chunk-JAPMIE6S.js +0 -10
  90. package/dist/chunk-LLR722CL.js +0 -96
  91. package/dist/chunk-SJPDVKUS.js +0 -112
  92. package/dist/chunk-SMQO5WD7.js +0 -30
  93. package/dist/chunk-UTDLUEEG.js +0 -31
  94. package/dist/chunk-ZV4BH47P.js +0 -154
  95. package/dist/error.cjs +0 -88
  96. package/dist/error.d.ts +0 -62
  97. package/dist/error.js +0 -16
  98. package/dist/response.d.ts +0 -10
  99. 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/error.ts
43
- var AuthError = class extends Error {
44
- constructor(type, message) {
45
- super(message);
46
- this.type = type;
47
- this.name = "AuthError";
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 InvalidCsrfTokenError = class extends AuthError {
51
- constructor(message = "The provided CSRF token is invalid or has expired") {
52
- super("invalid_csrf_token", message);
53
- this.name = "InvalidCsrfTokenError";
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 InvalidRedirectToError = class extends AuthError {
57
- constructor(message = "The redirectTo parameter does not match the hosted origin.") {
58
- super("invalid_redirect_to", message);
59
- this.name = "InvalidRedirectToError";
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 isAuthError = (error) => {
63
- return error instanceof AuthError;
75
+ var isNativeError = (error) => {
76
+ return error instanceof Error;
64
77
  };
65
- var throwAuthError = (error, message) => {
66
- if (error instanceof Error) {
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 ERROR_RESPONSE = {
74
- AUTHORIZATION: {
75
- INVALID_REQUEST: "invalid_request",
76
- UNAUTHORIZED_CLIENT: "unauthorized_client",
77
- ACCESS_DENIED: "access_denied",
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 = (url2) => {
105
+ var sanitizeURL = (url) => {
111
106
  try {
112
- let decodedURL = decodeURIComponent(url2).trim();
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 url2.trim();
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({ error: "invalid_request", error_description: message }, { status, statusText });
149
+ return Response.json({ type: "ROUTER_ERROR", code: "ROUTER_INTERNAL_ERROR", message }, { status, statusText });
155
150
  }
156
- if (isAuthError(error)) {
157
- const { type, message } = error;
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
- return Response.json({ error: "server_error", error_description: "An unexpected error occurred" }, { status: 500 });
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 url2 = new URL(path);
165
- url2.hash = "";
166
- url2.search = "";
167
- return `${url2.origin}${url2.pathname}`;
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 { token: cookieToken } = await jose.verifyJWS(cookie);
209
- const { token: headerToken } = await jose.verifyJWS(header);
210
- const cookieBuffer = Buffer.from(cookieToken);
211
- const headerBuffer = Buffer.from(headerToken);
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 InvalidCsrfTokenError();
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 InvalidCsrfTokenError();
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 InvalidCsrfTokenError();
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
- secret ?? (secret = process.env.AURA_AUTH_SECRET);
282
+ const env = process.env;
283
+ secret ??= env.AURA_AUTH_SECRET ?? env.AUTH_SECRET;
230
284
  if (!secret) {
231
- throw new AuthError("JOSE_INIT_ERROR", "AURA_AUTH_SECRET environment variable is not set and no secret was provided.");
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 = process.env.AURA_AUTH_SALT ?? createDerivedSalt(secret);
234
- const { derivedKey: derivedSessionKey } = (0, import_jose.createDeriveKey)(secret, salt, "session");
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)(derivedSessionKey);
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
- prefix: "__Secure-"
322
+ httpOnly: true
281
323
  };
282
324
  var defaultHostCookieConfig = {
283
325
  secure: true,
284
- prefix: "__Host-",
326
+ httpOnly: true,
285
327
  path: "/",
286
328
  domain: void 0
287
329
  };
288
- var expiredCookieOptions = {
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 defineDefaultCookieOptions = (options2) => {
294
- return {
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
- if (optional) {
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 { name, prefix } = defineDefaultCookieOptions(options2);
318
- const parsedCookies = (0, import_cookie.parse)(cookies);
319
- const value = parsedCookies[`${prefix}${name}.${cookie}`];
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 (session, cookieOptions, jose) => {
355
+ var createSessionCookie = async (jose, session) => {
329
356
  try {
330
357
  const encoded = await jose.encodeJWT(session);
331
- return setCookie("sessionToken", encoded, cookieOptions);
358
+ return encoded;
332
359
  } catch (error) {
333
- throw new AuthError("server_error", "Failed to create session cookie", { cause: error });
360
+ throw new AuthInternalError("INVALID_JWT_TOKEN", "Failed to create session cookie", { cause: error });
334
361
  }
335
362
  };
336
- var secureCookieOptions = (request, cookieOptions, trustedProxyHeaders) => {
337
- const name = cookieOptions.name ?? COOKIE_NAME;
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 (cookieOptions.options?.domain === "*") {
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 (!isSecure) {
348
- const options2 = cookieOptions.options;
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 (options2?.sameSite == "none") {
355
- console.warn("[WARNING]: SameSite=None without a secure connection can be blocked by browsers.");
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
- ...cookieOptions.options,
363
- sameSite: options2?.sameSite === "none" ? "lax" : options2?.sameSite ?? "lax",
364
- ...defaultStandardCookieConfig,
365
- name
391
+ ...attributes,
392
+ ...defaultStandardCookieConfig
366
393
  };
367
394
  }
368
- return cookieOptions.strategy === "host" ? {
395
+ return strategy === "host" ? {
369
396
  ...defaultCookieOptions,
370
- ...cookieOptions.options,
371
- ...defaultHostCookieConfig,
372
- name
373
- } : { ...defaultCookieOptions, ...cookieOptions.options, ...defaultSecureCookieConfig, name };
374
- };
375
- var expireCookie = (name, options2) => {
376
- return setCookie(name, "", { ...options2, ...expiredCookieOptions });
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
- ...options2,
381
- secure: options2.secure,
382
- httpOnly: options2.httpOnly,
383
- maxAge: 5 * 60,
384
- expires: new Date(Date.now() + 5 * 60 * 1e3)
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/index.ts
522
- var builtInOAuthProviders = {
523
- github,
524
- bitbucket,
525
- figma,
526
- discord,
527
- gitlab,
528
- spotify,
529
- x
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
- ...oauthConfig,
544
- ...definition
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.url)(),
571
- accessToken: (0, import_v4.url)(),
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.url)(),
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
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.SERVER_ERROR, "Invalid OAuth configuration");
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 url2 = getOriginURL(request, trustedProxyHeaders);
654
- return `${url2.origin}${basePath}/callback/${oauth}`;
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 InvalidRedirectToError();
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 AuthError(
676
- ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST,
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 AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
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 (isAuthError(error)) {
805
+ if (isAuthSecurityError(error)) {
692
806
  throw error;
693
807
  }
694
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
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
- try {
720
- const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
721
- const state = generateSecure();
722
- const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
723
- const stateCookie = setCookie("state", state, oauthCookie(cookieOptions));
724
- const redirectURICookie = setCookie("redirect_uri", redirectURI, oauthCookie(cookieOptions));
725
- const redirectToCookie = setCookie(
726
- "redirect_to",
727
- createRedirectTo(request, redirectTo, trustedProxyHeaders),
728
- oauthCookie(cookieOptions)
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
- return AuraResponse.json(
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 AuthError(data.error, data?.error_description ?? "An error occurred while fetching user information.");
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
- throw throwAuthError(error, "Failed to retrieve userinfo");
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
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST, "Invalid OAuth configuration");
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 AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_GRANT, "Invalid access token response format");
933
+ throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
839
934
  }
840
- throw new AuthError(data.error, data?.error_description ?? "Failed to retrieve access token");
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 throwAuthError(error, "Failed to create access token");
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 AuthError(error, error_description ?? "OAuth Authorization Error");
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, trustedProxyHeaders }
973
+ context: { oauth: providers, cookies, jose }
879
974
  } = ctx;
880
- try {
881
- const oauthConfig = providers[oauth2];
882
- const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
883
- const cookieState = getCookie(request, "state", cookieOptions);
884
- const cookieRedirectTo = getCookie(request, "redirect_to", cookieOptions);
885
- const cookieRedirectURI = getCookie(request, "redirect_uri", cookieOptions);
886
- const codeVerifier = getCookie(request, "code_verifier", cookieOptions);
887
- if (!equals(cookieState, state)) {
888
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST, "Mismatching state");
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
- headers.set("Set-Cookie", sessionCookie);
916
- headers.append("Set-Cookie", expireCookie("state", cookieOptions));
917
- headers.append("Set-Cookie", expireCookie("redirect_uri", cookieOptions));
918
- headers.append("Set-Cookie", expireCookie("redirect_to", cookieOptions));
919
- headers.append("Set-Cookie", expireCookie("code_verifier", cookieOptions));
920
- headers.append("Set-Cookie", csrfCookie);
921
- return Response.json({ oauth: oauth2 }, { status: 302, headers });
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: { cookies, jose, trustedProxyHeaders }
1009
+ context: { jose, cookies }
949
1010
  } = ctx;
950
- const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
951
1011
  try {
952
- const session = getCookie(request, "sessionToken", cookieOptions);
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 Headers(cacheControl);
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: { cookies, jose, trustedProxyHeaders }
1042
+ context: { jose, cookies }
985
1043
  } = ctx;
986
- try {
987
- const cookiesOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
988
- const session = getCookie(request, "sessionToken", cookiesOptions);
989
- const csrfToken = getCookie(request, "csrfToken", {
990
- ...cookiesOptions,
991
- prefix: cookiesOptions.secure ? "__Host-" : ""
992
- });
993
- const header = headers.get("X-CSRF-Token");
994
- if (!header || !session || !csrfToken) {
995
- throw new Error("Missing CSRF token or session token");
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: { cookies, jose, trustedProxyHeaders }
1083
+ context: { jose, cookies }
1055
1084
  } = ctx;
1056
- const cookieOptions = secureCookieOptions(request, { ...cookies, strategy: "host" }, trustedProxyHeaders);
1057
- const existingCSRFToken = getCookie(request, "csrfToken", cookieOptions, true);
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.set("Set-Cookie", setCookie("csrfToken", csrfToken, cookieOptions));
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 ?? defaultCookieConfig,
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: !!authConfig?.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) => {