@aura-stack/auth 0.1.0 → 0.2.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/@types/index.d.ts +5 -5
- package/dist/@types/router.d.d.ts +5 -5
- package/dist/@types/utility.d.ts +1 -5
- package/dist/actions/callback/access-token.cjs +95 -74
- package/dist/actions/callback/access-token.d.ts +7 -7
- package/dist/actions/callback/access-token.js +5 -3
- package/dist/actions/callback/callback.cjs +171 -249
- package/dist/actions/callback/callback.d.ts +6 -6
- package/dist/actions/callback/callback.js +10 -10
- package/dist/actions/callback/userinfo.cjs +81 -57
- package/dist/actions/callback/userinfo.d.ts +5 -5
- package/dist/actions/callback/userinfo.js +7 -5
- package/dist/actions/csrfToken/csrfToken.cjs +36 -105
- package/dist/actions/csrfToken/csrfToken.js +6 -6
- package/dist/actions/index.cjs +309 -444
- package/dist/actions/index.d.ts +5 -5
- package/dist/actions/index.js +19 -19
- package/dist/actions/session/session.cjs +25 -109
- package/dist/actions/session/session.js +4 -5
- package/dist/actions/signIn/authorization.cjs +96 -87
- package/dist/actions/signIn/authorization.d.ts +5 -5
- package/dist/actions/signIn/authorization.js +5 -5
- package/dist/actions/signIn/signIn.cjs +133 -242
- package/dist/actions/signIn/signIn.d.ts +6 -6
- package/dist/actions/signIn/signIn.js +8 -9
- package/dist/actions/signOut/signOut.cjs +136 -282
- package/dist/actions/signOut/signOut.js +8 -9
- package/dist/assert.cjs +5 -0
- package/dist/assert.d.ts +10 -1
- package/dist/assert.js +3 -1
- package/dist/chunk-3EUWD5BB.js +63 -0
- package/dist/chunk-6R2YZ4AC.js +22 -0
- package/dist/chunk-A3N4PVAT.js +70 -0
- package/dist/chunk-B737EUJV.js +22 -0
- package/dist/{chunk-256KIVJL.js → chunk-CXLATHS5.js} +53 -9
- package/dist/{chunk-6SM22VVJ.js → chunk-EIL2FPSS.js} +5 -1
- package/dist/{chunk-VFTYH33W.js → chunk-EMKJA2GJ.js} +36 -8
- package/dist/{chunk-UJJ7R56J.js → chunk-GA2SMTJO.js} +16 -10
- package/dist/chunk-HP34YGGJ.js +22 -0
- package/dist/chunk-HT4YLL7N.js +35 -0
- package/dist/{chunk-EBPE35JT.js → chunk-IUYZQTJV.js} +0 -1
- package/dist/{chunk-RLT4RFKV.js → chunk-IVET23KF.js} +21 -8
- package/dist/{chunk-XXJKNKGQ.js → chunk-JVFTCTTE.js} +9 -13
- package/dist/chunk-KSWLO5ZU.js +102 -0
- package/dist/{chunk-GZU3RBTB.js → chunk-N2APGLXA.js} +19 -10
- package/dist/{chunk-CAKJT3KS.js → chunk-N4SX7TZT.js} +21 -17
- package/dist/chunk-RRLIF4PQ.js +55 -0
- package/dist/chunk-TLE4PXY3.js +39 -0
- package/dist/chunk-W6LG7BFW.js +197 -0
- package/dist/{chunk-HMRKN75I.js → chunk-YRCB5FLE.js} +14 -9
- package/dist/chunk-ZNCZVF6U.js +14 -0
- package/dist/cookie.cjs +140 -99
- package/dist/cookie.d.ts +35 -44
- package/dist/cookie.js +10 -17
- package/dist/errors.cjs +85 -0
- package/dist/errors.d.ts +49 -0
- package/dist/errors.js +18 -0
- package/dist/{index-DpfbvTZ_.d.ts → index-DkaLJFn8.d.ts} +192 -61
- package/dist/index.cjs +543 -443
- package/dist/index.d.ts +6 -6
- package/dist/index.js +42 -28
- package/dist/jose.cjs +25 -14
- package/dist/jose.d.ts +4 -1
- package/dist/jose.js +5 -4
- package/dist/oauth/bitbucket.d.ts +5 -5
- package/dist/oauth/discord.cjs +0 -1
- package/dist/oauth/discord.d.ts +5 -5
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/figma.d.ts +5 -5
- package/dist/oauth/github.d.ts +5 -5
- package/dist/oauth/gitlab.d.ts +5 -5
- package/dist/oauth/index.cjs +176 -6
- package/dist/oauth/index.d.ts +5 -5
- package/dist/oauth/index.js +19 -4
- package/dist/oauth/mailchimp.cjs +46 -0
- package/dist/oauth/mailchimp.d.ts +7 -0
- package/dist/oauth/mailchimp.js +6 -0
- package/dist/oauth/pinterest.cjs +46 -0
- package/dist/oauth/pinterest.d.ts +7 -0
- package/dist/oauth/pinterest.js +6 -0
- package/dist/oauth/spotify.d.ts +5 -5
- package/dist/oauth/strava.cjs +46 -0
- package/dist/oauth/strava.d.ts +7 -0
- package/dist/oauth/strava.js +6 -0
- package/dist/oauth/x.d.ts +5 -5
- package/dist/{response.cjs → request.cjs} +14 -10
- package/dist/request.d.ts +13 -0
- package/dist/request.js +6 -0
- package/dist/schemas.cjs +43 -37
- package/dist/schemas.d.ts +67 -64
- package/dist/schemas.js +3 -1
- package/dist/secure.cjs +32 -24
- package/dist/secure.d.ts +5 -5
- 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 +8 -7
- 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
|
@@ -33,56 +33,28 @@ __export(signOut_exports, {
|
|
|
33
33
|
signOutAction: () => signOutAction
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(signOut_exports);
|
|
36
|
-
var
|
|
36
|
+
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_crypto = __toESM(require("crypto"), 1);
|
|
41
41
|
|
|
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
|
-
if (!
|
|
148
|
-
throw new
|
|
135
|
+
if (!import_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
|
|
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,46 +149,49 @@ var cacheControl = {
|
|
|
161
149
|
Vary: "Cookie"
|
|
162
150
|
};
|
|
163
151
|
|
|
164
|
-
// src/
|
|
165
|
-
var
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
152
|
+
// src/cookie.ts
|
|
153
|
+
var import_cookie = require("@aura-stack/router/cookie");
|
|
154
|
+
var defaultCookieOptions = {
|
|
155
|
+
httpOnly: true,
|
|
156
|
+
sameSite: "lax",
|
|
157
|
+
path: "/",
|
|
158
|
+
maxAge: 60 * 60 * 24 * 15
|
|
169
159
|
};
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
160
|
+
var oauthCookieOptions = {
|
|
161
|
+
httpOnly: true,
|
|
162
|
+
maxAge: 5 * 60,
|
|
163
|
+
sameSite: "lax",
|
|
164
|
+
expires: new Date(Date.now() + 5 * 60 * 1e3)
|
|
174
165
|
};
|
|
175
|
-
var
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
166
|
+
var expiredCookieAttributes = {
|
|
167
|
+
...defaultCookieOptions,
|
|
168
|
+
expires: /* @__PURE__ */ new Date(0),
|
|
169
|
+
maxAge: 0
|
|
179
170
|
};
|
|
180
171
|
|
|
181
172
|
// src/schemas.ts
|
|
182
|
-
var
|
|
183
|
-
var OAuthProviderConfigSchema = (0,
|
|
184
|
-
authorizeURL: (0,
|
|
185
|
-
accessToken: (0,
|
|
186
|
-
scope: (0,
|
|
187
|
-
userInfo: (0,
|
|
188
|
-
responseType: (0,
|
|
189
|
-
clientId: (0,
|
|
190
|
-
clientSecret: (0,
|
|
173
|
+
var import_zod = require("zod");
|
|
174
|
+
var OAuthProviderConfigSchema = (0, import_zod.object)({
|
|
175
|
+
authorizeURL: (0, import_zod.string)().url(),
|
|
176
|
+
accessToken: (0, import_zod.string)().url(),
|
|
177
|
+
scope: (0, import_zod.string)().optional(),
|
|
178
|
+
userInfo: (0, import_zod.string)().url(),
|
|
179
|
+
responseType: (0, import_zod.enum)(["code", "token", "id_token"]),
|
|
180
|
+
clientId: (0, import_zod.string)(),
|
|
181
|
+
clientSecret: (0, import_zod.string)()
|
|
191
182
|
});
|
|
192
183
|
var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
193
|
-
redirectURI: (0,
|
|
194
|
-
state: (0,
|
|
195
|
-
codeChallenge: (0,
|
|
196
|
-
codeChallengeMethod: (0,
|
|
184
|
+
redirectURI: (0, import_zod.string)(),
|
|
185
|
+
state: (0, import_zod.string)(),
|
|
186
|
+
codeChallenge: (0, import_zod.string)(),
|
|
187
|
+
codeChallengeMethod: (0, import_zod.enum)(["plain", "S256"])
|
|
197
188
|
});
|
|
198
|
-
var OAuthAuthorizationResponse = (0,
|
|
199
|
-
state: (0,
|
|
200
|
-
code: (0,
|
|
189
|
+
var OAuthAuthorizationResponse = (0, import_zod.object)({
|
|
190
|
+
state: (0, import_zod.string)({ message: "Missing state parameter in the OAuth authorization response." }),
|
|
191
|
+
code: (0, import_zod.string)({ message: "Missing code parameter in the OAuth authorization response." })
|
|
201
192
|
});
|
|
202
|
-
var OAuthAuthorizationErrorResponse = (0,
|
|
203
|
-
error: (0,
|
|
193
|
+
var OAuthAuthorizationErrorResponse = (0, import_zod.object)({
|
|
194
|
+
error: (0, import_zod.enum)([
|
|
204
195
|
"invalid_request",
|
|
205
196
|
"unauthorized_client",
|
|
206
197
|
"access_denied",
|
|
@@ -209,24 +200,24 @@ var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
|
209
200
|
"server_error",
|
|
210
201
|
"temporarily_unavailable"
|
|
211
202
|
]),
|
|
212
|
-
error_description: (0,
|
|
213
|
-
error_uri: (0,
|
|
214
|
-
state: (0,
|
|
203
|
+
error_description: (0, import_zod.string)().optional(),
|
|
204
|
+
error_uri: (0, import_zod.string)().optional(),
|
|
205
|
+
state: (0, import_zod.string)()
|
|
215
206
|
});
|
|
216
207
|
var OAuthAccessToken = OAuthProviderConfigSchema.extend({
|
|
217
|
-
redirectURI: (0,
|
|
218
|
-
code: (0,
|
|
219
|
-
codeVerifier: (0,
|
|
208
|
+
redirectURI: (0, import_zod.string)(),
|
|
209
|
+
code: (0, import_zod.string)(),
|
|
210
|
+
codeVerifier: (0, import_zod.string)().min(43).max(128)
|
|
220
211
|
});
|
|
221
|
-
var OAuthAccessTokenResponse = (0,
|
|
222
|
-
access_token: (0,
|
|
223
|
-
token_type: (0,
|
|
224
|
-
expires_in: (0,
|
|
225
|
-
refresh_token: (0,
|
|
226
|
-
scope: (0,
|
|
212
|
+
var OAuthAccessTokenResponse = (0, import_zod.object)({
|
|
213
|
+
access_token: (0, import_zod.string)(),
|
|
214
|
+
token_type: (0, import_zod.string)().optional(),
|
|
215
|
+
expires_in: (0, import_zod.number)().optional(),
|
|
216
|
+
refresh_token: (0, import_zod.string)().optional(),
|
|
217
|
+
scope: (0, import_zod.string)().optional().or((0, import_zod.null)())
|
|
227
218
|
});
|
|
228
|
-
var OAuthAccessTokenErrorResponse = (0,
|
|
229
|
-
error: (0,
|
|
219
|
+
var OAuthAccessTokenErrorResponse = (0, import_zod.object)({
|
|
220
|
+
error: (0, import_zod.enum)([
|
|
230
221
|
"invalid_request",
|
|
231
222
|
"invalid_client",
|
|
232
223
|
"invalid_grant",
|
|
@@ -234,12 +225,16 @@ var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
|
|
|
234
225
|
"unsupported_grant_type",
|
|
235
226
|
"invalid_scope"
|
|
236
227
|
]),
|
|
237
|
-
error_description: (0,
|
|
238
|
-
error_uri: (0,
|
|
228
|
+
error_description: (0, import_zod.string)().optional(),
|
|
229
|
+
error_uri: (0, import_zod.string)().optional()
|
|
239
230
|
});
|
|
240
|
-
var OAuthErrorResponse = (0,
|
|
241
|
-
error: (0,
|
|
242
|
-
error_description: (0,
|
|
231
|
+
var OAuthErrorResponse = (0, import_zod.object)({
|
|
232
|
+
error: (0, import_zod.string)(),
|
|
233
|
+
error_description: (0, import_zod.string)().optional()
|
|
234
|
+
});
|
|
235
|
+
var OAuthEnvSchema = (0, import_zod.object)({
|
|
236
|
+
clientId: import_zod.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
237
|
+
clientSecret: import_zod.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
243
238
|
});
|
|
244
239
|
|
|
245
240
|
// src/actions/signIn/authorization.ts
|
|
@@ -265,15 +260,18 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
265
260
|
}
|
|
266
261
|
const redirectToURL = new URL(sanitizeURL(getNormalizedOriginPath(redirectTo)));
|
|
267
262
|
if (!isValidURL(redirectTo) || !equals(redirectToURL.origin, hostedURL.origin)) {
|
|
268
|
-
throw new
|
|
263
|
+
throw new AuthSecurityError(
|
|
264
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
265
|
+
"The redirectTo parameter does not match the hosted origin."
|
|
266
|
+
);
|
|
269
267
|
}
|
|
270
268
|
return sanitizeURL(redirectToURL.pathname);
|
|
271
269
|
}
|
|
272
270
|
if (referer) {
|
|
273
271
|
const refererURL = new URL(sanitizeURL(referer));
|
|
274
272
|
if (!isValidURL(referer) || !equals(refererURL.origin, hostedURL.origin)) {
|
|
275
|
-
throw new
|
|
276
|
-
|
|
273
|
+
throw new AuthSecurityError(
|
|
274
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
277
275
|
"The referer of the request does not match the hosted origin."
|
|
278
276
|
);
|
|
279
277
|
}
|
|
@@ -282,133 +280,25 @@ var createRedirectTo = (request, redirectTo, trustedProxyHeaders) => {
|
|
|
282
280
|
if (origin) {
|
|
283
281
|
const originURL = new URL(sanitizeURL(getNormalizedOriginPath(origin)));
|
|
284
282
|
if (!isValidURL(origin) || !equals(originURL.origin, hostedURL.origin)) {
|
|
285
|
-
throw new
|
|
283
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
286
284
|
}
|
|
287
285
|
return sanitizeURL(originURL.pathname);
|
|
288
286
|
}
|
|
289
287
|
return "/";
|
|
290
288
|
} catch (error) {
|
|
291
|
-
if (
|
|
289
|
+
if (isAuthSecurityError(error)) {
|
|
292
290
|
throw error;
|
|
293
291
|
}
|
|
294
|
-
throw new
|
|
295
|
-
}
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
// src/cookie.ts
|
|
299
|
-
var import_cookie = require("cookie");
|
|
300
|
-
var import_cookie2 = require("cookie");
|
|
301
|
-
var COOKIE_NAME = "aura-auth";
|
|
302
|
-
var defaultCookieOptions = {
|
|
303
|
-
httpOnly: true,
|
|
304
|
-
sameSite: "lax",
|
|
305
|
-
path: "/",
|
|
306
|
-
maxAge: 60 * 60 * 24 * 15
|
|
307
|
-
};
|
|
308
|
-
var defaultStandardCookieConfig = {
|
|
309
|
-
secure: false,
|
|
310
|
-
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
|
|
322
|
-
};
|
|
323
|
-
var expiredCookieOptions = {
|
|
324
|
-
...defaultCookieOptions,
|
|
325
|
-
expires: /* @__PURE__ */ new Date(0),
|
|
326
|
-
maxAge: 0
|
|
327
|
-
};
|
|
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");
|
|
292
|
+
throw new AuthSecurityError("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", "Invalid origin (potential CSRF).");
|
|
351
293
|
}
|
|
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
294
|
};
|
|
405
295
|
|
|
406
296
|
// src/actions/signOut/signOut.ts
|
|
407
297
|
var config = (0, import_router2.createEndpointConfig)({
|
|
408
298
|
schemas: {
|
|
409
|
-
searchParams:
|
|
410
|
-
token_type_hint:
|
|
411
|
-
redirectTo:
|
|
299
|
+
searchParams: import_zod2.z.object({
|
|
300
|
+
token_type_hint: import_zod2.z.literal("session_token"),
|
|
301
|
+
redirectTo: import_zod2.z.string().optional()
|
|
412
302
|
})
|
|
413
303
|
}
|
|
414
304
|
});
|
|
@@ -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
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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.");
|
|
320
|
+
}
|
|
321
|
+
if (!csrfToken) {
|
|
322
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
483
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-A3N4PVAT.js";
|
|
4
|
+
import "../../chunk-N4SX7TZT.js";
|
|
5
|
+
import "../../chunk-W6LG7BFW.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-N2APGLXA.js";
|
|
8
|
+
import "../../chunk-CXLATHS5.js";
|
|
9
|
+
import "../../chunk-EIL2FPSS.js";
|
|
10
|
+
import "../../chunk-RRLIF4PQ.js";
|
|
11
|
+
import "../../chunk-YRCB5FLE.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,14 @@
|
|
|
1
|
+
import { J as JWTPayloadWithToken } from './index-DkaLJFn8.js';
|
|
2
|
+
import 'zod';
|
|
3
|
+
import './schemas.js';
|
|
4
|
+
import '@aura-stack/router/cookie';
|
|
5
|
+
import '@aura-stack/jose';
|
|
6
|
+
import '@aura-stack/jose/jose';
|
|
7
|
+
import './@types/utility.js';
|
|
8
|
+
|
|
1
9
|
declare const isFalsy: (value: unknown) => boolean;
|
|
2
10
|
declare const isRequest: (value: unknown) => value is Request;
|
|
3
11
|
declare const isValidURL: (value: string) => boolean;
|
|
12
|
+
declare const isJWTPayloadWithToken: (payload: unknown) => payload is JWTPayloadWithToken;
|
|
4
13
|
|
|
5
|
-
export { isFalsy, isRequest, isValidURL };
|
|
14
|
+
export { isFalsy, isJWTPayloadWithToken, isRequest, isValidURL };
|