@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
@@ -42,47 +42,19 @@ var import_node_crypto = __toESM(require("crypto"), 1);
42
42
  // src/utils.ts
43
43
  var import_router = require("@aura-stack/router");
44
44
 
45
- // src/error.ts
46
- var AuthError = class extends Error {
47
- constructor(type, message) {
48
- super(message);
49
- this.type = type;
50
- this.name = "AuthError";
45
+ // src/errors.ts
46
+ var AuthSecurityError = class extends Error {
47
+ type = "AUTH_SECURITY_ERROR";
48
+ code;
49
+ constructor(code, message, options2) {
50
+ super(message, options2);
51
+ this.code = code;
52
+ this.name = new.target.name;
53
+ Error.captureStackTrace(this, new.target);
51
54
  }
52
55
  };
53
- var InvalidCsrfTokenError = class extends AuthError {
54
- constructor(message = "The provided CSRF token is invalid or has expired") {
55
- super("invalid_csrf_token", message);
56
- this.name = "InvalidCsrfTokenError";
57
- }
58
- };
59
- var InvalidRedirectToError = class extends AuthError {
60
- constructor(message = "The redirectTo parameter does not match the hosted origin.") {
61
- super("invalid_redirect_to", message);
62
- this.name = "InvalidRedirectToError";
63
- }
64
- };
65
- var isAuthError = (error) => {
66
- return error instanceof AuthError;
67
- };
68
- var ERROR_RESPONSE = {
69
- AUTHORIZATION: {
70
- INVALID_REQUEST: "invalid_request",
71
- UNAUTHORIZED_CLIENT: "unauthorized_client",
72
- ACCESS_DENIED: "access_denied",
73
- UNSUPPORTED_RESPONSE_TYPE: "unsupported_response_type",
74
- INVALID_SCOPE: "invalid_scope",
75
- SERVER_ERROR: "server_error",
76
- TEMPORARILY_UNAVAILABLE: "temporarily_unavailable"
77
- },
78
- ACCESS_TOKEN: {
79
- INVALID_REQUEST: "invalid_request",
80
- INVALID_CLIENT: "invalid_client",
81
- INVALID_GRANT: "invalid_grant",
82
- UNAUTHORIZED_CLIENT: "unauthorized_client",
83
- UNSUPPORTED_GRANT_TYPE: "unsupported_grant_type",
84
- INVALID_SCOPE: "invalid_scope"
85
- }
56
+ var isAuthSecurityError = (error) => {
57
+ return error instanceof AuthSecurityError;
86
58
  };
87
59
 
88
60
  // src/utils.ts
@@ -90,9 +62,9 @@ var equals = (a, b) => {
90
62
  if (a === null || b === null || a === void 0 || b === void 0) return false;
91
63
  return a === b;
92
64
  };
93
- var sanitizeURL = (url2) => {
65
+ var sanitizeURL = (url) => {
94
66
  try {
95
- let decodedURL = decodeURIComponent(url2).trim();
67
+ let decodedURL = decodeURIComponent(url).trim();
96
68
  const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
97
69
  let protocol = "";
98
70
  let rest = decodedURL;
@@ -120,36 +92,52 @@ var sanitizeURL = (url2) => {
120
92
  }
121
93
  return sanitized;
122
94
  } catch {
123
- return url2.trim();
95
+ return url.trim();
124
96
  }
125
97
  };
126
98
  var getNormalizedOriginPath = (path) => {
127
99
  try {
128
- const url2 = new URL(path);
129
- url2.hash = "";
130
- url2.search = "";
131
- return `${url2.origin}${url2.pathname}`;
100
+ const url = new URL(path);
101
+ url.hash = "";
102
+ url.search = "";
103
+ return `${url.origin}${url.pathname}`;
132
104
  } catch {
133
105
  return sanitizeURL(path);
134
106
  }
135
107
  };
136
108
 
109
+ // src/assert.ts
110
+ var isValidURL = (value) => {
111
+ if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
112
+ const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
113
+ return regex.test(value);
114
+ };
115
+ var isJWTPayloadWithToken = (payload) => {
116
+ return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
117
+ };
118
+
137
119
  // src/secure.ts
138
120
  var verifyCSRF = async (jose, cookie, header) => {
139
121
  try {
140
- const { token: cookieToken } = await jose.verifyJWS(cookie);
141
- const { token: headerToken } = await jose.verifyJWS(header);
142
- const cookieBuffer = Buffer.from(cookieToken);
143
- const headerBuffer = Buffer.from(headerToken);
122
+ const cookiePayload = await jose.verifyJWS(cookie);
123
+ const headerPayload = await jose.verifyJWS(header);
124
+ if (!isJWTPayloadWithToken(cookiePayload)) {
125
+ throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
126
+ }
127
+ if (!isJWTPayloadWithToken(headerPayload)) {
128
+ throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
129
+ }
130
+ const cookieBuffer = Buffer.from(cookiePayload.token);
131
+ const headerBuffer = Buffer.from(headerPayload.token);
144
132
  if (!equals(headerBuffer.length, cookieBuffer.length)) {
145
- throw new InvalidCsrfTokenError();
133
+ throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
146
134
  }
147
135
  if (!import_node_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
148
- throw new InvalidCsrfTokenError();
136
+ throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
149
137
  }
150
138
  return true;
151
139
  } catch {
152
- throw new InvalidCsrfTokenError();
140
+ throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
153
141
  }
154
142
  };
155
143
 
@@ -161,30 +149,13 @@ var cacheControl = {
161
149
  Vary: "Cookie"
162
150
  };
163
151
 
164
- // src/response.ts
165
- var AuraResponse = class extends Response {
166
- static json(body, init) {
167
- return Response.json(body, init);
168
- }
169
- };
170
-
171
- // src/assert.ts
172
- var isRequest = (value) => {
173
- return typeof Request !== "undefined" && value instanceof Request;
174
- };
175
- var isValidURL = (value) => {
176
- if (value.includes("\r\n") || value.includes("\n") || value.includes("\r")) return false;
177
- const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
178
- return regex.test(value);
179
- };
180
-
181
152
  // src/schemas.ts
182
153
  var import_v4 = require("zod/v4");
183
154
  var OAuthProviderConfigSchema = (0, import_v4.object)({
184
- authorizeURL: (0, import_v4.url)(),
185
- accessToken: (0, import_v4.url)(),
155
+ authorizeURL: (0, import_v4.httpUrl)(),
156
+ accessToken: (0, import_v4.httpUrl)(),
186
157
  scope: (0, import_v4.string)().optional(),
187
- userInfo: (0, import_v4.url)(),
158
+ userInfo: (0, import_v4.httpUrl)(),
188
159
  responseType: (0, import_v4.enum)(["code", "token", "id_token"]),
189
160
  clientId: (0, import_v4.string)(),
190
161
  clientSecret: (0, import_v4.string)()
@@ -196,8 +167,8 @@ var OAuthAuthorization = OAuthProviderConfigSchema.extend({
196
167
  codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
197
168
  });
198
169
  var OAuthAuthorizationResponse = (0, import_v4.object)({
199
- state: (0, import_v4.string)(),
200
- code: (0, import_v4.string)()
170
+ state: (0, import_v4.string)("Missing state parameter in the OAuth authorization response."),
171
+ code: (0, import_v4.string)("Missing code parameter in the OAuth authorization response.")
201
172
  });
202
173
  var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
203
174
  error: (0, import_v4.enum)([
@@ -241,6 +212,10 @@ var OAuthErrorResponse = (0, import_v4.object)({
241
212
  error: (0, import_v4.string)(),
242
213
  error_description: (0, import_v4.string)().optional()
243
214
  });
215
+ var OAuthEnvSchema = (0, import_v4.object)({
216
+ clientId: import_v4.z.string().min(1, "OAuth Client ID is required in the environment variables."),
217
+ clientSecret: import_v4.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
218
+ });
244
219
 
245
220
  // src/actions/signIn/authorization.ts
246
221
  var getOriginURL = (request, trustedProxyHeaders) => {
@@ -265,15 +240,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
265
240
  }
266
241
  const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
267
242
  if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
268
- throw new InvalidRedirectToError();
243
+ throw new AuthSecurityError(
244
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
245
+ "The redirectTo parameter does not match the hosted origin."
246
+ );
269
247
  }
270
248
  return sanitizeURL(redirectToURL.pathname);
271
249
  }
272
250
  if (referer) {
273
251
  const refererURL = new URL(sanitizeURL(referer));
274
252
  if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
275
- throw new AuthError(
276
- ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST,
253
+ throw new AuthSecurityError(
254
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
277
255
  "The referer of the request does not match the hosted origin."
278
256
  );
279
257
  }
@@ -282,126 +260,38 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
282
260
  if (origin) {
283
261
  const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
284
262
  if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
285
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
263
+ throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
286
264
  }
287
265
  return sanitizeURL(originURL.pathname);
288
266
  }
289
267
  return "/";
290
268
  } catch (error) {
291
- if (isAuthError(error)) {
269
+ if (isAuthSecurityError(error)) {
292
270
  throw error;
293
271
  }
294
- throw new AuthError(ERROR_RESPONSE.AUTHORIZATION.INVALID_REQUEST, "Invalid origin (potential CSRF).");
272
+ throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
295
273
  }
296
274
  };
297
275
 
298
276
  // src/cookie.ts
299
- var import_cookie = require("cookie");
300
- var import_cookie2 = require("cookie");
301
- var COOKIE_NAME = "aura-auth";
277
+ var import_cookie = require("@aura-stack/router/cookie");
302
278
  var defaultCookieOptions = {
303
279
  httpOnly: true,
304
280
  sameSite: "lax",
305
281
  path: "/",
306
282
  maxAge: 60 * 60 * 24 * 15
307
283
  };
308
- var defaultStandardCookieConfig = {
309
- secure: false,
284
+ var oauthCookieOptions = {
310
285
  httpOnly: true,
311
- prefix: ""
312
- };
313
- var defaultSecureCookieConfig = {
314
- secure: true,
315
- prefix: "__Secure-"
316
- };
317
- var defaultHostCookieConfig = {
318
- secure: true,
319
- prefix: "__Host-",
320
- path: "/",
321
- domain: void 0
286
+ maxAge: 5 * 60,
287
+ sameSite: "lax",
288
+ expires: new Date(Date.now() + 5 * 60 * 1e3)
322
289
  };
323
- var expiredCookieOptions = {
290
+ var expiredCookieAttributes = {
324
291
  ...defaultCookieOptions,
325
292
  expires: /* @__PURE__ */ new Date(0),
326
293
  maxAge: 0
327
294
  };
328
- var defineDefaultCookieOptions = (options2) => {
329
- return {
330
- name: options2?.name ?? COOKIE_NAME,
331
- prefix: options2?.prefix ?? (options2?.secure ? "__Secure-" : ""),
332
- ...defaultCookieOptions,
333
- ...options2
334
- };
335
- };
336
- var setCookie = (cookieName, value, options2) => {
337
- const { prefix, name } = defineDefaultCookieOptions(options2);
338
- const cookieNameWithPrefix = `${prefix}${name}.${cookieName}`;
339
- return (0, import_cookie.serialize)(cookieNameWithPrefix, value, {
340
- ...defaultCookieOptions,
341
- ...options2
342
- });
343
- };
344
- var getCookie = (petition, cookie, options2, optional = false) => {
345
- const cookies = isRequest(petition) ? petition.headers.get("Cookie") : petition.headers.getSetCookie().join("; ");
346
- if (!cookies) {
347
- if (optional) {
348
- return "";
349
- }
350
- throw new AuthError("invalid_request", "No cookies found. There is no active session");
351
- }
352
- const { name, prefix } = defineDefaultCookieOptions(options2);
353
- const parsedCookies = (0, import_cookie.parse)(cookies);
354
- const value = parsedCookies[`${prefix}${name}.${cookie}`];
355
- if (value === void 0) {
356
- if (optional) {
357
- return "";
358
- }
359
- throw new AuthError("invalid_request", `Cookie "${cookie}" not found. There is no active session`);
360
- }
361
- return value;
362
- };
363
- var secureCookieOptions = (request, cookieOptions, trustedProxyHeaders) => {
364
- const name = cookieOptions.name ?? COOKIE_NAME;
365
- 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://");
366
- if (!cookieOptions.options?.httpOnly) {
367
- console.warn(
368
- "[WARNING]: Cookie is configured without HttpOnly. This allows JavaScript access via document.cookie and increases XSS risk."
369
- );
370
- }
371
- if (cookieOptions.options?.domain === "*") {
372
- console.warn("[WARNING]: Cookie 'Domain' is set to '*', which is insecure. Avoid wildcard domains.");
373
- }
374
- if (!isSecure) {
375
- const options2 = cookieOptions.options;
376
- if (options2?.secure) {
377
- console.warn(
378
- "[WARNING]: The 'Secure' attribute will be disabled for this cookie. Serve over HTTPS to enforce Secure cookies."
379
- );
380
- }
381
- if (options2?.sameSite == "none") {
382
- console.warn("[WARNING]: SameSite=None without a secure connection can be blocked by browsers.");
383
- }
384
- if (process.env.NODE_ENV === "production") {
385
- console.warn("[WARNING]: In production, ensure cookies are served over HTTPS to maintain security.");
386
- }
387
- return {
388
- ...defaultCookieOptions,
389
- ...cookieOptions.options,
390
- sameSite: options2?.sameSite === "none" ? "lax" : options2?.sameSite ?? "lax",
391
- ...defaultStandardCookieConfig,
392
- name
393
- };
394
- }
395
- return cookieOptions.strategy === "host" ? {
396
- ...defaultCookieOptions,
397
- ...cookieOptions.options,
398
- ...defaultHostCookieConfig,
399
- name
400
- } : { ...defaultCookieOptions, ...cookieOptions.options, ...defaultSecureCookieConfig, name };
401
- };
402
- var expireCookie = (name, options2) => {
403
- return setCookie(name, "", { ...options2, ...expiredCookieOptions });
404
- };
405
295
 
406
296
  // src/actions/signOut/signOut.ts
407
297
  var config = (0, import_router2.createEndpointConfig)({
@@ -420,67 +310,31 @@ var signOutAction = (0, import_router2.createEndpoint)(
420
310
  request,
421
311
  headers,
422
312
  searchParams: { redirectTo },
423
- context: { cookies, jose, trustedProxyHeaders }
313
+ context: { jose, cookies }
424
314
  } = ctx;
425
- try {
426
- const cookiesOptions = secureCookieOptions(request, cookies, trustedProxyHeaders);
427
- const session = getCookie(request, "sessionToken", cookiesOptions);
428
- const csrfToken = getCookie(request, "csrfToken", {
429
- ...cookiesOptions,
430
- prefix: cookiesOptions.secure ? "__Host-" : ""
431
- });
432
- const header = headers.get("X-CSRF-Token");
433
- if (!header || !session || !csrfToken) {
434
- throw new Error("Missing CSRF token or session token");
435
- }
436
- await verifyCSRF(jose, csrfToken, header);
437
- await jose.decodeJWT(session);
438
- const normalizedOriginPath = getNormalizedOriginPath(request.url);
439
- const location = createRedirectTo(
440
- new Request(normalizedOriginPath, {
441
- headers
442
- }),
443
- redirectTo
444
- );
445
- const responseHeaders = new Headers(cacheControl);
446
- responseHeaders.append("Set-Cookie", expireCookie("sessionToken", cookiesOptions));
447
- responseHeaders.append(
448
- "Set-Cookie",
449
- expireCookie("csrfToken", { ...cookiesOptions, prefix: cookiesOptions.secure ? "__Host-" : "" })
450
- );
451
- responseHeaders.append("Location", location);
452
- return Response.json(
453
- { message: "Signed out successfully" },
454
- { status: import_router2.statusCode.ACCEPTED, headers: responseHeaders }
455
- );
456
- } catch (error) {
457
- if (error instanceof InvalidCsrfTokenError) {
458
- return AuraResponse.json(
459
- {
460
- error: "invalid_csrf_token",
461
- error_description: "The provided CSRF token is invalid or has expired"
462
- },
463
- { status: import_router2.statusCode.UNAUTHORIZED }
464
- );
465
- }
466
- if (error instanceof InvalidRedirectToError) {
467
- const { type, message } = error;
468
- return AuraResponse.json(
469
- {
470
- error: type,
471
- error_description: message
472
- },
473
- { status: import_router2.statusCode.BAD_REQUEST }
474
- );
475
- }
476
- return AuraResponse.json(
477
- {
478
- error: "invalid_session_token",
479
- error_description: "The provided sessionToken is invalid or has already expired"
480
- },
481
- { status: import_router2.statusCode.UNAUTHORIZED }
482
- );
315
+ const session = headers.getCookie(cookies.sessionToken.name);
316
+ const csrfToken = headers.getCookie(cookies.csrfToken.name);
317
+ const header = headers.getHeader("X-CSRF-Token");
318
+ if (!session) {
319
+ throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
483
320
  }
321
+ if (!csrfToken) {
322
+ throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
323
+ }
324
+ if (!header) {
325
+ throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
326
+ }
327
+ await verifyCSRF(jose, csrfToken, header);
328
+ await jose.decodeJWT(session);
329
+ const normalizedOriginPath = getNormalizedOriginPath(request.url);
330
+ const location = createRedirectTo(
331
+ new Request(normalizedOriginPath, {
332
+ headers: headers.toHeaders()
333
+ }),
334
+ redirectTo
335
+ );
336
+ const headersList = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", location).setCookie(cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
337
+ return Response.json({ message: "Signed out successfully" }, { status: import_router2.statusCode.ACCEPTED, headers: headersList });
484
338
  },
485
339
  config
486
340
  );
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  signOutAction
3
- } from "../../chunk-SJPDVKUS.js";
4
- import "../../chunk-CAKJT3KS.js";
5
- import "../../chunk-ZV4BH47P.js";
6
- import "../../chunk-6SM22VVJ.js";
3
+ } from "../../chunk-NEVKX6K2.js";
4
+ import "../../chunk-QEZL7EYN.js";
5
+ import "../../chunk-IMICRJ5U.js";
7
6
  import "../../chunk-STHEPPUZ.js";
8
- import "../../chunk-GZU3RBTB.js";
9
- import "../../chunk-256KIVJL.js";
10
- import "../../chunk-FJUDBLCP.js";
11
- import "../../chunk-JAPMIE6S.js";
12
- import "../../chunk-HMRKN75I.js";
7
+ import "../../chunk-WD7AUHQ5.js";
8
+ import "../../chunk-N2APGLXA.js";
9
+ import "../../chunk-CXLATHS5.js";
10
+ import "../../chunk-EIL2FPSS.js";
11
+ import "../../chunk-RRLIF4PQ.js";
13
12
  export {
14
13
  signOutAction
15
14
  };
package/dist/assert.cjs CHANGED
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var assert_exports = {};
22
22
  __export(assert_exports, {
23
23
  isFalsy: () => isFalsy,
24
+ isJWTPayloadWithToken: () => isJWTPayloadWithToken,
24
25
  isRequest: () => isRequest,
25
26
  isValidURL: () => isValidURL
26
27
  });
@@ -36,9 +37,13 @@ var isValidURL = (value) => {
36
37
  const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
37
38
  return regex.test(value);
38
39
  };
40
+ var isJWTPayloadWithToken = (payload) => {
41
+ return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
42
+ };
39
43
  // Annotate the CommonJS export names for ESM import in node:
40
44
  0 && (module.exports = {
41
45
  isFalsy,
46
+ isJWTPayloadWithToken,
42
47
  isRequest,
43
48
  isValidURL
44
49
  });
package/dist/assert.d.ts CHANGED
@@ -1,5 +1,13 @@
1
+ import { J as JWTPayloadWithToken } from './index-EqsoyjrF.js';
2
+ import 'zod/v4';
3
+ import './schemas.js';
4
+ import '@aura-stack/router/cookie';
5
+ import '@aura-stack/jose/jose';
6
+ import './@types/utility.js';
7
+
1
8
  declare const isFalsy: (value: unknown) => boolean;
2
9
  declare const isRequest: (value: unknown) => value is Request;
3
10
  declare const isValidURL: (value: string) => boolean;
11
+ declare const isJWTPayloadWithToken: (payload: unknown) => payload is JWTPayloadWithToken;
4
12
 
5
- export { isFalsy, isRequest, isValidURL };
13
+ export { isFalsy, isJWTPayloadWithToken, isRequest, isValidURL };
package/dist/assert.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import {
2
2
  isFalsy,
3
+ isJWTPayloadWithToken,
3
4
  isRequest,
4
5
  isValidURL
5
- } from "./chunk-6SM22VVJ.js";
6
+ } from "./chunk-EIL2FPSS.js";
6
7
  export {
7
8
  isFalsy,
9
+ isJWTPayloadWithToken,
8
10
  isRequest,
9
11
  isValidURL
10
12
  };
@@ -0,0 +1,55 @@
1
+ import {
2
+ createAuthorizationURL,
3
+ createRedirectTo,
4
+ createRedirectURI
5
+ } from "./chunk-QEZL7EYN.js";
6
+ import {
7
+ createPKCE,
8
+ generateSecure
9
+ } from "./chunk-N2APGLXA.js";
10
+
11
+ // src/actions/signIn/signIn.ts
12
+ import z from "zod";
13
+ import { createEndpoint, createEndpointConfig } from "@aura-stack/router";
14
+ var signInConfig = (oauth) => {
15
+ return createEndpointConfig("/signIn/:oauth", {
16
+ schemas: {
17
+ params: z.object({
18
+ oauth: z.enum(Object.keys(oauth), "The OAuth provider is not supported or invalid."),
19
+ redirectTo: z.string().optional()
20
+ })
21
+ }
22
+ });
23
+ };
24
+ var signInAction = (oauth) => {
25
+ return createEndpoint(
26
+ "GET",
27
+ "/signIn/:oauth",
28
+ async (ctx) => {
29
+ const {
30
+ request,
31
+ headers: headersBuilder,
32
+ params: { oauth: oauth2, redirectTo },
33
+ context: { oauth: providers, cookies, trustedProxyHeaders, basePath }
34
+ } = ctx;
35
+ const state = generateSecure();
36
+ const redirectURI = createRedirectURI(request, oauth2, basePath, trustedProxyHeaders);
37
+ const redirectToValue = createRedirectTo(request, redirectTo, trustedProxyHeaders);
38
+ const { codeVerifier, codeChallenge, method } = await createPKCE();
39
+ const authorization = createAuthorizationURL(providers[oauth2], redirectURI, state, codeChallenge, method);
40
+ 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();
41
+ return Response.json(
42
+ { oauth: oauth2 },
43
+ {
44
+ status: 302,
45
+ headers
46
+ }
47
+ );
48
+ },
49
+ signInConfig(oauth)
50
+ );
51
+ };
52
+
53
+ export {
54
+ signInAction
55
+ };
@@ -1,19 +1,22 @@
1
- import {
2
- AuthError,
3
- ERROR_RESPONSE,
4
- throwAuthError
5
- } from "./chunk-FJUDBLCP.js";
6
1
  import {
7
2
  OAuthAccessToken,
8
3
  OAuthAccessTokenErrorResponse,
9
4
  OAuthAccessTokenResponse
10
- } from "./chunk-HMRKN75I.js";
5
+ } from "./chunk-WD7AUHQ5.js";
6
+ import {
7
+ formatZodError
8
+ } from "./chunk-CXLATHS5.js";
9
+ import {
10
+ AuthInternalError,
11
+ OAuthProtocolError
12
+ } from "./chunk-RRLIF4PQ.js";
11
13
 
12
14
  // src/actions/callback/access-token.ts
13
15
  var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
14
16
  const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
15
17
  if (!parsed.success) {
16
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_REQUEST, "Invalid OAuth configuration");
18
+ const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
19
+ throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
17
20
  }
18
21
  const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
19
22
  try {
@@ -37,13 +40,13 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
37
40
  if (!token.success) {
38
41
  const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
39
42
  if (!success) {
40
- throw new AuthError(ERROR_RESPONSE.ACCESS_TOKEN.INVALID_GRANT, "Invalid access token response format");
43
+ throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
41
44
  }
42
- throw new AuthError(data.error, data?.error_description ?? "Failed to retrieve access token");
45
+ throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
43
46
  }
44
47
  return token.data;
45
48
  } catch (error) {
46
- throw throwAuthError(error, "Failed to create access token");
49
+ throw error;
47
50
  }
48
51
  };
49
52
 
@@ -0,0 +1,22 @@
1
+ // src/oauth/strava.ts
2
+ var strava = {
3
+ id: "strava",
4
+ name: "Strava",
5
+ authorizeURL: "https://www.strava.com/oauth/authorize",
6
+ accessToken: "https://www.strava.com/oauth/token",
7
+ userInfo: "https://www.strava.com/api/v3/athlete",
8
+ scope: "read",
9
+ responseType: "code",
10
+ profile(profile) {
11
+ return {
12
+ sub: profile.id.toString(),
13
+ name: `${profile.firstname} ${profile.lastname}`,
14
+ image: profile.profile,
15
+ email: ""
16
+ };
17
+ }
18
+ };
19
+
20
+ export {
21
+ strava
22
+ };