@aura-stack/auth 0.4.0-rc.4 → 0.4.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 (143) hide show
  1. package/dist/@types/index.d.ts +4 -3
  2. package/dist/@types/router.d.cjs +0 -17
  3. package/dist/@types/router.d.d.ts +3 -2
  4. package/dist/@types/router.d.js +0 -1
  5. package/dist/actions/callback/access-token.cjs +40 -25
  6. package/dist/actions/callback/access-token.d.ts +4 -3
  7. package/dist/actions/callback/access-token.js +3 -4
  8. package/dist/actions/callback/callback.cjs +287 -77
  9. package/dist/actions/callback/callback.d.ts +5 -26
  10. package/dist/actions/callback/callback.js +13 -10
  11. package/dist/actions/callback/userinfo.cjs +68 -7
  12. package/dist/actions/callback/userinfo.d.ts +4 -3
  13. package/dist/actions/callback/userinfo.js +8 -6
  14. package/dist/actions/csrfToken/csrfToken.cjs +63 -4
  15. package/dist/actions/csrfToken/csrfToken.d.ts +1 -3
  16. package/dist/actions/csrfToken/csrfToken.js +8 -6
  17. package/dist/actions/index.cjs +400 -175
  18. package/dist/actions/index.d.ts +3 -2
  19. package/dist/actions/index.js +21 -19
  20. package/dist/actions/session/session.cjs +40 -11
  21. package/dist/actions/session/session.d.ts +1 -3
  22. package/dist/actions/session/session.js +4 -4
  23. package/dist/actions/signIn/authorization.cjs +171 -132
  24. package/dist/actions/signIn/authorization.d.ts +21 -11
  25. package/dist/actions/signIn/authorization.js +8 -6
  26. package/dist/actions/signIn/signIn.cjs +220 -113
  27. package/dist/actions/signIn/signIn.d.ts +5 -25
  28. package/dist/actions/signIn/signIn.js +9 -7
  29. package/dist/actions/signOut/signOut.cjs +268 -119
  30. package/dist/actions/signOut/signOut.d.ts +1 -9
  31. package/dist/actions/signOut/signOut.js +10 -8
  32. package/dist/assert.cjs +117 -5
  33. package/dist/assert.d.ts +22 -3
  34. package/dist/assert.js +17 -3
  35. package/dist/chunk-4EKY7655.js +123 -0
  36. package/dist/chunk-4MYWAOLG.js +31 -0
  37. package/dist/chunk-4YHJ4IEQ.js +25 -0
  38. package/dist/chunk-54CZPKR4.js +25 -0
  39. package/dist/chunk-5LZ7TOM3.js +25 -0
  40. package/dist/{chunk-W6LG7BFW.js → chunk-5W4BRQYG.js} +24 -20
  41. package/dist/chunk-6MXFPFR3.js +143 -0
  42. package/dist/{chunk-3EUWD5BB.js → chunk-7QF22LHP.js} +13 -9
  43. package/dist/chunk-ALG3GIV4.js +95 -0
  44. package/dist/chunk-E6G5YCI6.js +25 -0
  45. package/dist/chunk-EBAMFRB7.js +34 -0
  46. package/dist/chunk-EEE7UM5T.js +25 -0
  47. package/dist/{chunk-TLE4PXY3.js → chunk-FRJFWTOY.js} +38 -7
  48. package/dist/chunk-FW4W3REU.js +25 -0
  49. package/dist/{chunk-HT4YLL7N.js → chunk-ICAZ4OVS.js} +10 -8
  50. package/dist/chunk-IPKO6UQN.js +25 -0
  51. package/dist/{chunk-YRCB5FLE.js → chunk-KJBAQZX2.js} +13 -0
  52. package/dist/chunk-KMMAZFSJ.js +25 -0
  53. package/dist/chunk-LDU7A2JE.js +25 -0
  54. package/dist/{chunk-N2APGLXA.js → chunk-NUDITUKX.js} +18 -16
  55. package/dist/chunk-OVHNRULD.js +33 -0
  56. package/dist/{chunk-JVFTCTTE.js → chunk-PHFH2MGS.js} +12 -9
  57. package/dist/chunk-QQVSRXGX.js +149 -0
  58. package/dist/chunk-TM5IPSNF.js +113 -0
  59. package/dist/{chunk-GA2SMTJO.js → chunk-TZB6MUXN.js} +33 -13
  60. package/dist/chunk-VNCNJKS2.js +267 -0
  61. package/dist/{chunk-IVET23KF.js → chunk-XGLBNXL4.js} +31 -14
  62. package/dist/chunk-XUP6KKNG.js +106 -0
  63. package/dist/cookie.cjs +24 -20
  64. package/dist/cookie.d.ts +4 -3
  65. package/dist/cookie.js +1 -1
  66. package/dist/env.cjs +56 -0
  67. package/dist/env.d.ts +7 -0
  68. package/dist/env.js +6 -0
  69. package/dist/errors.d.ts +4 -3
  70. package/dist/headers.cjs +28 -2
  71. package/dist/headers.d.ts +25 -1
  72. package/dist/headers.js +9 -3
  73. package/dist/{index-DkaLJFn8.d.ts → index-CSyIJmCM.d.ts} +373 -45
  74. package/dist/index.cjs +1128 -483
  75. package/dist/index.d.ts +6 -10
  76. package/dist/index.js +83 -42
  77. package/dist/jose.cjs +62 -25
  78. package/dist/jose.d.ts +7 -5
  79. package/dist/jose.js +8 -6
  80. package/dist/logger.cjs +292 -0
  81. package/dist/logger.d.ts +8 -0
  82. package/dist/logger.js +8 -0
  83. package/dist/oauth/bitbucket.cjs +19 -15
  84. package/dist/oauth/bitbucket.d.ts +3 -2
  85. package/dist/oauth/bitbucket.js +1 -1
  86. package/dist/oauth/discord.cjs +27 -24
  87. package/dist/oauth/discord.d.ts +3 -2
  88. package/dist/oauth/discord.js +1 -1
  89. package/dist/oauth/figma.cjs +19 -16
  90. package/dist/oauth/figma.d.ts +3 -2
  91. package/dist/oauth/figma.js +1 -1
  92. package/dist/oauth/github.cjs +19 -8
  93. package/dist/oauth/github.d.ts +3 -2
  94. package/dist/oauth/github.js +1 -1
  95. package/dist/oauth/gitlab.cjs +19 -16
  96. package/dist/oauth/gitlab.d.ts +3 -2
  97. package/dist/oauth/gitlab.js +1 -1
  98. package/dist/oauth/index.cjs +266 -166
  99. package/dist/oauth/index.d.ts +3 -2
  100. package/dist/oauth/index.js +22 -21
  101. package/dist/oauth/mailchimp.cjs +19 -16
  102. package/dist/oauth/mailchimp.d.ts +3 -2
  103. package/dist/oauth/mailchimp.js +1 -1
  104. package/dist/oauth/pinterest.cjs +19 -16
  105. package/dist/oauth/pinterest.d.ts +3 -2
  106. package/dist/oauth/pinterest.js +1 -1
  107. package/dist/oauth/spotify.cjs +19 -16
  108. package/dist/oauth/spotify.d.ts +3 -2
  109. package/dist/oauth/spotify.js +1 -1
  110. package/dist/oauth/strava.cjs +19 -16
  111. package/dist/oauth/strava.d.ts +3 -2
  112. package/dist/oauth/strava.js +1 -1
  113. package/dist/oauth/x.cjs +19 -16
  114. package/dist/oauth/x.d.ts +3 -2
  115. package/dist/oauth/x.js +1 -1
  116. package/dist/schemas.cjs +16 -2
  117. package/dist/schemas.d.ts +17 -1
  118. package/dist/schemas.js +5 -3
  119. package/dist/secure.cjs +58 -16
  120. package/dist/secure.d.ts +4 -10
  121. package/dist/secure.js +5 -5
  122. package/dist/utils.cjs +94 -87
  123. package/dist/utils.d.ts +9 -39
  124. package/dist/utils.js +11 -9
  125. package/package.json +3 -4
  126. package/dist/chunk-42XB3YCW.js +0 -22
  127. package/dist/chunk-6R2YZ4AC.js +0 -22
  128. package/dist/chunk-A3N4PVAT.js +0 -70
  129. package/dist/chunk-B737EUJV.js +0 -22
  130. package/dist/chunk-CXLATHS5.js +0 -143
  131. package/dist/chunk-DIVDFNAP.js +0 -0
  132. package/dist/chunk-E3OXBRYF.js +0 -22
  133. package/dist/chunk-EIL2FPSS.js +0 -22
  134. package/dist/chunk-EMKJA2GJ.js +0 -89
  135. package/dist/chunk-FIPU4MLT.js +0 -21
  136. package/dist/chunk-FKRDCWBF.js +0 -22
  137. package/dist/chunk-HP34YGGJ.js +0 -22
  138. package/dist/chunk-IKHPGFCW.js +0 -14
  139. package/dist/chunk-IUYZQTJV.js +0 -30
  140. package/dist/chunk-KRNOMBXQ.js +0 -22
  141. package/dist/chunk-KSWLO5ZU.js +0 -102
  142. package/dist/chunk-N4SX7TZT.js +0 -96
  143. package/dist/chunk-STHEPPUZ.js +0 -11
@@ -37,7 +37,7 @@ var import_zod2 = require("zod");
37
37
  var import_router2 = require("@aura-stack/router");
38
38
 
39
39
  // src/secure.ts
40
- var import_crypto = __toESM(require("crypto"), 1);
40
+ var import_crypto2 = __toESM(require("crypto"), 1);
41
41
 
42
42
  // src/utils.ts
43
43
  var import_router = require("@aura-stack/router");
@@ -87,72 +87,150 @@ var equals = (a, b) => {
87
87
  if (a === null || b === null || a === void 0 || b === void 0) return false;
88
88
  return a === b;
89
89
  };
90
- var sanitizeURL = (url) => {
90
+
91
+ // src/assert.ts
92
+ var import_crypto = require("crypto");
93
+ var unsafeChars = [
94
+ "<",
95
+ ">",
96
+ '"',
97
+ "`",
98
+ " ",
99
+ "\r",
100
+ "\n",
101
+ " ",
102
+ "\\",
103
+ "%2F",
104
+ "%5C",
105
+ "%2f",
106
+ "%5c",
107
+ "\r\n",
108
+ "%0A",
109
+ "%0D",
110
+ "%0a",
111
+ "%0d",
112
+ "..",
113
+ "//",
114
+ "///",
115
+ "...",
116
+ "%20",
117
+ "\0"
118
+ ];
119
+ var isValidURL = (value) => {
120
+ if (!new RegExp(/^https?:\/\/[^/]/).test(value)) {
121
+ return false;
122
+ }
123
+ const match = value.match(/^(https?:\/\/)(.*)$/);
124
+ if (!match) return false;
125
+ const rest = match[2];
126
+ for (const char of unsafeChars) {
127
+ if (rest.includes(char)) return false;
128
+ }
129
+ const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()?#*+,;=:@-]*)*\/?$/;
130
+ return regex.test(match[0]);
131
+ };
132
+ var isRelativeURL = (value) => {
133
+ if (value.length > 100) return false;
134
+ for (const char of unsafeChars) {
135
+ if (value.includes(char)) return false;
136
+ }
137
+ const regex = /^\/[a-zA-Z0-9\-_\/.?&=#]*\/?$/;
138
+ return regex.test(value);
139
+ };
140
+ var isSameOrigin = (origin, expected) => {
141
+ const originURL = new URL(origin);
142
+ const expectedURL = new URL(expected);
143
+ return equals(originURL.origin, expectedURL.origin);
144
+ };
145
+ var patternToRegex = (pattern) => {
91
146
  try {
92
- let decodedURL = decodeURIComponent(url).trim();
93
- const protocolMatch = decodedURL.match(/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)/);
94
- let protocol = "";
95
- let rest = decodedURL;
96
- if (protocolMatch) {
97
- protocol = protocolMatch[1];
98
- rest = decodedURL.slice(protocol.length);
99
- const slashIndex = rest.indexOf("/");
100
- if (slashIndex === -1) {
101
- return protocol + rest;
102
- }
103
- const domain = rest.slice(0, slashIndex);
104
- let path = rest.slice(slashIndex).replace(/\/\.\.\//g, "/").replace(/\/\.\.$/, "").replace(/\.{2,}/g, "").replace(/\/{2,}/g, "/");
105
- if (path !== "/" && path.endsWith("/")) {
106
- path = path.replace(/\/+$/, "/");
107
- } else if (path !== "/") {
108
- path = path.replace(/\/+$/, "");
147
+ if (pattern.length > 2048) return null;
148
+ pattern = pattern.replace(/\\/g, "");
149
+ const match = pattern.match(/^(https?):\/\/([a-zA-Z0-9.*-]{1,253})(?::(\d{1,5}|\*))?(?:\/.*)?$/);
150
+ if (!match) return null;
151
+ const [, protocol, host, port] = match;
152
+ const hasWildcard = host.includes("*");
153
+ if (hasWildcard && !host.startsWith("*.")) return null;
154
+ if (hasWildcard && host.slice(2).includes("*")) return null;
155
+ const domain = hasWildcard ? host.slice(2) : host;
156
+ const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
157
+ const hostRegex = hasWildcard ? `[^.]+\\.${escapedDomain}` : escapedDomain;
158
+ const portRegex = port === "*" ? ":\\d{1,5}" : port ? `:${port}` : "";
159
+ return new RegExp(`^${protocol}:\\/\\/${hostRegex}${portRegex}$`);
160
+ } catch {
161
+ return null;
162
+ }
163
+ };
164
+ var isTrustedOrigin = (url, trustedOrigins) => {
165
+ if (!isValidURL(url) || trustedOrigins.length === 0) return false;
166
+ try {
167
+ const urlOrigin = new URL(url).origin;
168
+ for (const pattern of trustedOrigins) {
169
+ const regex = patternToRegex(pattern);
170
+ if (regex?.test(urlOrigin)) return true;
171
+ try {
172
+ if (isValidURL(pattern) && equals(new URL(pattern).origin, urlOrigin)) return true;
173
+ } catch {
109
174
  }
110
- return protocol + domain + path;
111
- }
112
- let sanitized = decodedURL.replace(/\/\.\.\//g, "/").replace(/\/\.\.$/, "").replace(/\.{2,}/g, "").replace(/\/{2,}/g, "/");
113
- if (sanitized !== "/" && sanitized.endsWith("/")) {
114
- sanitized = sanitized.replace(/\/+$/, "/");
115
- } else if (sanitized !== "/") {
116
- sanitized = sanitized.replace(/\/+$/, "");
117
175
  }
118
- return sanitized;
119
176
  } catch {
120
- return url.trim();
121
177
  }
178
+ return false;
122
179
  };
123
- var isValidRelativePath = (path) => {
124
- if (!path || typeof path !== "string") return false;
125
- if (!path.startsWith("/") || path.includes("://") || path.includes("\r") || path.includes("\n")) return false;
126
- if (/[\x00-\x1F\x7F]/.test(path) || path.includes("\0")) return false;
127
- const sanitized = sanitizeURL(path);
128
- if (sanitized.includes("..")) return false;
129
- return true;
130
- };
131
- var formatZodError = (error) => {
132
- if (!error.issues || error.issues.length === 0) {
133
- return {};
180
+ var safeEquals = (a, b) => {
181
+ const bufferA = Buffer.from(a);
182
+ const bufferB = Buffer.from(b);
183
+ if (bufferA.length !== bufferB.length) {
184
+ return false;
134
185
  }
135
- return error.issues.reduce((previous, issue) => {
136
- const key = issue.path.join(".");
137
- return {
138
- ...previous,
139
- [key]: {
140
- code: issue.code,
141
- message: issue.message
142
- }
186
+ return (0, import_crypto.timingSafeEqual)(bufferA, bufferB);
187
+ };
188
+
189
+ // src/env.ts
190
+ var import_meta = {};
191
+ var env = new Proxy({}, {
192
+ get(_, prop) {
193
+ if (typeof prop !== "string") return void 0;
194
+ const hasProperty = (process2) => {
195
+ return process2 && Object.prototype.hasOwnProperty.call(process2, prop);
143
196
  };
144
- }, {});
197
+ try {
198
+ if (typeof process !== "undefined" && hasProperty(process.env)) {
199
+ return process.env[prop];
200
+ }
201
+ if (typeof import_meta !== "undefined" && hasProperty(import_meta.env)) {
202
+ return import_meta.env[prop];
203
+ }
204
+ if (typeof Deno !== "undefined" && Deno.env?.get) {
205
+ return Deno.env.get(prop);
206
+ }
207
+ if (typeof Bun !== "undefined" && hasProperty(Bun.env)) {
208
+ return Bun.env[prop];
209
+ }
210
+ const globalValue = globalThis[prop];
211
+ return typeof globalValue === "string" ? globalValue : void 0;
212
+ } catch {
213
+ return void 0;
214
+ }
215
+ }
216
+ });
217
+
218
+ // src/jose.ts
219
+ var import_jose = require("@aura-stack/jose");
220
+ var jwtVerificationOptions = {
221
+ algorithms: ["HS256"],
222
+ typ: "JWT"
145
223
  };
146
224
 
147
225
  // src/secure.ts
148
226
  var generateSecure = (length = 32) => {
149
- return import_crypto.default.randomBytes(length).toString("base64url");
227
+ return import_crypto2.default.randomBytes(length).toString("base64url");
150
228
  };
151
229
  var createCSRF = async (jose, csrfCookie) => {
152
230
  try {
153
231
  const token = generateSecure(32);
154
232
  if (csrfCookie) {
155
- await jose.verifyJWS(csrfCookie);
233
+ await jose.verifyJWS(csrfCookie, jwtVerificationOptions);
156
234
  return csrfCookie;
157
235
  }
158
236
  return jose.signJWS({ token });
@@ -169,6 +247,26 @@ var cacheControl = {
169
247
  Expires: "0",
170
248
  Vary: "Cookie"
171
249
  };
250
+ var contentSecurityPolicy = {
251
+ "Content-Security-Policy": [
252
+ "default-src 'none'",
253
+ "script-src 'self'",
254
+ "frame-src 'none'",
255
+ "object-src 'none'",
256
+ "frame-ancestors 'none'",
257
+ "base-uri 'none'"
258
+ ].join("; ")
259
+ };
260
+ var secureHeaders = {
261
+ "X-Content-Type-Options": "nosniff",
262
+ "X-Frame-Options": "DENY",
263
+ "Referrer-Policy": "strict-origin-when-cross-origin"
264
+ };
265
+ var secureApiHeaders = {
266
+ ...cacheControl,
267
+ ...contentSecurityPolicy,
268
+ ...secureHeaders
269
+ };
172
270
 
173
271
  // src/request.ts
174
272
  var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
@@ -183,6 +281,18 @@ var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
183
281
 
184
282
  // src/schemas.ts
185
283
  var import_zod = require("zod");
284
+ var OAuthProviderCredentialsSchema = (0, import_zod.object)({
285
+ id: (0, import_zod.string)(),
286
+ name: (0, import_zod.string)(),
287
+ authorizeURL: (0, import_zod.string)().url(),
288
+ accessToken: (0, import_zod.string)().url(),
289
+ scope: (0, import_zod.string)(),
290
+ userInfo: (0, import_zod.string)().url(),
291
+ responseType: (0, import_zod.enum)(["code", "token", "id_token"]),
292
+ clientId: (0, import_zod.string)(),
293
+ clientSecret: (0, import_zod.string)(),
294
+ profile: import_zod.z.function().optional()
295
+ });
186
296
  var OAuthProviderConfigSchema = (0, import_zod.object)({
187
297
  authorizeURL: (0, import_zod.string)().url(),
188
298
  accessToken: (0, import_zod.string)().url(),
@@ -259,9 +369,14 @@ var getDefaultUserInfo = (profile) => {
259
369
  image: profile?.image ?? profile?.picture
260
370
  };
261
371
  };
262
- var getUserInfo = async (oauthConfig, accessToken) => {
372
+ var getUserInfo = async (oauthConfig, accessToken, logger) => {
263
373
  const userinfoEndpoint = oauthConfig.userInfo;
264
374
  try {
375
+ logger?.log("OAUTH_USERINFO_REQUEST_INITIATED", {
376
+ structuredData: {
377
+ endpoint: userinfoEndpoint
378
+ }
379
+ });
265
380
  const response = await fetchAsync(userinfoEndpoint, {
266
381
  method: "GET",
267
382
  headers: {
@@ -269,35 +384,77 @@ var getUserInfo = async (oauthConfig, accessToken) => {
269
384
  Authorization: `Bearer ${accessToken}`
270
385
  }
271
386
  });
387
+ if (!response.ok) {
388
+ logger?.log("OAUTH_USERINFO_INVALID_RESPONSE");
389
+ throw new OAuthProtocolError("INVALID_REQUEST", "Invalid userinfo response format");
390
+ }
272
391
  const json = await response.json();
273
392
  const { success, data } = OAuthErrorResponse.safeParse(json);
274
393
  if (success) {
275
- throw new OAuthProtocolError(
276
- data.error,
277
- data?.error_description ?? "An error occurred while fetching user information."
278
- );
394
+ logger?.log("OAUTH_USERINFO_ERROR", {
395
+ message: "Error response received from OAuth userinfo endpoint",
396
+ structuredData: {
397
+ error: data.error,
398
+ error_description: data.error_description ?? ""
399
+ }
400
+ });
401
+ throw new OAuthProtocolError("INVALID_REQUEST", "An error was received from the OAuth userinfo endpoint.");
279
402
  }
403
+ logger?.log("OAUTH_USERINFO_SUCCESS");
280
404
  return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
281
405
  } catch (error) {
282
406
  if (isOAuthProtocolError(error)) {
283
407
  throw error;
284
408
  }
409
+ logger?.log("OAUTH_USERINFO_REQUEST_FAILED");
285
410
  if (isNativeError(error)) {
286
- throw new OAuthProtocolError("invalid_request", error.message, "", { cause: error });
411
+ throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information from OAuth provider", "", {
412
+ cause: error
413
+ });
287
414
  }
288
- throw new OAuthProtocolError("invalid_request", "Failed to fetch user information.", "", { cause: error });
415
+ throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information", "", { cause: error });
289
416
  }
290
417
  };
291
418
 
419
+ // src/actions/signIn/authorization.ts
420
+ var getTrustedOrigins = async (request, trustedOrigins) => {
421
+ if (!trustedOrigins) return [];
422
+ const raw = typeof trustedOrigins === "function" ? await trustedOrigins(request) : trustedOrigins;
423
+ return Array.isArray(raw) ? raw : typeof raw === "string" ? [raw] : [];
424
+ };
425
+ var getOriginURL = async (request, context) => {
426
+ const headers = request.headers;
427
+ let origin = new URL(request.url).origin;
428
+ const trustedOrigins = await getTrustedOrigins(request, context?.trustedOrigins);
429
+ trustedOrigins.push(origin);
430
+ if (context?.trustedProxyHeaders) {
431
+ const protocol = headers.get("Forwarded")?.match(/proto=([^;]+)/i)?.[1] ?? headers.get("X-Forwarded-Proto") ?? "http";
432
+ const host = headers.get("Host") ?? headers.get("Forwarded")?.match(/host=([^;]+)/i)?.[1] ?? headers.get("X-Forwarded-Host") ?? null;
433
+ origin = `${protocol}://${host}`;
434
+ }
435
+ if (!isTrustedOrigin(origin, trustedOrigins)) {
436
+ context?.logger?.log("UNTRUSTED_ORIGIN", { structuredData: { origin } });
437
+ throw new AuthInternalError("UNTRUSTED_ORIGIN", "The constructed origin URL is not trusted.");
438
+ }
439
+ return origin;
440
+ };
441
+
292
442
  // src/actions/callback/access-token.ts
293
- var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
443
+ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier, logger) => {
294
444
  const parsed = OAuthAccessToken.safeParse({ ...oauthConfig, redirectURI, code, codeVerifier });
295
445
  if (!parsed.success) {
296
- const msg = JSON.stringify(formatZodError(parsed.error), null, 2);
297
- throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", msg);
446
+ logger?.log("INVALID_OAUTH_CONFIGURATION");
447
+ throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "The OAuth provider configuration is invalid.");
298
448
  }
299
449
  const { accessToken, clientId, clientSecret, code: codeParsed, redirectURI: redirectParsed } = parsed.data;
300
450
  try {
451
+ logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_INITIATED", {
452
+ structuredData: {
453
+ has_client_id: Boolean(clientId),
454
+ redirect_uri: redirectParsed,
455
+ grant_type: "authorization_code"
456
+ }
457
+ });
301
458
  const response = await fetchAsync(accessToken, {
302
459
  method: "POST",
303
460
  headers: {
@@ -313,17 +470,33 @@ var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) =>
313
470
  code_verifier: codeVerifier
314
471
  }).toString()
315
472
  });
473
+ if (!response.ok) {
474
+ logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
475
+ throw new OAuthProtocolError("invalid_request", "Invalid access token response");
476
+ }
316
477
  const json = await response.json();
317
478
  const token = OAuthAccessTokenResponse.safeParse(json);
318
479
  if (!token.success) {
319
480
  const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
320
481
  if (!success) {
321
- throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
482
+ logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
483
+ throw new OAuthProtocolError("invalid_request", "Invalid access token response format");
322
484
  }
323
- throw new OAuthProtocolError(data.error, data?.error_description ?? "Failed to retrieve access token");
485
+ logger?.log("OAUTH_ACCESS_TOKEN_ERROR", {
486
+ structuredData: {
487
+ error: data.error,
488
+ error_description: data.error_description ?? ""
489
+ }
490
+ });
491
+ throw new OAuthProtocolError("INVALID_ACCESS_TOKEN", "Failed to retrieve access token");
324
492
  }
493
+ logger?.log("OAUTH_ACCESS_TOKEN_SUCCESS");
325
494
  return token.data;
326
495
  } catch (error) {
496
+ logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_FAILED");
497
+ if (error instanceof Error) {
498
+ throw new OAuthProtocolError("server_error", "Failed to communicate with OAuth provider", "", { cause: error });
499
+ }
327
500
  throw error;
328
501
  }
329
502
  };
@@ -345,7 +518,8 @@ var oauthCookieOptions = {
345
518
  var expiredCookieAttributes = {
346
519
  ...defaultCookieOptions,
347
520
  expires: /* @__PURE__ */ new Date(0),
348
- maxAge: 0
521
+ maxAge: 0,
522
+ secure: true
349
523
  };
350
524
  var getCookie = (request, cookieName) => {
351
525
  const cookies = request.headers.get("Cookie");
@@ -384,10 +558,23 @@ var callbackConfig = (oauth) => {
384
558
  },
385
559
  middlewares: [
386
560
  (ctx) => {
387
- const response = OAuthAuthorizationErrorResponse.safeParse(ctx.searchParams);
561
+ const {
562
+ searchParams,
563
+ context: { logger }
564
+ } = ctx;
565
+ const response = OAuthAuthorizationErrorResponse.safeParse(searchParams);
388
566
  if (response.success) {
389
567
  const { error, error_description } = response.data;
390
- throw new OAuthProtocolError(error, error_description ?? "OAuth Authorization Error");
568
+ const criticalAuthErrors = ["access_denied", "server_error"];
569
+ const severity = criticalAuthErrors.includes(error.toLowerCase()) ? "critical" : "warning";
570
+ logger?.log("OAUTH_AUTHORIZATION_ERROR", {
571
+ severity,
572
+ structuredData: {
573
+ error,
574
+ error_description: error_description ?? ""
575
+ }
576
+ });
577
+ throw new OAuthProtocolError(error, error_description || "OAuth Authorization Error");
391
578
  }
392
579
  return ctx;
393
580
  }
@@ -403,31 +590,54 @@ var callbackAction = (oauth) => {
403
590
  request,
404
591
  params: { oauth: oauth2 },
405
592
  searchParams: { code, state },
406
- context: { oauth: providers, cookies, jose }
593
+ context
407
594
  } = ctx;
595
+ const { oauth: providers, cookies, jose, logger, trustedOrigins } = context;
408
596
  const oauthConfig = providers[oauth2];
409
597
  const cookieState = getCookie(request, cookies.state.name);
598
+ const codeVerifier = getCookie(request, cookies.codeVerifier.name);
410
599
  const cookieRedirectTo = getCookie(request, cookies.redirectTo.name);
411
600
  const cookieRedirectURI = getCookie(request, cookies.redirectURI.name);
412
- const codeVerifier = getCookie(request, cookies.codeVerifier.name);
413
- if (!equals(cookieState, state)) {
601
+ if (!safeEquals(cookieState, state)) {
602
+ logger?.log("MISMATCHING_STATE", {
603
+ structuredData: {
604
+ oauth_provider: oauth2
605
+ }
606
+ });
414
607
  throw new AuthSecurityError(
415
608
  "MISMATCHING_STATE",
416
609
  "The provided state passed in the OAuth response does not match the stored state."
417
610
  );
418
611
  }
419
- const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
420
- const sanitized = sanitizeURL(cookieRedirectTo);
421
- if (!isValidRelativePath(sanitized)) {
422
- throw new AuthSecurityError(
423
- "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
424
- "Invalid redirect path. Potential open redirect attack detected."
425
- );
612
+ const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier, logger);
613
+ const origins = await getTrustedOrigins(request, trustedOrigins);
614
+ const requestOrigin = await getOriginURL(request, context);
615
+ if (!isRelativeURL(cookieRedirectTo)) {
616
+ const isValid = origins.length > 0 ? isTrustedOrigin(cookieRedirectTo, origins) : isSameOrigin(cookieRedirectTo, requestOrigin);
617
+ if (!isValid) {
618
+ logger?.log("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", {
619
+ structuredData: {
620
+ redirect_path: cookieRedirectTo,
621
+ provider: oauth2,
622
+ has_trusted_origins: origins.length > 0,
623
+ request_origin: requestOrigin
624
+ }
625
+ });
626
+ throw new AuthSecurityError(
627
+ "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
628
+ "Invalid redirect path. Potential open redirect attack detected."
629
+ );
630
+ }
426
631
  }
427
- const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
632
+ const userInfo = await getUserInfo(oauthConfig, accessToken.access_token, logger);
428
633
  const sessionCookie = await createSessionCookie(jose, userInfo);
429
634
  const csrfToken = await createCSRF(jose);
430
- const headers = new import_router2.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();
635
+ logger?.log("OAUTH_CALLBACK_SUCCESS", {
636
+ structuredData: {
637
+ provider: oauth2
638
+ }
639
+ });
640
+ const headers = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", cookieRedirectTo).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();
431
641
  return Response.json({ oauth: oauth2 }, { status: 302, headers });
432
642
  },
433
643
  callbackConfig(oauth)
@@ -1,34 +1,13 @@
1
1
  import * as _aura_stack_router from '@aura-stack/router';
2
- import { z } from 'zod';
3
- import { j as OAuthProviderRecord } from '../../index-DkaLJFn8.js';
2
+ import { k as OAuthProviderRecord } from '../../index-CSyIJmCM.js';
3
+ import 'zod';
4
4
  import '../../schemas.js';
5
- import '@aura-stack/router/cookie';
5
+ import '../../jose.js';
6
6
  import '@aura-stack/jose';
7
7
  import '@aura-stack/jose/jose';
8
+ import '@aura-stack/router/cookie';
8
9
  import '../../@types/utility.js';
9
10
 
10
- declare const callbackAction: (oauth: OAuthProviderRecord) => _aura_stack_router.RouteEndpoint<"GET", "/callback/:oauth", {
11
- schemas?: {
12
- params: z.ZodObject<{
13
- oauth: z.ZodEnum<{
14
- [x: string & Record<never, never>]: string & Record<never, never>;
15
- github: "github";
16
- bitbucket: "bitbucket";
17
- figma: "figma";
18
- discord: "discord";
19
- gitlab: "gitlab";
20
- spotify: "spotify";
21
- x: "x";
22
- strava: "strava";
23
- mailchimp: "mailchimp";
24
- pinterest: "pinterest";
25
- }>;
26
- }, z.core.$strip>;
27
- searchParams: z.ZodObject<{
28
- code: z.ZodString;
29
- state: z.ZodString;
30
- }, z.core.$strip>;
31
- } | undefined;
32
- }>;
11
+ declare const callbackAction: (oauth: OAuthProviderRecord) => _aura_stack_router.RouteEndpoint<"GET", "/callback/:oauth", {}>;
33
12
 
34
13
  export { callbackAction };
@@ -1,16 +1,19 @@
1
1
  import {
2
2
  callbackAction
3
- } from "../../chunk-KSWLO5ZU.js";
4
- import "../../chunk-GA2SMTJO.js";
5
- import "../../chunk-IVET23KF.js";
6
- import "../../chunk-W6LG7BFW.js";
7
- import "../../chunk-STHEPPUZ.js";
8
- import "../../chunk-N2APGLXA.js";
9
- import "../../chunk-CXLATHS5.js";
10
- import "../../chunk-EIL2FPSS.js";
11
- import "../../chunk-RRLIF4PQ.js";
3
+ } from "../../chunk-6MXFPFR3.js";
4
+ import "../../chunk-TZB6MUXN.js";
5
+ import "../../chunk-XGLBNXL4.js";
6
+ import "../../chunk-XUP6KKNG.js";
12
7
  import "../../chunk-ZNCZVF6U.js";
13
- import "../../chunk-YRCB5FLE.js";
8
+ import "../../chunk-KJBAQZX2.js";
9
+ import "../../chunk-NUDITUKX.js";
10
+ import "../../chunk-4EKY7655.js";
11
+ import "../../chunk-QQVSRXGX.js";
12
+ import "../../chunk-5W4BRQYG.js";
13
+ import "../../chunk-EBAMFRB7.js";
14
+ import "../../chunk-FRJFWTOY.js";
15
+ import "../../chunk-4MYWAOLG.js";
16
+ import "../../chunk-RRLIF4PQ.js";
14
17
  export {
15
18
  callbackAction
16
19
  };