@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.
- package/dist/@types/index.d.ts +3 -4
- package/dist/@types/router.d.d.ts +3 -4
- package/dist/@types/utility.d.ts +1 -5
- package/dist/actions/callback/access-token.cjs +51 -41
- package/dist/actions/callback/access-token.d.ts +3 -4
- package/dist/actions/callback/access-token.js +4 -3
- package/dist/actions/callback/callback.cjs +115 -210
- package/dist/actions/callback/callback.d.ts +3 -4
- package/dist/actions/callback/callback.js +9 -10
- package/dist/actions/callback/userinfo.cjs +35 -22
- package/dist/actions/callback/userinfo.d.ts +3 -4
- package/dist/actions/callback/userinfo.js +6 -5
- package/dist/actions/csrfToken/csrfToken.cjs +34 -103
- package/dist/actions/csrfToken/csrfToken.js +6 -6
- package/dist/actions/index.cjs +234 -391
- package/dist/actions/index.d.ts +3 -4
- package/dist/actions/index.js +16 -17
- package/dist/actions/session/session.cjs +25 -109
- package/dist/actions/session/session.js +4 -5
- package/dist/actions/signIn/authorization.cjs +64 -55
- package/dist/actions/signIn/authorization.d.ts +3 -4
- package/dist/actions/signIn/authorization.js +5 -5
- package/dist/actions/signIn/signIn.cjs +84 -206
- package/dist/actions/signIn/signIn.d.ts +3 -4
- package/dist/actions/signIn/signIn.js +7 -9
- package/dist/actions/signOut/signOut.cjs +88 -234
- package/dist/actions/signOut/signOut.js +8 -9
- package/dist/assert.cjs +5 -0
- package/dist/assert.d.ts +9 -1
- package/dist/assert.js +3 -1
- package/dist/chunk-2RXNXMCZ.js +55 -0
- package/dist/{chunk-UJJ7R56J.js → chunk-4V4JNXVF.js} +13 -10
- package/dist/chunk-6R2YZ4AC.js +22 -0
- package/dist/{chunk-VFTYH33W.js → chunk-7H3OR6UU.js} +29 -9
- package/dist/{chunk-256KIVJL.js → chunk-CXLATHS5.js} +53 -9
- package/dist/{chunk-6SM22VVJ.js → chunk-EIL2FPSS.js} +5 -1
- package/dist/chunk-IMICRJ5U.js +197 -0
- package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
- package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
- package/dist/chunk-NEVKX6K2.js +70 -0
- package/dist/{chunk-XXJKNKGQ.js → chunk-PTJUYB33.js} +9 -13
- package/dist/chunk-QDO2KSRJ.js +35 -0
- package/dist/{chunk-CAKJT3KS.js → chunk-QEZL7EYN.js} +21 -17
- package/dist/chunk-RRLIF4PQ.js +55 -0
- package/dist/chunk-TLE4PXY3.js +39 -0
- package/dist/chunk-UEH3LVON.js +97 -0
- package/dist/{chunk-HMRKN75I.js → chunk-WD7AUHQ5.js} +12 -7
- package/dist/{chunk-RLT4RFKV.js → chunk-ZLR3LI6X.js} +19 -9
- package/dist/cookie.cjs +140 -99
- package/dist/cookie.d.ts +33 -43
- package/dist/cookie.js +10 -17
- package/dist/errors.cjs +85 -0
- package/dist/errors.d.ts +48 -0
- package/dist/errors.js +18 -0
- package/dist/{index-DpfbvTZ_.d.ts → index-EqsoyjrF.d.ts} +139 -57
- package/dist/index.cjs +427 -389
- package/dist/index.d.ts +4 -5
- package/dist/index.js +37 -26
- package/dist/jose.cjs +23 -12
- package/dist/jose.d.ts +4 -1
- package/dist/jose.js +5 -4
- package/dist/oauth/bitbucket.d.ts +3 -4
- package/dist/oauth/discord.cjs +0 -1
- package/dist/oauth/discord.d.ts +3 -4
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/figma.d.ts +3 -4
- package/dist/oauth/github.d.ts +3 -4
- package/dist/oauth/gitlab.d.ts +3 -4
- package/dist/oauth/index.cjs +132 -6
- package/dist/oauth/index.d.ts +3 -4
- package/dist/oauth/index.js +12 -5
- package/dist/oauth/spotify.d.ts +3 -4
- package/dist/{response.cjs → oauth/strava.cjs} +21 -9
- package/dist/oauth/strava.d.ts +6 -0
- package/dist/oauth/strava.js +6 -0
- package/dist/oauth/x.d.ts +3 -4
- package/dist/schemas.cjs +11 -5
- package/dist/schemas.d.ts +70 -67
- package/dist/schemas.js +3 -1
- package/dist/secure.cjs +27 -19
- package/dist/secure.d.ts +3 -4
- package/dist/secure.js +4 -3
- package/dist/utils.cjs +90 -15
- package/dist/utils.d.ts +11 -2
- package/dist/utils.js +8 -4
- package/package.json +5 -6
- package/dist/chunk-FJUDBLCP.js +0 -59
- package/dist/chunk-HGJ4TXY4.js +0 -137
- package/dist/chunk-JAPMIE6S.js +0 -10
- package/dist/chunk-LLR722CL.js +0 -96
- package/dist/chunk-SJPDVKUS.js +0 -112
- package/dist/chunk-SMQO5WD7.js +0 -30
- package/dist/chunk-UTDLUEEG.js +0 -31
- package/dist/chunk-ZV4BH47P.js +0 -154
- package/dist/error.cjs +0 -88
- package/dist/error.d.ts +0 -62
- package/dist/error.js +0 -16
- package/dist/response.d.ts +0 -10
- 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/
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
54
|
-
|
|
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 = (
|
|
65
|
+
var sanitizeURL = (url) => {
|
|
94
66
|
try {
|
|
95
|
-
let decodedURL = decodeURIComponent(
|
|
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
|
|
95
|
+
return url.trim();
|
|
124
96
|
}
|
|
125
97
|
};
|
|
126
98
|
var getNormalizedOriginPath = (path) => {
|
|
127
99
|
try {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return `${
|
|
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
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
185
|
-
accessToken: (0, import_v4.
|
|
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.
|
|
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
|
|
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
|
|
276
|
-
|
|
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
|
|
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 (
|
|
269
|
+
if (isAuthSecurityError(error)) {
|
|
292
270
|
throw error;
|
|
293
271
|
}
|
|
294
|
-
throw new
|
|
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
|
|
309
|
-
secure: false,
|
|
284
|
+
var oauthCookieOptions = {
|
|
310
285
|
httpOnly: true,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
|
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: {
|
|
313
|
+
context: { jose, cookies }
|
|
424
314
|
} = ctx;
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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-
|
|
4
|
-
import "../../chunk-
|
|
5
|
-
import "../../chunk-
|
|
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-
|
|
9
|
-
import "../../chunk-
|
|
10
|
-
import "../../chunk-
|
|
11
|
-
import "../../chunk-
|
|
12
|
-
import "../../chunk-
|
|
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
|
@@ -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-
|
|
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
|
-
|
|
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
|
|
43
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid access token response format");
|
|
41
44
|
}
|
|
42
|
-
throw new
|
|
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
|
|
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
|
+
};
|