@aura-stack/auth 0.1.0 → 0.2.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/@types/index.d.ts +5 -5
  2. package/dist/@types/router.d.d.ts +5 -5
  3. package/dist/@types/utility.d.ts +1 -5
  4. package/dist/actions/callback/access-token.cjs +95 -74
  5. package/dist/actions/callback/access-token.d.ts +7 -7
  6. package/dist/actions/callback/access-token.js +5 -3
  7. package/dist/actions/callback/callback.cjs +171 -249
  8. package/dist/actions/callback/callback.d.ts +6 -6
  9. package/dist/actions/callback/callback.js +10 -10
  10. package/dist/actions/callback/userinfo.cjs +81 -57
  11. package/dist/actions/callback/userinfo.d.ts +5 -5
  12. package/dist/actions/callback/userinfo.js +7 -5
  13. package/dist/actions/csrfToken/csrfToken.cjs +36 -105
  14. package/dist/actions/csrfToken/csrfToken.js +6 -6
  15. package/dist/actions/index.cjs +309 -444
  16. package/dist/actions/index.d.ts +5 -5
  17. package/dist/actions/index.js +19 -19
  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 +96 -87
  21. package/dist/actions/signIn/authorization.d.ts +5 -5
  22. package/dist/actions/signIn/authorization.js +5 -5
  23. package/dist/actions/signIn/signIn.cjs +133 -242
  24. package/dist/actions/signIn/signIn.d.ts +6 -6
  25. package/dist/actions/signIn/signIn.js +8 -9
  26. package/dist/actions/signOut/signOut.cjs +136 -282
  27. package/dist/actions/signOut/signOut.js +8 -9
  28. package/dist/assert.cjs +5 -0
  29. package/dist/assert.d.ts +10 -1
  30. package/dist/assert.js +3 -1
  31. package/dist/chunk-3EUWD5BB.js +63 -0
  32. package/dist/chunk-6R2YZ4AC.js +22 -0
  33. package/dist/chunk-A3N4PVAT.js +70 -0
  34. package/dist/chunk-B737EUJV.js +22 -0
  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-VFTYH33W.js → chunk-EMKJA2GJ.js} +36 -8
  38. package/dist/{chunk-UJJ7R56J.js → chunk-GA2SMTJO.js} +16 -10
  39. package/dist/chunk-HP34YGGJ.js +22 -0
  40. package/dist/chunk-HT4YLL7N.js +35 -0
  41. package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
  42. package/dist/{chunk-RLT4RFKV.js → chunk-IVET23KF.js} +21 -8
  43. package/dist/{chunk-XXJKNKGQ.js → chunk-JVFTCTTE.js} +9 -13
  44. package/dist/chunk-KSWLO5ZU.js +102 -0
  45. package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
  46. package/dist/{chunk-CAKJT3KS.js → chunk-N4SX7TZT.js} +21 -17
  47. package/dist/chunk-RRLIF4PQ.js +55 -0
  48. package/dist/chunk-TLE4PXY3.js +39 -0
  49. package/dist/chunk-W6LG7BFW.js +197 -0
  50. package/dist/{chunk-HMRKN75I.js → chunk-YRCB5FLE.js} +14 -9
  51. package/dist/chunk-ZNCZVF6U.js +14 -0
  52. package/dist/cookie.cjs +140 -99
  53. package/dist/cookie.d.ts +35 -44
  54. package/dist/cookie.js +10 -17
  55. package/dist/errors.cjs +85 -0
  56. package/dist/errors.d.ts +49 -0
  57. package/dist/errors.js +18 -0
  58. package/dist/{index-DpfbvTZ_.d.ts → index-DkaLJFn8.d.ts} +192 -61
  59. package/dist/index.cjs +543 -443
  60. package/dist/index.d.ts +6 -6
  61. package/dist/index.js +42 -28
  62. package/dist/jose.cjs +25 -14
  63. package/dist/jose.d.ts +4 -1
  64. package/dist/jose.js +5 -4
  65. package/dist/oauth/bitbucket.d.ts +5 -5
  66. package/dist/oauth/discord.cjs +0 -1
  67. package/dist/oauth/discord.d.ts +5 -5
  68. package/dist/oauth/discord.js +1 -1
  69. package/dist/oauth/figma.d.ts +5 -5
  70. package/dist/oauth/github.d.ts +5 -5
  71. package/dist/oauth/gitlab.d.ts +5 -5
  72. package/dist/oauth/index.cjs +176 -6
  73. package/dist/oauth/index.d.ts +5 -5
  74. package/dist/oauth/index.js +19 -4
  75. package/dist/oauth/mailchimp.cjs +46 -0
  76. package/dist/oauth/mailchimp.d.ts +7 -0
  77. package/dist/oauth/mailchimp.js +6 -0
  78. package/dist/oauth/pinterest.cjs +46 -0
  79. package/dist/oauth/pinterest.d.ts +7 -0
  80. package/dist/oauth/pinterest.js +6 -0
  81. package/dist/oauth/spotify.d.ts +5 -5
  82. package/dist/oauth/strava.cjs +46 -0
  83. package/dist/oauth/strava.d.ts +7 -0
  84. package/dist/oauth/strava.js +6 -0
  85. package/dist/oauth/x.d.ts +5 -5
  86. package/dist/{response.cjs → request.cjs} +14 -10
  87. package/dist/request.d.ts +13 -0
  88. package/dist/request.js +6 -0
  89. package/dist/schemas.cjs +43 -37
  90. package/dist/schemas.d.ts +67 -64
  91. package/dist/schemas.js +3 -1
  92. package/dist/secure.cjs +32 -24
  93. package/dist/secure.d.ts +5 -5
  94. package/dist/secure.js +4 -3
  95. package/dist/utils.cjs +90 -15
  96. package/dist/utils.d.ts +11 -2
  97. package/dist/utils.js +8 -4
  98. package/package.json +8 -7
  99. package/dist/chunk-FJUDBLCP.js +0 -59
  100. package/dist/chunk-HGJ4TXY4.js +0 -137
  101. package/dist/chunk-JAPMIE6S.js +0 -10
  102. package/dist/chunk-LLR722CL.js +0 -96
  103. package/dist/chunk-SJPDVKUS.js +0 -112
  104. package/dist/chunk-SMQO5WD7.js +0 -30
  105. package/dist/chunk-UTDLUEEG.js +0 -31
  106. package/dist/chunk-ZV4BH47P.js +0 -154
  107. package/dist/error.cjs +0 -88
  108. package/dist/error.d.ts +0 -62
  109. package/dist/error.js +0 -16
  110. package/dist/response.d.ts +0 -10
  111. package/dist/response.js +0 -6
package/dist/index.cjs CHANGED
@@ -36,58 +36,60 @@ module.exports = __toCommonJS(index_exports);
36
36
  var import_config2 = require("dotenv/config");
37
37
  var import_router7 = require("@aura-stack/router");
38
38
 
39
+ // src/jose.ts
40
+ var import_config = require("dotenv/config");
41
+ var import_jose = require("@aura-stack/jose");
42
+
43
+ // src/secure.ts
44
+ var import_crypto = __toESM(require("crypto"), 1);
45
+
39
46
  // src/utils.ts
40
47
  var import_router = require("@aura-stack/router");
41
48
 
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";
49
+ // src/errors.ts
50
+ var OAuthProtocolError = class extends Error {
51
+ type = "OAUTH_PROTOCOL_ERROR";
52
+ error;
53
+ errorURI;
54
+ constructor(error, description, errorURI, options2) {
55
+ super(description, options2);
56
+ this.error = error;
57
+ this.errorURI = errorURI;
58
+ this.name = new.target.name;
59
+ Error.captureStackTrace(this, new.target);
48
60
  }
49
61
  };
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";
62
+ var AuthInternalError = class extends Error {
63
+ type = "AUTH_INTERNAL_ERROR";
64
+ code;
65
+ constructor(code, message, options2) {
66
+ super(message, options2);
67
+ this.code = code;
68
+ this.name = new.target.name;
69
+ Error.captureStackTrace(this, new.target);
54
70
  }
55
71
  };
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";
72
+ var AuthSecurityError = class extends Error {
73
+ type = "AUTH_SECURITY_ERROR";
74
+ code;
75
+ constructor(code, message, options2) {
76
+ super(message, options2);
77
+ this.code = code;
78
+ this.name = new.target.name;
79
+ Error.captureStackTrace(this, new.target);
60
80
  }
61
81
  };
62
- var isAuthError = (error) => {
63
- return error instanceof AuthError;
82
+ var isNativeError = (error) => {
83
+ return error instanceof Error;
64
84
  };
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
- }
85
+ var isOAuthProtocolError = (error) => {
86
+ return error instanceof OAuthProtocolError;
72
87
  };
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
- }
88
+ var isAuthInternalError = (error) => {
89
+ return error instanceof AuthInternalError;
90
+ };
91
+ var isAuthSecurityError = (error) => {
92
+ return error instanceof AuthSecurityError;
91
93
  };
92
94
 
93
95
  // src/utils.ts
@@ -107,9 +109,9 @@ var equals = (a, b) => {
107
109
  if (a === null || b === null || a === void 0 || b === void 0) return false;
108
110
  return a === b;
109
111
  };
110
- var sanitizeURL = (url2) => {
112
+ var sanitizeURL = (url) => {
111
113
  try {
112
- let decodedURL = decodeURIComponent(url2).trim();
114
+ let decodedURL = decodeURIComponent(url).trim();
113
115
  const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
114
116
  let protocol = "";
115
117
  let rest = decodedURL;
@@ -137,7 +139,7 @@ var sanitizeURL = (url2) => {
137
139
  }
138
140
  return sanitized;
139
141
  } catch {
140
- return url2.trim();
142
+ return url.trim();
141
143
  }
142
144
  };
143
145
  var isValidRelativePath = (path) => {
@@ -151,20 +153,42 @@ var isValidRelativePath = (path) => {
151
153
  var onErrorHandler = (error) => {
152
154
  if ((0, import_router.isRouterError)(error)) {
153
155
  const { message, status, statusText } = error;
154
- return Response.json({ error: "invalid_request", error_description: message }, { status, statusText });
156
+ return Response.json({ type: "ROUTER_ERROR", code: "ROUTER_INTERNAL_ERROR", message }, { status, statusText });
155
157
  }
156
- if (isAuthError(error)) {
157
- const { type, message } = error;
158
- return Response.json({ error: type, error_description: message }, { status: 400 });
158
+ if ((0, import_router.isInvalidZodSchemaError)(error)) {
159
+ return Response.json({ type: "ROUTER_ERROR", code: "INVALID_REQUEST", message: error.errors }, { status: 422 });
160
+ }
161
+ if (isOAuthProtocolError(error)) {
162
+ const { error: errorCode, message, type, errorURI } = error;
163
+ return Response.json(
164
+ {
165
+ type,
166
+ error: errorCode,
167
+ error_description: message,
168
+ error_uri: errorURI
169
+ },
170
+ { status: 400 }
171
+ );
159
172
  }
160
- return Response.json({ error: "server_error", error_description: "An unexpected error occurred" }, { status: 500 });
173
+ if (isAuthInternalError(error) || isAuthSecurityError(error)) {
174
+ const { type, code, message } = error;
175
+ return Response.json(
176
+ {
177
+ type,
178
+ code,
179
+ message
180
+ },
181
+ { status: 400 }
182
+ );
183
+ }
184
+ return Response.json({ type: "SERVER_ERROR", code: "server_error", message: "An unexpected error occurred" }, { status: 500 });
161
185
  };
162
186
  var getNormalizedOriginPath = (path) => {
163
187
  try {
164
- const url2 = new URL(path);
165
- url2.hash = "";
166
- url2.search = "";
167
- return `${url2.origin}${url2.pathname}`;
188
+ const url = new URL(path);
189
+ url.hash = "";
190
+ url.search = "";
191
+ return `${url.origin}${url.pathname}`;
168
192
  } catch {
169
193
  return sanitizeURL(path);
170
194
  }
@@ -172,18 +196,41 @@ var getNormalizedOriginPath = (path) => {
172
196
  var toISOString = (date) => {
173
197
  return new Date(date).toISOString();
174
198
  };
199
+ var useSecureCookies = (request, trustedProxyHeaders) => {
200
+ 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://");
201
+ };
202
+ var formatZodError = (error) => {
203
+ if (!error.issues || error.issues.length === 0) {
204
+ return {};
205
+ }
206
+ return error.issues.reduce((previous, issue) => {
207
+ const key = issue.path.join(".");
208
+ return {
209
+ ...previous,
210
+ [key]: {
211
+ code: issue.code,
212
+ message: issue.message
213
+ }
214
+ };
215
+ }, {});
216
+ };
175
217
 
176
- // src/jose.ts
177
- var import_config = require("dotenv/config");
178
- var import_jose = require("@aura-stack/jose");
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
+ };
179
227
 
180
228
  // src/secure.ts
181
- var import_node_crypto = __toESM(require("crypto"), 1);
182
229
  var generateSecure = (length = 32) => {
183
- return import_node_crypto.default.randomBytes(length).toString("base64url");
230
+ return import_crypto.default.randomBytes(length).toString("base64url");
184
231
  };
185
232
  var createHash = (data, base = "hex") => {
186
- return import_node_crypto.default.createHash("sha256").update(data).digest().toString(base);
233
+ return import_crypto.default.createHash("sha256").update(data).digest().toString(base);
187
234
  };
188
235
  var createPKCE = async (verifier) => {
189
236
  const codeVerifier = verifier ?? generateSecure(86);
@@ -205,59 +252,60 @@ 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
- if (!import_node_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
216
- throw new InvalidCsrfTokenError();
268
+ if (!import_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
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) => {
224
- return import_node_crypto.default.createHash("sha256").update(secret).update("aura-auth-salt").digest("hex");
277
+ return import_crypto.default.createHash("sha256").update(secret).update("aura-auth-salt").digest("hex");
225
278
  };
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 ?? "session_token"}`,
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 ?? "csrf_token"}`,
430
+ attributes: defineSecureCookieOptions(
431
+ useSecure,
432
+ {
433
+ ...overrides?.csrfToken?.attributes,
434
+ ...defaultHostCookieConfig
435
+ },
436
+ overrides?.csrfToken?.attributes?.strategy ?? "host"
437
+ )
438
+ },
439
+ redirectTo: {
440
+ name: `${securePrefix}${prefix}.${overrides?.redirectTo?.name ?? "redirect_to"}`,
441
+ attributes: defineSecureCookieOptions(
442
+ useSecure,
443
+ {
444
+ ...oauthCookieOptions,
445
+ ...overrides?.redirectTo?.attributes
446
+ },
447
+ overrides?.redirectTo?.attributes?.strategy ?? "secure"
448
+ )
449
+ },
450
+ redirectURI: {
451
+ name: `${securePrefix}${prefix}.${overrides?.redirectURI?.name ?? "redirect_uri"}`,
452
+ attributes: defineSecureCookieOptions(
453
+ useSecure,
454
+ {
455
+ ...oauthCookieOptions,
456
+ ...overrides?.redirectURI?.attributes
457
+ },
458
+ overrides?.redirectURI?.attributes?.strategy ?? "secure"
459
+ )
460
+ },
461
+ codeVerifier: {
462
+ name: `${securePrefix}${prefix}.${overrides?.codeVerifier?.name ?? "code_verifier"}`,
463
+ attributes: defineSecureCookieOptions(
464
+ useSecure,
465
+ {
466
+ ...oauthCookieOptions,
467
+ ...overrides?.codeVerifier?.attributes
468
+ },
469
+ overrides?.codeVerifier?.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,75 +604,86 @@ 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
624
  };
555
625
 
556
- // src/actions/signIn/signIn.ts
557
- var import_zod = __toESM(require("zod"), 1);
558
- var import_router2 = require("@aura-stack/router");
626
+ // src/oauth/mailchimp.ts
627
+ var mailchimp = {
628
+ id: "mailchimp",
629
+ name: "Mailchimp",
630
+ authorizeURL: "https://login.mailchimp.com/oauth2/authorize",
631
+ accessToken: "https://login.mailchimp.com/oauth2/token",
632
+ userInfo: "https://login.mailchimp.com/oauth2/metadata",
633
+ scope: "",
634
+ responseType: "code",
635
+ profile(profile) {
636
+ return {
637
+ sub: profile.user_id,
638
+ name: profile.accountname,
639
+ email: profile.login.login_email,
640
+ image: null
641
+ };
642
+ }
643
+ };
559
644
 
560
- // src/response.ts
561
- var AuraResponse = class extends Response {
562
- static json(body, init) {
563
- return Response.json(body, init);
645
+ // src/oauth/pinterest.ts
646
+ var pinterest = {
647
+ id: "pinterest",
648
+ name: "Pinterest",
649
+ authorizeURL: "https://api.pinterest.com/oauth/",
650
+ accessToken: "https://api.pinterest.com/v5/oauth/token",
651
+ userInfo: "https://api.pinterest.com/v5/user_account",
652
+ scope: "user_accounts:read",
653
+ responseType: "code",
654
+ profile(profile) {
655
+ return {
656
+ sub: profile.id,
657
+ name: profile.username,
658
+ email: null,
659
+ image: profile.profile_image
660
+ };
564
661
  }
565
662
  };
566
663
 
567
664
  // src/schemas.ts
568
- var import_v4 = require("zod/v4");
569
- var OAuthProviderConfigSchema = (0, import_v4.object)({
570
- authorizeURL: (0, import_v4.url)(),
571
- accessToken: (0, import_v4.url)(),
572
- scope: (0, import_v4.string)().optional(),
573
- userInfo: (0, import_v4.url)(),
574
- responseType: (0, import_v4.enum)(["code", "token", "id_token"]),
575
- clientId: (0, import_v4.string)(),
576
- clientSecret: (0, import_v4.string)()
665
+ var import_zod = require("zod");
666
+ var OAuthProviderConfigSchema = (0, import_zod.object)({
667
+ authorizeURL: (0, import_zod.string)().url(),
668
+ accessToken: (0, import_zod.string)().url(),
669
+ scope: (0, import_zod.string)().optional(),
670
+ userInfo: (0, import_zod.string)().url(),
671
+ responseType: (0, import_zod.enum)(["code", "token", "id_token"]),
672
+ clientId: (0, import_zod.string)(),
673
+ clientSecret: (0, import_zod.string)()
577
674
  });
578
675
  var OAuthAuthorization = OAuthProviderConfigSchema.extend({
579
- redirectURI: (0, import_v4.string)(),
580
- state: (0, import_v4.string)(),
581
- codeChallenge: (0, import_v4.string)(),
582
- codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
676
+ redirectURI: (0, import_zod.string)(),
677
+ state: (0, import_zod.string)(),
678
+ codeChallenge: (0, import_zod.string)(),
679
+ codeChallengeMethod: (0, import_zod.enum)(["plain", "S256"])
583
680
  });
584
- var OAuthAuthorizationResponse = (0, import_v4.object)({
585
- state: (0, import_v4.string)(),
586
- code: (0, import_v4.string)()
681
+ var OAuthAuthorizationResponse = (0, import_zod.object)({
682
+ state: (0, import_zod.string)({ message: "Missing state parameter in the OAuth authorization response." }),
683
+ code: (0, import_zod.string)({ message: "Missing code parameter in the OAuth authorization response." })
587
684
  });
588
- var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
589
- error: (0, import_v4.enum)([
685
+ var OAuthAuthorizationErrorResponse = (0, import_zod.object)({
686
+ error: (0, import_zod.enum)([
590
687
  "invalid_request",
591
688
  "unauthorized_client",
592
689
  "access_denied",
@@ -595,24 +692,24 @@ var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
595
692
  "server_error",
596
693
  "temporarily_unavailable"
597
694
  ]),
598
- error_description: (0, import_v4.string)().optional(),
599
- error_uri: (0, import_v4.string)().optional(),
600
- state: (0, import_v4.string)()
695
+ error_description: (0, import_zod.string)().optional(),
696
+ error_uri: (0, import_zod.string)().optional(),
697
+ state: (0, import_zod.string)()
601
698
  });
602
699
  var OAuthAccessToken = OAuthProviderConfigSchema.extend({
603
- redirectURI: (0, import_v4.string)(),
604
- code: (0, import_v4.string)(),
605
- codeVerifier: (0, import_v4.string)().min(43).max(128)
700
+ redirectURI: (0, import_zod.string)(),
701
+ code: (0, import_zod.string)(),
702
+ codeVerifier: (0, import_zod.string)().min(43).max(128)
606
703
  });
607
- var OAuthAccessTokenResponse = (0, import_v4.object)({
608
- access_token: (0, import_v4.string)(),
609
- token_type: (0, import_v4.string)(),
610
- expires_in: (0, import_v4.number)().optional(),
611
- refresh_token: (0, import_v4.string)().optional(),
612
- scope: (0, import_v4.string)().optional()
704
+ var OAuthAccessTokenResponse = (0, import_zod.object)({
705
+ access_token: (0, import_zod.string)(),
706
+ token_type: (0, import_zod.string)().optional(),
707
+ expires_in: (0, import_zod.number)().optional(),
708
+ refresh_token: (0, import_zod.string)().optional(),
709
+ scope: (0, import_zod.string)().optional().or((0, import_zod.null)())
613
710
  });
614
- var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
615
- error: (0, import_v4.enum)([
711
+ var OAuthAccessTokenErrorResponse = (0, import_zod.object)({
712
+ error: (0, import_zod.enum)([
616
713
  "invalid_request",
617
714
  "invalid_client",
618
715
  "invalid_grant",
@@ -620,19 +717,81 @@ var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
620
717
  "unsupported_grant_type",
621
718
  "invalid_scope"
622
719
  ]),
623
- error_description: (0, import_v4.string)().optional(),
624
- error_uri: (0, import_v4.string)().optional()
720
+ error_description: (0, import_zod.string)().optional(),
721
+ error_uri: (0, import_zod.string)().optional()
625
722
  });
626
- var OAuthErrorResponse = (0, import_v4.object)({
627
- error: (0, import_v4.string)(),
628
- error_description: (0, import_v4.string)().optional()
723
+ var OAuthErrorResponse = (0, import_zod.object)({
724
+ error: (0, import_zod.string)(),
725
+ error_description: (0, import_zod.string)().optional()
629
726
  });
727
+ var OAuthEnvSchema = (0, import_zod.object)({
728
+ clientId: import_zod.z.string().min(1, "OAuth Client ID is required in the environment variables."),
729
+ clientSecret: import_zod.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
730
+ });
731
+
732
+ // src/oauth/index.ts
733
+ var builtInOAuthProviders = {
734
+ github,
735
+ bitbucket,
736
+ figma,
737
+ discord,
738
+ gitlab,
739
+ spotify,
740
+ x,
741
+ strava,
742
+ mailchimp,
743
+ pinterest
744
+ };
745
+ var defineOAuthEnvironment = (oauth) => {
746
+ const env = process.env;
747
+ const clientIdSuffix = `${oauth.toUpperCase()}_CLIENT_ID`;
748
+ const clientSecretSuffix = `${oauth.toUpperCase()}_CLIENT_SECRET`;
749
+ const loadEnvs = OAuthEnvSchema.safeParse({
750
+ clientId: env[`AURA_AUTH_${clientIdSuffix}`] ?? env[`AUTH_${clientIdSuffix}`] ?? env[`${clientIdSuffix}`],
751
+ clientSecret: env[`AURA_AUTH_${clientSecretSuffix}`] ?? env[`AUTH_${clientSecretSuffix}`] ?? env[`${clientSecretSuffix}`]
752
+ });
753
+ if (!loadEnvs.success) {
754
+ const msg = JSON.stringify(formatZodError(loadEnvs.error), null, 2);
755
+ throw new AuthInternalError("INVALID_ENVIRONMENT_CONFIGURATION", msg);
756
+ }
757
+ return loadEnvs.data;
758
+ };
759
+ var defineOAuthProviderConfig = (config2) => {
760
+ if (typeof config2 === "string") {
761
+ const definition = defineOAuthEnvironment(config2);
762
+ const oauthConfig = builtInOAuthProviders[config2];
763
+ return {
764
+ ...oauthConfig,
765
+ ...definition
766
+ };
767
+ }
768
+ return config2;
769
+ };
770
+ var createBuiltInOAuthProviders = (oauth = []) => {
771
+ return oauth.reduce((previous, config2) => {
772
+ const oauthConfig = defineOAuthProviderConfig(config2);
773
+ return { ...previous, [oauthConfig.id]: oauthConfig };
774
+ }, {});
775
+ };
776
+
777
+ // src/actions/signIn/signIn.ts
778
+ var import_zod2 = require("zod");
779
+ var import_router2 = require("@aura-stack/router");
780
+
781
+ // src/headers.ts
782
+ var cacheControl = {
783
+ "Cache-Control": "no-store",
784
+ Pragma: "no-cache",
785
+ Expires: "0",
786
+ Vary: "Cookie"
787
+ };
630
788
 
631
789
  // src/actions/signIn/authorization.ts
632
790
  var createAuthorizationURL = (oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod) => {
633
791
  const parsed = OAuthAuthorization.safeParse({ ...oauthConfig, redirectURI, state, codeChallenge, codeChallengeMethod });
634
792
  if (!parsed.success) {
635
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.SERVER_ERROR, "Invalid OAuth configuration");
793
+ const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
794
+ throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
636
795
  }
637
796
  const { authorizeURL, ...options2 } = parsed.data;
638
797
  const { userInfo, accessToken, clientSecret, ...required } = options2;
@@ -650,8 +809,8 @@ var getOriginURL = (request, trustedProxyHeaders) => {
650
809
  }
651
810
  };
652
811
  var createRedirectURI = (request, oauth, basePath, trustedProxyHeaders) => {
653
- const url2 = getOriginURL(request, trustedProxyHeaders);
654
- return `${url2.origin}${basePath}/callback/${oauth}`;
812
+ const url = getOriginURL(request, trustedProxyHeaders);
813
+ return `${url.origin}${basePath}/callback/${oauth}`;
655
814
  };
656
815
  var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
657
816
  try {
@@ -665,15 +824,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
665
824
  }
666
825
  const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
667
826
  if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
668
- throw new InvalidRedirectToError();
827
+ throw new AuthSecurityError(
828
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
829
+ "The redirectTo parameter does not match the hosted origin."
830
+ );
669
831
  }
670
832
  return sanitizeURL(redirectToURL.pathname);
671
833
  }
672
834
  if (referer) {
673
835
  const refererURL = new URL(sanitizeURL(referer));
674
836
  if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
675
- throw new AuthError(
676
- ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST,
837
+ throw new AuthSecurityError(
838
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
677
839
  "The referer of the request does not match the hosted origin."
678
840
  );
679
841
  }
@@ -682,16 +844,16 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
682
844
  if (origin) {
683
845
  const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
684
846
  if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
685
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
847
+ throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
686
848
  }
687
849
  return sanitizeURL(originURL.pathname);
688
850
  }
689
851
  return "/";
690
852
  } catch (error) {
691
- if (isAuthError(error)) {
853
+ if (isAuthSecurityError(error)) {
692
854
  throw error;
693
855
  }
694
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
856
+ throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
695
857
  }
696
858
  };
697
859
 
@@ -699,9 +861,14 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
699
861
  var signInConfig = (oauth) => {
700
862
  return (0, import_router2.createEndpointConfig)("/signIn/:oauth", {
701
863
  schemas: {
702
- params: import_zod.default.object({
703
- oauth: import_zod.default.enum(Object.keys(oauth)),
704
- redirectTo: import_zod.default.string().optional()
864
+ params: import_zod2.z.object({
865
+ oauth: import_zod2.z.enum(
866
+ Object.keys(oauth),
867
+ "The OAuth provider is not supported or invalid."
868
+ )
869
+ }),
870
+ searchParams: import_zod2.z.object({
871
+ redirectTo: import_zod2.z.string().optional()
705
872
  })
706
873
  }
707
874
  });
@@ -713,67 +880,41 @@ var signInAction = (oauth) => {
713
880
  async (ctx) => {
714
881
  const {
715
882
  request,
716
- params: { oauth: oauth2, redirectTo },
883
+ params: { oauth: oauth2 },
884
+ searchParams: { redirectTo },
717
885
  context: { oauth: providers, cookies, trustedProxyHeaders, basePath }
718
886
  } = 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
- );
887
+ const state = generateSecure();
888
+ const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
889
+ const redirectToValue = createRedirectTo(request, redirectTo, trustedProxyHeaders);
890
+ const { codeVerifier, codeChallenge, method } = await createPKCE();
891
+ const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
892
+ const headers = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", authorization).setCookie(cookies.state.name, state, cookies.state.attributes).setCookie(cookies.redirectURI.name, redirectURI, cookies.redirectURI.attributes).setCookie(cookies.redirectTo.name, redirectToValue, cookies.redirectTo.attributes).setCookie(cookies.codeVerifier.name, codeVerifier, cookies.codeVerifier.attributes).toHeaders();
893
+ return Response.json(
894
+ { oauth: oauth2 },
895
+ {
896
+ status: 302,
897
+ headers
753
898
  }
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
- }
899
+ );
762
900
  },
763
901
  signInConfig(oauth)
764
902
  );
765
903
  };
766
904
 
767
905
  // src/actions/callback/callback.ts
768
- var import_zod2 = __toESM(require("zod"), 1);
906
+ var import_zod3 = require("zod");
769
907
  var import_router3 = require("@aura-stack/router");
770
908
 
771
- // src/headers.ts
772
- var cacheControl = {
773
- "Cache-Control": "no-store",
774
- Pragma: "no-cache",
775
- Expires: "0",
776
- Vary: "Cookie"
909
+ // src/request.ts
910
+ var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
911
+ const controller = new AbortController();
912
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
913
+ const response = await fetch(url, {
914
+ ...options2,
915
+ signal: controller.signal
916
+ }).finally(() => clearTimeout(timeoutId));
917
+ return response;
777
918
  };
778
919
 
779
920
  // src/actions/callback/userinfo.ts
@@ -789,7 +930,7 @@ var getDefaultUserInfo = (profile) => {
789
930
  var getUserInfo = async (oauthConfig, accessToken) => {
790
931
  const userinfoEndpoint = oauthConfig.userInfo;
791
932
  try {
792
- const response = await fetch(userinfoEndpoint, {
933
+ const response = await fetchAsync(userinfoEndpoint, {
793
934
  method: "GET",
794
935
  headers: {
795
936
  Accept: "application/json",
@@ -799,11 +940,20 @@ var getUserInfo = async (oauthConfig, accessToken) => {
799
940
  const json = await response.json();
800
941
  const { success, data } = OAuthErrorResponse.safeParse(json);
801
942
  if (success) {
802
- throw new AuthError(data.error, data?.error_description ?? "An error occurred while fetching user information.");
943
+ throw new OAuthProtocolError(
944
+ data.error,
945
+ data?.error_description ?? "An error occurred while fetching user information."
946
+ );
803
947
  }
804
948
  return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
805
949
  } catch (error) {
806
- throw throwAuthError(error, "Failed to retrieve userinfo");
950
+ if (isOAuthProtocolError(error)) {
951
+ throw error;
952
+ }
953
+ if (isNativeError(error)) {
954
+ throw new OAuthProtocolError("invalid_request", error.message, "", { cause: error });
955
+ }
956
+ throw new OAuthProtocolError("invalid_request", "Failed to fetch user information.", "", { cause: error });
807
957
  }
808
958
  };
809
959
 
@@ -811,11 +961,12 @@ var getUserInfo = async (oauthConfig, accessToken) => {
811
961
  var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
812
962
  const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
813
963
  if (!parsed.success) {
814
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST, "Invalid OAuth configuration");
964
+ const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
965
+ throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
815
966
  }
816
967
  const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
817
968
  try {
818
- const response = await fetch(accessToken, {
969
+ const response = await fetchAsync(accessToken, {
819
970
  method: "POST",
820
971
  headers: {
821
972
  Accept: "application/json",
@@ -835,13 +986,13 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
835
986
  if (!token.success) {
836
987
  const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
837
988
  if (!success) {
838
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_GRANT, "Invalid access token response format");
989
+ throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
839
990
  }
840
- throw new AuthError(data.error, data?.error_description ?? "Failed to retrieve access token");
991
+ throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
841
992
  }
842
993
  return token.data;
843
994
  } catch (error) {
844
- throw throwAuthError(error, "Failed to create access token");
995
+ throw error;
845
996
  }
846
997
  };
847
998
 
@@ -849,9 +1000,15 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
849
1000
  var callbackConfig = (oauth) => {
850
1001
  return (0, import_router3.createEndpointConfig)("/callback/:oauth", {
851
1002
  schemas: {
852
- searchParams: OAuthAuthorizationResponse,
853
- params: import_zod2.default.object({
854
- oauth: import_zod2.default.enum(Object.keys(oauth))
1003
+ params: import_zod3.z.object({
1004
+ oauth: import_zod3.z.enum(
1005
+ Object.keys(oauth),
1006
+ "The OAuth provider is not supported or invalid."
1007
+ )
1008
+ }),
1009
+ searchParams: import_zod3.z.object({
1010
+ code: import_zod3.z.string("Missing code parameter in the OAuth authorization response."),
1011
+ state: import_zod3.z.string("Missing state parameter in the OAuth authorization response.")
855
1012
  })
856
1013
  },
857
1014
  middlewares: [
@@ -859,7 +1016,7 @@ var callbackConfig = (oauth) => {
859
1016
  const response = OAuthAuthorizationErrorResponse.safeParse(ctx.searchParams);
860
1017
  if (response.success) {
861
1018
  const { error, error_description } = response.data;
862
- throw new AuthError(error, error_description ?? "OAuth Authorization Error");
1019
+ throw new OAuthProtocolError(error, error_description ?? "OAuth Authorization Error");
863
1020
  }
864
1021
  return ctx;
865
1022
  }
@@ -875,66 +1032,32 @@ var callbackAction = (oauth) => {
875
1032
  request,
876
1033
  params: { oauth: oauth2 },
877
1034
  searchParams: { code, state },
878
- context: { oauth: providers, cookies, jose, trustedProxyHeaders }
1035
+ context: { oauth: providers, cookies, jose }
879
1036
  } = 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
- )
1037
+ const oauthConfig = providers[oauth2];
1038
+ const cookieState = getCookie(request, cookies.state.name);
1039
+ const cookieRedirectTo = getCookie(request, cookies.redirectTo.name);
1040
+ const cookieRedirectURI = getCookie(request, cookies.redirectURI.name);
1041
+ const codeVerifier = getCookie(request, cookies.codeVerifier.name);
1042
+ if (!equals(cookieState, state)) {
1043
+ throw new AuthSecurityError(
1044
+ "MISMATCHING_STATE",
1045
+ "The provided state passed in the OAuth response does not match the stored state."
914
1046
  );
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 }
1047
+ }
1048
+ const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
1049
+ const sanitized = sanitizeURL(cookieRedirectTo);
1050
+ if (!isValidRelativePath(sanitized)) {
1051
+ throw new AuthSecurityError(
1052
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
1053
+ "Invalid redirect path. Potential open redirect attack detected."
936
1054
  );
937
1055
  }
1056
+ const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
1057
+ const sessionCookie = await createSessionCookie(jose, userInfo);
1058
+ const csrfToken = await createCSRF(jose);
1059
+ const headers = new import_router3.HeadersBuilder(cacheControl).setHeader("Location", sanitized).setCookie(cookies.sessionToken.name, sessionCookie, cookies.sessionToken.attributes).setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes).setCookie(cookies.state.name, "", expiredCookieAttributes).setCookie(cookies.redirectURI.name, "", expiredCookieAttributes).setCookie(cookies.redirectTo.name, "", expiredCookieAttributes).setCookie(cookies.codeVerifier.name, "", expiredCookieAttributes).toHeaders();
1060
+ return Response.json({ oauth: oauth2 }, { status: 302, headers });
938
1061
  },
939
1062
  callbackConfig(oauth)
940
1063
  );
@@ -945,31 +1068,28 @@ var import_router4 = require("@aura-stack/router");
945
1068
  var sessionAction = (0, import_router4.createEndpoint)("GET", "/session", async (ctx) => {
946
1069
  const {
947
1070
  request,
948
- context: { cookies, jose, trustedProxyHeaders }
1071
+ context: { jose, cookies }
949
1072
  } = ctx;
950
- const cookieOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
951
1073
  try {
952
- const session = getCookie(request, "sessionToken", cookieOptions);
1074
+ const session = getCookie(request, cookies.sessionToken.name);
953
1075
  const decoded = await jose.decodeJWT(session);
954
1076
  const { exp, iat, jti, nbf, ...user } = decoded;
955
1077
  const headers = new Headers(cacheControl);
956
1078
  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);
1079
+ } catch (error) {
1080
+ const headers = new import_router4.HeadersBuilder(cacheControl).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
961
1081
  return Response.json({ authenticated: false, message: "Unauthorized" }, { status: 401, headers });
962
1082
  }
963
1083
  });
964
1084
 
965
1085
  // src/actions/signOut/signOut.ts
966
- var import_zod3 = __toESM(require("zod"), 1);
1086
+ var import_zod4 = require("zod");
967
1087
  var import_router5 = require("@aura-stack/router");
968
1088
  var config = (0, import_router5.createEndpointConfig)({
969
1089
  schemas: {
970
- searchParams: import_zod3.default.object({
971
- token_type_hint: import_zod3.default.literal("session_token"),
972
- redirectTo: import_zod3.default.string().optional()
1090
+ searchParams: import_zod4.z.object({
1091
+ token_type_hint: import_zod4.z.literal("session_token"),
1092
+ redirectTo: import_zod4.z.string().optional()
973
1093
  })
974
1094
  }
975
1095
  });
@@ -981,98 +1101,78 @@ var signOutAction = (0, import_router5.createEndpoint)(
981
1101
  request,
982
1102
  headers,
983
1103
  searchParams: { redirectTo },
984
- context: { cookies, jose, trustedProxyHeaders }
1104
+ context: { jose, cookies }
985
1105
  } = 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
- );
1106
+ const session = headers.getCookie(cookies.sessionToken.name);
1107
+ const csrfToken = headers.getCookie(cookies.csrfToken.name);
1108
+ const header = headers.getHeader("X-CSRF-Token");
1109
+ if (!session) {
1110
+ throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
1044
1111
  }
1112
+ if (!csrfToken) {
1113
+ throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
1114
+ }
1115
+ if (!header) {
1116
+ throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
1117
+ }
1118
+ await verifyCSRF(jose, csrfToken, header);
1119
+ await jose.decodeJWT(session);
1120
+ const normalizedOriginPath = getNormalizedOriginPath(request.url);
1121
+ const location = createRedirectTo(
1122
+ new Request(normalizedOriginPath, {
1123
+ headers: headers.toHeaders()
1124
+ }),
1125
+ redirectTo
1126
+ );
1127
+ const headersList = new import_router5.HeadersBuilder(cacheControl).setHeader("Location", location).setCookie(cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
1128
+ return Response.json({ message: "Signed out successfully" }, { status: import_router5.statusCode.ACCEPTED, headers: headersList });
1045
1129
  },
1046
1130
  config
1047
1131
  );
1048
1132
 
1049
1133
  // src/actions/csrfToken/csrfToken.ts
1050
1134
  var import_router6 = require("@aura-stack/router");
1135
+ var getCSRFToken = (request, cookieName) => {
1136
+ try {
1137
+ return getCookie(request, cookieName);
1138
+ } catch {
1139
+ return void 0;
1140
+ }
1141
+ };
1051
1142
  var csrfTokenAction = (0, import_router6.createEndpoint)("GET", "/csrfToken", async (ctx) => {
1052
1143
  const {
1053
1144
  request,
1054
- context: { cookies, jose, trustedProxyHeaders }
1145
+ context: { jose, cookies }
1055
1146
  } = 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);
1147
+ const token = getCSRFToken(request, cookies.csrfToken.name);
1148
+ const csrfToken = await createCSRF(jose, token);
1059
1149
  const headers = new Headers(cacheControl);
1060
- headers.set("Set-Cookie", setCookie("csrfToken", csrfToken, cookieOptions));
1150
+ headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
1061
1151
  return Response.json({ csrfToken }, { headers });
1062
1152
  });
1063
1153
 
1064
1154
  // src/index.ts
1065
1155
  var createInternalConfig = (authConfig) => {
1156
+ const useSecure = authConfig?.trustedProxyHeaders ?? false;
1066
1157
  return {
1067
1158
  basePath: authConfig?.basePath ?? "/auth",
1068
1159
  onError: onErrorHandler,
1069
1160
  context: {
1070
1161
  oauth: createBuiltInOAuthProviders(authConfig?.oauth),
1071
- cookies: authConfig?.cookies ?? defaultCookieConfig,
1162
+ cookies: createCookieStore(useSecure, authConfig?.cookies?.prefix, authConfig?.cookies?.overrides ?? {}),
1072
1163
  jose: createJoseInstance(authConfig?.secret),
1164
+ secret: authConfig?.secret,
1073
1165
  basePath: authConfig?.basePath ?? "/auth",
1074
- trustedProxyHeaders: !!authConfig?.trustedProxyHeaders
1075
- }
1166
+ trustedProxyHeaders: useSecure
1167
+ },
1168
+ middlewares: [
1169
+ (ctx) => {
1170
+ const useSecure2 = useSecureCookies(ctx.request, ctx.context.trustedProxyHeaders);
1171
+ const cookies = createCookieStore(useSecure2, authConfig?.cookies?.prefix, authConfig?.cookies?.overrides ?? {});
1172
+ ctx.context.cookies = cookies;
1173
+ return ctx;
1174
+ }
1175
+ ]
1076
1176
  };
1077
1177
  };
1078
1178
  var createAuth = (authConfig) => {