@aura-stack/auth 0.4.0-rc.5 → 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.
- package/dist/@types/index.d.ts +4 -3
- package/dist/@types/router.d.cjs +0 -17
- package/dist/@types/router.d.d.ts +3 -2
- package/dist/@types/router.d.js +0 -1
- package/dist/actions/callback/access-token.cjs +40 -25
- package/dist/actions/callback/access-token.d.ts +4 -3
- package/dist/actions/callback/access-token.js +3 -4
- package/dist/actions/callback/callback.cjs +287 -77
- package/dist/actions/callback/callback.d.ts +5 -26
- package/dist/actions/callback/callback.js +13 -10
- package/dist/actions/callback/userinfo.cjs +68 -7
- package/dist/actions/callback/userinfo.d.ts +4 -3
- package/dist/actions/callback/userinfo.js +8 -6
- package/dist/actions/csrfToken/csrfToken.cjs +63 -4
- package/dist/actions/csrfToken/csrfToken.d.ts +1 -3
- package/dist/actions/csrfToken/csrfToken.js +8 -6
- package/dist/actions/index.cjs +400 -175
- package/dist/actions/index.d.ts +3 -2
- package/dist/actions/index.js +21 -19
- package/dist/actions/session/session.cjs +40 -11
- package/dist/actions/session/session.d.ts +1 -3
- package/dist/actions/session/session.js +4 -4
- package/dist/actions/signIn/authorization.cjs +171 -132
- package/dist/actions/signIn/authorization.d.ts +21 -11
- package/dist/actions/signIn/authorization.js +8 -6
- package/dist/actions/signIn/signIn.cjs +220 -113
- package/dist/actions/signIn/signIn.d.ts +5 -25
- package/dist/actions/signIn/signIn.js +9 -7
- package/dist/actions/signOut/signOut.cjs +268 -119
- package/dist/actions/signOut/signOut.d.ts +1 -9
- package/dist/actions/signOut/signOut.js +10 -8
- package/dist/assert.cjs +117 -5
- package/dist/assert.d.ts +22 -3
- package/dist/assert.js +17 -3
- package/dist/chunk-4EKY7655.js +123 -0
- package/dist/chunk-4MYWAOLG.js +31 -0
- package/dist/chunk-4YHJ4IEQ.js +25 -0
- package/dist/chunk-54CZPKR4.js +25 -0
- package/dist/chunk-5LZ7TOM3.js +25 -0
- package/dist/{chunk-W6LG7BFW.js → chunk-5W4BRQYG.js} +24 -20
- package/dist/chunk-6MXFPFR3.js +143 -0
- package/dist/{chunk-3EUWD5BB.js → chunk-7QF22LHP.js} +13 -9
- package/dist/chunk-ALG3GIV4.js +95 -0
- package/dist/chunk-E6G5YCI6.js +25 -0
- package/dist/chunk-EBAMFRB7.js +34 -0
- package/dist/chunk-EEE7UM5T.js +25 -0
- package/dist/{chunk-TLE4PXY3.js → chunk-FRJFWTOY.js} +38 -7
- package/dist/chunk-FW4W3REU.js +25 -0
- package/dist/{chunk-HT4YLL7N.js → chunk-ICAZ4OVS.js} +10 -8
- package/dist/chunk-IPKO6UQN.js +25 -0
- package/dist/{chunk-YRCB5FLE.js → chunk-KJBAQZX2.js} +13 -0
- package/dist/chunk-KMMAZFSJ.js +25 -0
- package/dist/chunk-LDU7A2JE.js +25 -0
- package/dist/{chunk-N2APGLXA.js → chunk-NUDITUKX.js} +18 -16
- package/dist/chunk-OVHNRULD.js +33 -0
- package/dist/{chunk-JVFTCTTE.js → chunk-PHFH2MGS.js} +12 -9
- package/dist/chunk-QQVSRXGX.js +149 -0
- package/dist/chunk-TM5IPSNF.js +113 -0
- package/dist/{chunk-GA2SMTJO.js → chunk-TZB6MUXN.js} +33 -13
- package/dist/chunk-VNCNJKS2.js +267 -0
- package/dist/{chunk-IVET23KF.js → chunk-XGLBNXL4.js} +31 -14
- package/dist/chunk-XUP6KKNG.js +106 -0
- package/dist/cookie.cjs +24 -20
- package/dist/cookie.d.ts +4 -3
- package/dist/cookie.js +1 -1
- package/dist/env.cjs +56 -0
- package/dist/env.d.ts +7 -0
- package/dist/env.js +6 -0
- package/dist/errors.d.ts +4 -3
- package/dist/headers.cjs +28 -2
- package/dist/headers.d.ts +25 -1
- package/dist/headers.js +9 -3
- package/dist/{index-B8jeIElf.d.ts → index-CSyIJmCM.d.ts} +373 -45
- package/dist/index.cjs +1128 -483
- package/dist/index.d.ts +7 -67
- package/dist/index.js +83 -42
- package/dist/jose.cjs +62 -25
- package/dist/jose.d.ts +7 -5
- package/dist/jose.js +8 -6
- package/dist/logger.cjs +292 -0
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +8 -0
- package/dist/oauth/bitbucket.cjs +19 -15
- package/dist/oauth/bitbucket.d.ts +3 -2
- package/dist/oauth/bitbucket.js +1 -1
- package/dist/oauth/discord.cjs +27 -24
- package/dist/oauth/discord.d.ts +3 -2
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/figma.cjs +19 -16
- package/dist/oauth/figma.d.ts +3 -2
- package/dist/oauth/figma.js +1 -1
- package/dist/oauth/github.cjs +19 -8
- package/dist/oauth/github.d.ts +3 -2
- package/dist/oauth/github.js +1 -1
- package/dist/oauth/gitlab.cjs +19 -16
- package/dist/oauth/gitlab.d.ts +3 -2
- package/dist/oauth/gitlab.js +1 -1
- package/dist/oauth/index.cjs +266 -166
- package/dist/oauth/index.d.ts +3 -2
- package/dist/oauth/index.js +22 -21
- package/dist/oauth/mailchimp.cjs +19 -16
- package/dist/oauth/mailchimp.d.ts +3 -2
- package/dist/oauth/mailchimp.js +1 -1
- package/dist/oauth/pinterest.cjs +19 -16
- package/dist/oauth/pinterest.d.ts +3 -2
- package/dist/oauth/pinterest.js +1 -1
- package/dist/oauth/spotify.cjs +19 -16
- package/dist/oauth/spotify.d.ts +3 -2
- package/dist/oauth/spotify.js +1 -1
- package/dist/oauth/strava.cjs +19 -16
- package/dist/oauth/strava.d.ts +3 -2
- package/dist/oauth/strava.js +1 -1
- package/dist/oauth/x.cjs +19 -16
- package/dist/oauth/x.d.ts +3 -2
- package/dist/oauth/x.js +1 -1
- package/dist/schemas.cjs +16 -2
- package/dist/schemas.d.ts +17 -1
- package/dist/schemas.js +5 -3
- package/dist/secure.cjs +58 -16
- package/dist/secure.d.ts +4 -10
- package/dist/secure.js +5 -5
- package/dist/utils.cjs +94 -87
- package/dist/utils.d.ts +9 -39
- package/dist/utils.js +11 -9
- package/package.json +3 -4
- package/dist/chunk-42XB3YCW.js +0 -22
- package/dist/chunk-6R2YZ4AC.js +0 -22
- package/dist/chunk-A3N4PVAT.js +0 -70
- package/dist/chunk-B737EUJV.js +0 -22
- package/dist/chunk-CXLATHS5.js +0 -143
- package/dist/chunk-DIVDFNAP.js +0 -0
- package/dist/chunk-E3OXBRYF.js +0 -22
- package/dist/chunk-EIL2FPSS.js +0 -22
- package/dist/chunk-EMKJA2GJ.js +0 -89
- package/dist/chunk-FIPU4MLT.js +0 -21
- package/dist/chunk-FKRDCWBF.js +0 -22
- package/dist/chunk-HP34YGGJ.js +0 -22
- package/dist/chunk-IKHPGFCW.js +0 -14
- package/dist/chunk-IUYZQTJV.js +0 -30
- package/dist/chunk-KRNOMBXQ.js +0 -22
- package/dist/chunk-KSWLO5ZU.js +0 -102
- package/dist/chunk-N4SX7TZT.js +0 -96
- 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
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
|
|
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
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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("
|
|
411
|
+
throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information from OAuth provider", "", {
|
|
412
|
+
cause: error
|
|
413
|
+
});
|
|
287
414
|
}
|
|
288
|
-
throw new OAuthProtocolError("
|
|
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
|
-
|
|
297
|
-
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION",
|
|
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
|
-
|
|
482
|
+
logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
|
|
483
|
+
throw new OAuthProtocolError("invalid_request", "Invalid access token response format");
|
|
322
484
|
}
|
|
323
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
413
|
-
|
|
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
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
|
|
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 {
|
|
3
|
-
import
|
|
2
|
+
import { k as OAuthProviderRecord } from '../../index-CSyIJmCM.js';
|
|
3
|
+
import 'zod';
|
|
4
4
|
import '../../schemas.js';
|
|
5
|
-
import '
|
|
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-
|
|
4
|
-
import "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
6
|
-
import "../../chunk-
|
|
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-
|
|
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
|
};
|