@aura-stack/auth 0.4.0-rc.5 → 0.5.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 +8 -3
- package/dist/@types/router.d.cjs +0 -17
- package/dist/@types/router.d.d.ts +7 -2
- package/dist/@types/router.d.js +0 -1
- package/dist/actions/callback/access-token.cjs +130 -71
- package/dist/actions/callback/access-token.d.ts +9 -4
- package/dist/actions/callback/access-token.js +3 -4
- package/dist/actions/callback/callback.cjs +428 -152
- package/dist/actions/callback/callback.d.ts +11 -3
- package/dist/actions/callback/callback.js +12 -10
- package/dist/actions/callback/userinfo.cjs +159 -65
- package/dist/actions/callback/userinfo.d.ts +8 -3
- package/dist/actions/callback/userinfo.js +7 -6
- package/dist/actions/csrfToken/csrfToken.cjs +70 -19
- package/dist/actions/csrfToken/csrfToken.js +8 -7
- package/dist/actions/index.cjs +780 -348
- package/dist/actions/index.d.ts +6 -2
- package/dist/actions/index.js +23 -18
- package/dist/actions/session/session.cjs +107 -26
- package/dist/actions/session/session.js +7 -5
- package/dist/actions/signIn/authorization-url.cjs +288 -0
- package/dist/actions/signIn/authorization-url.d.ts +31 -0
- package/dist/actions/signIn/authorization-url.js +16 -0
- package/dist/actions/signIn/authorization.cjs +209 -211
- package/dist/actions/signIn/authorization.d.ts +32 -21
- package/dist/actions/signIn/authorization.js +12 -9
- package/dist/actions/signIn/signIn.cjs +470 -235
- package/dist/actions/signIn/signIn.d.ts +12 -3
- package/dist/actions/signIn/signIn.js +11 -8
- package/dist/actions/signOut/signOut.cjs +376 -228
- package/dist/actions/signOut/signOut.d.ts +1 -1
- package/dist/actions/signOut/signOut.js +10 -9
- package/dist/api/createApi.cjs +750 -0
- package/dist/api/createApi.d.ts +12 -0
- package/dist/api/createApi.js +19 -0
- package/dist/api/getSession.cjs +141 -0
- package/dist/api/getSession.d.ts +16 -0
- package/dist/api/getSession.js +10 -0
- package/dist/api/signIn.cjs +549 -0
- package/dist/api/signIn.d.ts +26 -0
- package/dist/api/signIn.js +15 -0
- package/dist/api/signOut.cjs +279 -0
- package/dist/api/signOut.d.ts +16 -0
- package/dist/api/signOut.js +13 -0
- package/dist/assert.cjs +150 -5
- package/dist/assert.d.ts +26 -3
- package/dist/assert.js +17 -3
- package/dist/{chunk-YRCB5FLE.js → chunk-2A5B7GWR.js} +52 -6
- package/dist/chunk-2GQLSIJ2.js +40 -0
- package/dist/chunk-2IR674WX.js +44 -0
- package/dist/chunk-3J5TUH2I.js +50 -0
- package/dist/chunk-4RWSYUKX.js +98 -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-7BE46WWS.js +88 -0
- package/dist/chunk-7YYXFKLR.js +35 -0
- package/dist/chunk-C3A37LQC.js +33 -0
- package/dist/chunk-CITNGXDA.js +31 -0
- package/dist/chunk-CWX724AG.js +78 -0
- package/dist/chunk-D2CSIUKP.js +74 -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-HT4YLL7N.js → chunk-FPCVZUVG.js} +10 -8
- package/dist/chunk-FW4W3REU.js +25 -0
- package/dist/chunk-GNNBM2WJ.js +83 -0
- package/dist/chunk-IPKO6UQN.js +25 -0
- package/dist/chunk-JOCGX3RP.js +59 -0
- package/dist/chunk-KBXWTD6E.js +94 -0
- package/dist/chunk-KMMAZFSJ.js +25 -0
- package/dist/chunk-LATR3NIV.js +117 -0
- package/dist/chunk-LAYPUDQF.js +39 -0
- package/dist/chunk-LDU7A2JE.js +25 -0
- package/dist/chunk-LX3TJ2TJ.js +294 -0
- package/dist/chunk-NHZBQNRR.js +143 -0
- package/dist/chunk-OVHNRULD.js +33 -0
- package/dist/chunk-PDP3PHB3.js +127 -0
- package/dist/chunk-PHYNROD4.js +47 -0
- package/dist/chunk-QQEKY4XP.js +29 -0
- package/dist/chunk-U4RK4LKJ.js +348 -0
- package/dist/{chunk-RRLIF4PQ.js → chunk-U5663F2U.js} +16 -1
- package/dist/chunk-UN7X6SU5.js +53 -0
- package/dist/chunk-UZQJJD6A.js +100 -0
- package/dist/chunk-V6LLEAR4.js +80 -0
- package/dist/chunk-WHNDRO3N.js +50 -0
- package/dist/{chunk-W6LG7BFW.js → chunk-XY5R3EHH.js} +30 -23
- package/dist/client/client.cjs +135 -0
- package/dist/client/client.d.ts +85 -0
- package/dist/client/client.js +9 -0
- package/dist/client/index.cjs +135 -0
- package/dist/client/index.d.ts +14 -0
- package/dist/client/index.js +10 -0
- package/dist/context.cjs +1237 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.js +28 -0
- package/dist/cookie.cjs +57 -22
- package/dist/cookie.d.ts +11 -6
- package/dist/cookie.js +3 -2
- package/dist/createAuth.cjs +2320 -0
- package/dist/createAuth.d.ts +12 -0
- package/dist/createAuth.js +48 -0
- package/dist/env.cjs +78 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.js +12 -0
- package/dist/errors.cjs +17 -0
- package/dist/errors.d.ts +15 -4
- package/dist/errors.js +5 -1
- package/dist/headers.cjs +28 -2
- package/dist/headers.d.ts +25 -1
- package/dist/headers.js +9 -3
- package/dist/index-_aXtxb_s.d.ts +1377 -0
- package/dist/index.cjs +1843 -610
- package/dist/index.d.ts +11 -92
- package/dist/index.js +53 -85
- package/dist/jose.cjs +113 -38
- package/dist/jose.d.ts +12 -23
- package/dist/jose.js +17 -7
- package/dist/logger.cjs +424 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.js +17 -0
- package/dist/oauth/atlassian.cjs +57 -0
- package/dist/oauth/atlassian.d.ts +12 -0
- package/dist/oauth/atlassian.js +6 -0
- package/dist/oauth/bitbucket.cjs +19 -15
- package/dist/oauth/bitbucket.d.ts +7 -2
- package/dist/oauth/bitbucket.js +1 -1
- package/dist/oauth/discord.cjs +27 -24
- package/dist/oauth/discord.d.ts +7 -2
- package/dist/oauth/discord.js +1 -1
- package/dist/oauth/dropbox.cjs +53 -0
- package/dist/oauth/dropbox.d.ts +12 -0
- package/dist/oauth/dropbox.js +6 -0
- package/dist/oauth/figma.cjs +19 -16
- package/dist/oauth/figma.d.ts +7 -2
- package/dist/oauth/figma.js +1 -1
- package/dist/oauth/github.cjs +19 -8
- package/dist/oauth/github.d.ts +7 -2
- package/dist/oauth/github.js +1 -1
- package/dist/oauth/gitlab.cjs +19 -16
- package/dist/oauth/gitlab.d.ts +7 -2
- package/dist/oauth/gitlab.js +1 -1
- package/dist/oauth/index.cjs +529 -239
- package/dist/oauth/index.d.ts +7 -2
- package/dist/oauth/index.js +39 -22
- package/dist/oauth/mailchimp.cjs +19 -16
- package/dist/oauth/mailchimp.d.ts +7 -2
- package/dist/oauth/mailchimp.js +1 -1
- package/dist/oauth/notion.cjs +131 -0
- package/dist/oauth/notion.d.ts +12 -0
- package/dist/oauth/notion.js +9 -0
- package/dist/oauth/pinterest.cjs +19 -16
- package/dist/oauth/pinterest.d.ts +7 -2
- package/dist/oauth/pinterest.js +1 -1
- package/dist/oauth/spotify.cjs +19 -16
- package/dist/oauth/spotify.d.ts +7 -2
- package/dist/oauth/spotify.js +1 -1
- package/dist/oauth/strava.cjs +19 -16
- package/dist/oauth/strava.d.ts +7 -2
- package/dist/oauth/strava.js +1 -1
- package/dist/oauth/twitch.cjs +95 -0
- package/dist/oauth/twitch.d.ts +12 -0
- package/dist/oauth/twitch.js +7 -0
- package/dist/oauth/x.cjs +19 -16
- package/dist/oauth/x.d.ts +7 -2
- package/dist/oauth/x.js +1 -1
- package/dist/schemas.cjs +89 -42
- package/dist/schemas.d.ts +114 -18
- package/dist/schemas.js +5 -3
- package/dist/secure.cjs +73 -31
- package/dist/secure.d.ts +11 -11
- package/dist/secure.js +7 -6
- package/dist/utils.cjs +203 -90
- package/dist/utils.d.ts +21 -40
- package/dist/utils.js +21 -12
- package/package.json +9 -6
- package/dist/chunk-3EUWD5BB.js +0 -63
- 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-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-GA2SMTJO.js +0 -58
- package/dist/chunk-HP34YGGJ.js +0 -22
- package/dist/chunk-IKHPGFCW.js +0 -14
- package/dist/chunk-IUYZQTJV.js +0 -30
- package/dist/chunk-IVET23KF.js +0 -58
- package/dist/chunk-JVFTCTTE.js +0 -33
- package/dist/chunk-KRNOMBXQ.js +0 -22
- package/dist/chunk-KSWLO5ZU.js +0 -102
- package/dist/chunk-N2APGLXA.js +0 -71
- package/dist/chunk-N4SX7TZT.js +0 -96
- package/dist/chunk-STHEPPUZ.js +0 -11
- package/dist/chunk-TLE4PXY3.js +0 -39
- package/dist/index-B8jeIElf.d.ts +0 -679
- /package/dist/{chunk-DIVDFNAP.js → chunk-5X7JZMEF.js} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __create = Object.create;
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
5
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
6
|
var __export = (target, all) => {
|
|
9
7
|
for (var name in all)
|
|
@@ -17,35 +15,69 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
17
15
|
}
|
|
18
16
|
return to;
|
|
19
17
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
19
|
|
|
30
20
|
// src/index.ts
|
|
31
21
|
var index_exports = {};
|
|
32
22
|
__export(index_exports, {
|
|
23
|
+
builtInOAuthProviders: () => builtInOAuthProviders,
|
|
33
24
|
createAuth: () => createAuth,
|
|
34
|
-
|
|
25
|
+
createAuthClient: () => createAuthClient,
|
|
26
|
+
createClient: () => createClient,
|
|
27
|
+
createSyslogMessage: () => createSyslogMessage
|
|
35
28
|
});
|
|
36
29
|
module.exports = __toCommonJS(index_exports);
|
|
37
|
-
var import_config2 = require("dotenv/config");
|
|
38
|
-
var import_router7 = require("@aura-stack/router");
|
|
39
30
|
|
|
40
|
-
// src/
|
|
41
|
-
var
|
|
42
|
-
var import_jose = require("@aura-stack/jose");
|
|
31
|
+
// src/createAuth.ts
|
|
32
|
+
var import_router9 = require("@aura-stack/router");
|
|
43
33
|
|
|
44
|
-
// src/
|
|
45
|
-
var
|
|
34
|
+
// src/env.ts
|
|
35
|
+
var import_meta = {};
|
|
36
|
+
var env = new Proxy({}, {
|
|
37
|
+
get(_, prop) {
|
|
38
|
+
if (typeof prop !== "string") return void 0;
|
|
39
|
+
const hasProperty = (process2) => {
|
|
40
|
+
return process2 && Object.prototype.hasOwnProperty.call(process2, prop);
|
|
41
|
+
};
|
|
42
|
+
try {
|
|
43
|
+
if (typeof process !== "undefined" && hasProperty(process.env)) {
|
|
44
|
+
return process.env[prop];
|
|
45
|
+
}
|
|
46
|
+
if (typeof import_meta !== "undefined" && hasProperty(import_meta.env)) {
|
|
47
|
+
return import_meta.env[prop];
|
|
48
|
+
}
|
|
49
|
+
if (typeof Deno !== "undefined" && Deno.env?.get) {
|
|
50
|
+
return Deno.env.get(prop);
|
|
51
|
+
}
|
|
52
|
+
if (typeof Bun !== "undefined" && hasProperty(Bun.env)) {
|
|
53
|
+
return Bun.env[prop];
|
|
54
|
+
}
|
|
55
|
+
const globalValue = globalThis[prop];
|
|
56
|
+
return typeof globalValue === "string" ? globalValue : void 0;
|
|
57
|
+
} catch {
|
|
58
|
+
return void 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
var getEnv = (key) => {
|
|
63
|
+
const keys = [`AURA_AUTH_${key.toUpperCase()}`, `AURA_${key.toUpperCase()}`, `AUTH_${key.toUpperCase()}`, key.toUpperCase()];
|
|
64
|
+
return env[keys.find((k) => env[k]) ?? ""];
|
|
65
|
+
};
|
|
66
|
+
var getEnvBoolean = (key) => {
|
|
67
|
+
const value = getEnv(key);
|
|
68
|
+
if (value === void 0) return false;
|
|
69
|
+
const normalized = value.trim().toLowerCase();
|
|
70
|
+
if (["1", "true", "yes", "on", "debug"].includes(normalized)) return true;
|
|
71
|
+
return false;
|
|
72
|
+
};
|
|
73
|
+
var getEnvArray = (key, defaultValue = []) => {
|
|
74
|
+
const value = getEnv(key);
|
|
75
|
+
if (!value) return defaultValue;
|
|
76
|
+
return value.split(/[,;\n]+/).map((v) => v.trim()).filter(Boolean);
|
|
77
|
+
};
|
|
46
78
|
|
|
47
|
-
// src/
|
|
48
|
-
var
|
|
79
|
+
// src/jose.ts
|
|
80
|
+
var import_jose = require("@aura-stack/jose");
|
|
49
81
|
|
|
50
82
|
// src/errors.ts
|
|
51
83
|
var OAuthProtocolError = class extends Error {
|
|
@@ -80,6 +112,16 @@ var AuthSecurityError = class extends Error {
|
|
|
80
112
|
Error.captureStackTrace(this, new.target);
|
|
81
113
|
}
|
|
82
114
|
};
|
|
115
|
+
var AuthClientError = class extends Error {
|
|
116
|
+
type = "AUTH_CLIENT_ERROR";
|
|
117
|
+
code;
|
|
118
|
+
constructor(code, message, options2) {
|
|
119
|
+
super(message, options2);
|
|
120
|
+
this.code = code;
|
|
121
|
+
this.name = new.target.name;
|
|
122
|
+
Error.captureStackTrace(this, new.target);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
83
125
|
var isNativeError = (error) => {
|
|
84
126
|
return error instanceof Error;
|
|
85
127
|
};
|
|
@@ -93,112 +135,268 @@ var isAuthSecurityError = (error) => {
|
|
|
93
135
|
return error instanceof AuthSecurityError;
|
|
94
136
|
};
|
|
95
137
|
|
|
138
|
+
// src/jose.ts
|
|
139
|
+
var import_jose2 = require("@aura-stack/jose/jose");
|
|
140
|
+
var import_crypto = require("@aura-stack/jose/crypto");
|
|
141
|
+
var createJoseInstance = (secret) => {
|
|
142
|
+
secret ??= getEnv("SECRET");
|
|
143
|
+
if (!secret) {
|
|
144
|
+
throw new AuthInternalError(
|
|
145
|
+
"JOSE_INITIALIZATION_FAILED",
|
|
146
|
+
"AURA_AUTH_SECRET environment variable is not set and no secret was provided."
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
const salt = getEnv("SALT");
|
|
150
|
+
if (!salt) {
|
|
151
|
+
throw new AuthInternalError(
|
|
152
|
+
"JOSE_INITIALIZATION_FAILED",
|
|
153
|
+
"AURA_AUTH_SALT or AUTH_SALT environment variable is not set. A salt value is required for key derivation."
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
(0, import_jose.createSecret)(salt);
|
|
158
|
+
} catch (error) {
|
|
159
|
+
throw new AuthInternalError(
|
|
160
|
+
"INVALID_SALT_SECRET_VALUE",
|
|
161
|
+
"AURA_AUTH_SALT/AUTH_SALT is invalid. It must be at least 32 bytes long and meet entropy requirements.",
|
|
162
|
+
{ cause: error }
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
const jose = (async () => {
|
|
166
|
+
const derivedSigningKey = await (0, import_jose.createDeriveKey)(secret, salt, "signing");
|
|
167
|
+
const derivedEncryptionKey = await (0, import_jose.createDeriveKey)(secret, salt, "encryption");
|
|
168
|
+
const derivedCsrfTokenKey = await (0, import_jose.createDeriveKey)(secret, salt, "csrfToken");
|
|
169
|
+
return {
|
|
170
|
+
jwt: (0, import_jose.createJWT)({ jws: derivedSigningKey, jwe: derivedEncryptionKey }),
|
|
171
|
+
jws: (0, import_jose.createJWS)(derivedCsrfTokenKey),
|
|
172
|
+
jwe: (0, import_jose.createJWE)(derivedEncryptionKey)
|
|
173
|
+
};
|
|
174
|
+
})();
|
|
175
|
+
jose.catch(() => {
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
decodeJWT: async (token, options2) => {
|
|
179
|
+
const { jwt } = await jose;
|
|
180
|
+
return jwt.decodeJWT(token, options2);
|
|
181
|
+
},
|
|
182
|
+
encodeJWT: async (payload) => {
|
|
183
|
+
const { jwt } = await jose;
|
|
184
|
+
return jwt.encodeJWT(payload);
|
|
185
|
+
},
|
|
186
|
+
signJWS: async (...args) => {
|
|
187
|
+
const { jws } = await jose;
|
|
188
|
+
return jws.signJWS(...args);
|
|
189
|
+
},
|
|
190
|
+
verifyJWS: async (...args) => {
|
|
191
|
+
const { jws } = await jose;
|
|
192
|
+
return jws.verifyJWS(...args);
|
|
193
|
+
},
|
|
194
|
+
encryptJWE: async (...args) => {
|
|
195
|
+
const { jwe } = await jose;
|
|
196
|
+
return jwe.encryptJWE(...args);
|
|
197
|
+
},
|
|
198
|
+
decryptJWE: async (...args) => {
|
|
199
|
+
const { jwe } = await jose;
|
|
200
|
+
return jwe.decryptJWE(...args);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
var jwtVerificationOptions = {
|
|
205
|
+
algorithms: ["HS256"],
|
|
206
|
+
typ: "JWT"
|
|
207
|
+
};
|
|
208
|
+
|
|
96
209
|
// src/utils.ts
|
|
97
|
-
var
|
|
98
|
-
|
|
210
|
+
var import_router = require("@aura-stack/router");
|
|
211
|
+
|
|
212
|
+
// src/assert.ts
|
|
213
|
+
var import_crypto2 = require("@aura-stack/jose/crypto");
|
|
214
|
+
var unsafeChars = [
|
|
215
|
+
"<",
|
|
216
|
+
">",
|
|
217
|
+
'"',
|
|
218
|
+
"`",
|
|
219
|
+
" ",
|
|
220
|
+
"\r",
|
|
221
|
+
"\n",
|
|
222
|
+
" ",
|
|
223
|
+
"\\",
|
|
224
|
+
"%2F",
|
|
225
|
+
"%5C",
|
|
226
|
+
"%2f",
|
|
227
|
+
"%5c",
|
|
228
|
+
"\r\n",
|
|
229
|
+
"%0A",
|
|
230
|
+
"%0D",
|
|
231
|
+
"%0a",
|
|
232
|
+
"%0d",
|
|
233
|
+
"..",
|
|
234
|
+
"//",
|
|
235
|
+
"///",
|
|
236
|
+
"...",
|
|
237
|
+
"%20",
|
|
238
|
+
"\0"
|
|
239
|
+
];
|
|
240
|
+
var isValidURL = (value) => {
|
|
241
|
+
if (!new RegExp(/^https?:\/\/[^/]/).test(value)) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
const match = value.match(/^(https?:\/\/)(.*)$/);
|
|
245
|
+
if (!match) return false;
|
|
246
|
+
const rest = match[2];
|
|
247
|
+
for (const char of unsafeChars) {
|
|
248
|
+
if (rest.includes(char)) return false;
|
|
249
|
+
}
|
|
250
|
+
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()?#*+,;=:@-]*)*\/?$/;
|
|
251
|
+
return regex.test(match[0]);
|
|
99
252
|
};
|
|
100
|
-
var
|
|
101
|
-
return
|
|
253
|
+
var isJWTPayloadWithToken = (payload) => {
|
|
254
|
+
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
102
255
|
};
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
256
|
+
var isRelativeURL = (value) => {
|
|
257
|
+
if (value.length > 100) return false;
|
|
258
|
+
for (const char of unsafeChars) {
|
|
259
|
+
if (value.includes(char)) return false;
|
|
260
|
+
}
|
|
261
|
+
const regex = /^\/[a-zA-Z0-9\-_\/.?&=#]*\/?$/;
|
|
262
|
+
return regex.test(value);
|
|
108
263
|
};
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
264
|
+
var isSameOrigin = (origin, expected) => {
|
|
265
|
+
const originURL = new URL(origin);
|
|
266
|
+
const expectedURL = new URL(expected);
|
|
267
|
+
return equals(originURL.origin, expectedURL.origin);
|
|
112
268
|
};
|
|
113
|
-
var
|
|
269
|
+
var patternToRegex = (pattern) => {
|
|
114
270
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (path !== "/" && path.endsWith("/")) {
|
|
129
|
-
path = path.replace(/\/+$/, "/");
|
|
130
|
-
} else if (path !== "/") {
|
|
131
|
-
path = path.replace(/\/+$/, "");
|
|
132
|
-
}
|
|
133
|
-
return protocol + domain + path;
|
|
134
|
-
}
|
|
135
|
-
let sanitized = decodedURL.replace(/\/\.\.\//g, "/").replace(/\/\.\.$/, "").replace(/\.{2,}/g, "").replace(/\/{2,}/g, "/");
|
|
136
|
-
if (sanitized !== "/" && sanitized.endsWith("/")) {
|
|
137
|
-
sanitized = sanitized.replace(/\/+$/, "/");
|
|
138
|
-
} else if (sanitized !== "/") {
|
|
139
|
-
sanitized = sanitized.replace(/\/+$/, "");
|
|
140
|
-
}
|
|
141
|
-
return sanitized;
|
|
271
|
+
if (pattern.length > 2048) return null;
|
|
272
|
+
pattern = pattern.replace(/\\/g, "");
|
|
273
|
+
const match = pattern.match(/^(https?):\/\/([a-zA-Z0-9.*-]{1,253})(?::(\d{1,5}|\*))?(?:\/.*)?$/);
|
|
274
|
+
if (!match) return null;
|
|
275
|
+
const [, protocol, host, port] = match;
|
|
276
|
+
const hasWildcard = host.includes("*");
|
|
277
|
+
if (hasWildcard && !host.startsWith("*.")) return null;
|
|
278
|
+
if (hasWildcard && host.slice(2).includes("*")) return null;
|
|
279
|
+
const domain = hasWildcard ? host.slice(2) : host;
|
|
280
|
+
const escapedDomain = domain.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
281
|
+
const hostRegex = hasWildcard ? `[^.]+\\.${escapedDomain}` : escapedDomain;
|
|
282
|
+
const portRegex = port === "*" ? ":\\d{1,5}" : port ? `:${port}` : "";
|
|
283
|
+
return new RegExp(`^${protocol}:\\/\\/${hostRegex}${portRegex}$`);
|
|
142
284
|
} catch {
|
|
143
|
-
return
|
|
285
|
+
return null;
|
|
144
286
|
}
|
|
145
287
|
};
|
|
146
|
-
var
|
|
147
|
-
if (!
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
if ((0, import_router.isInvalidZodSchemaError)(error)) {
|
|
160
|
-
return Response.json({ type: "ROUTER_ERROR", code: "INVALID_REQUEST", message: error.errors }, { status: 422 });
|
|
288
|
+
var isTrustedOrigin = (url, trustedOrigins) => {
|
|
289
|
+
if (!isValidURL(url) || trustedOrigins.length === 0) return false;
|
|
290
|
+
try {
|
|
291
|
+
const urlOrigin = new URL(url).origin;
|
|
292
|
+
for (const pattern of trustedOrigins) {
|
|
293
|
+
const regex = patternToRegex(pattern);
|
|
294
|
+
if (regex?.test(urlOrigin)) return true;
|
|
295
|
+
try {
|
|
296
|
+
if (isValidURL(pattern) && equals(new URL(pattern).origin, urlOrigin)) return true;
|
|
297
|
+
} catch {
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
} catch {
|
|
161
301
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
{ status: 400 }
|
|
172
|
-
);
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
304
|
+
var timingSafeEqual = (a, b) => {
|
|
305
|
+
const bufferA = import_crypto2.encoder.encode(a);
|
|
306
|
+
const bufferB = import_crypto2.encoder.encode(b);
|
|
307
|
+
const len = Math.max(bufferA.length, bufferB.length);
|
|
308
|
+
let diff = 0;
|
|
309
|
+
for (let i = 0; i < len; i++) {
|
|
310
|
+
diff |= (bufferA[i] ?? 0) ^ (bufferB[i] ?? 0);
|
|
173
311
|
}
|
|
174
|
-
|
|
175
|
-
|
|
312
|
+
return diff === 0 && bufferA.length === bufferB.length;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
// src/utils.ts
|
|
316
|
+
var AURA_AUTH_VERSION = "0.4.0";
|
|
317
|
+
var equals = (a, b) => {
|
|
318
|
+
if (a === null || b === null || a === void 0 || b === void 0) return false;
|
|
319
|
+
return a === b;
|
|
320
|
+
};
|
|
321
|
+
var createErrorHandler = (logger) => {
|
|
322
|
+
return (error) => {
|
|
323
|
+
if ((0, import_router.isRouterError)(error)) {
|
|
324
|
+
const { message, status, statusText } = error;
|
|
325
|
+
logger?.log("ROUTER_INTERNAL_ERROR");
|
|
326
|
+
return Response.json({ type: "ROUTER_ERROR", code: "ROUTER_INTERNAL_ERROR", message }, { status, statusText });
|
|
327
|
+
}
|
|
328
|
+
if ((0, import_router.isInvalidZodSchemaError)(error)) {
|
|
329
|
+
logger?.log("INVALID_REQUEST");
|
|
330
|
+
return Response.json({ type: "ROUTER_ERROR", code: "INVALID_REQUEST", message: error.errors }, { status: 422 });
|
|
331
|
+
}
|
|
332
|
+
if (isOAuthProtocolError(error)) {
|
|
333
|
+
const { error: errorCode, message, type, errorURI } = error;
|
|
334
|
+
logger?.log("OAUTH_PROTOCOL_ERROR", {
|
|
335
|
+
structuredData: {
|
|
336
|
+
error: errorCode,
|
|
337
|
+
error_description: message,
|
|
338
|
+
error_uri: errorURI ?? ""
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
return Response.json(
|
|
342
|
+
{
|
|
343
|
+
type,
|
|
344
|
+
message
|
|
345
|
+
},
|
|
346
|
+
{ status: 400 }
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (isAuthInternalError(error)) {
|
|
350
|
+
const { type, code, message } = error;
|
|
351
|
+
logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
352
|
+
structuredData: {
|
|
353
|
+
error: code,
|
|
354
|
+
error_description: message
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
return Response.json(
|
|
358
|
+
{
|
|
359
|
+
type,
|
|
360
|
+
message
|
|
361
|
+
},
|
|
362
|
+
{ status: 400 }
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
if (isAuthSecurityError(error)) {
|
|
366
|
+
const { type, code, message } = error;
|
|
367
|
+
logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
368
|
+
structuredData: {
|
|
369
|
+
error: code,
|
|
370
|
+
error_description: message
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
return Response.json(
|
|
374
|
+
{
|
|
375
|
+
type,
|
|
376
|
+
code,
|
|
377
|
+
message
|
|
378
|
+
},
|
|
379
|
+
{ status: 400 }
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
logger?.log("SERVER_ERROR");
|
|
176
383
|
return Response.json(
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
code,
|
|
180
|
-
message
|
|
181
|
-
},
|
|
182
|
-
{ status: 400 }
|
|
384
|
+
{ type: "SERVER_ERROR", code: "SERVER_ERROR", message: "An unexpected error occurred" },
|
|
385
|
+
{ status: 500 }
|
|
183
386
|
);
|
|
184
|
-
}
|
|
185
|
-
return Response.json({ type: "SERVER_ERROR", code: "server_error", message: "An unexpected error occurred" }, { status: 500 });
|
|
387
|
+
};
|
|
186
388
|
};
|
|
187
|
-
var
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
url.hash = "";
|
|
191
|
-
url.search = "";
|
|
192
|
-
return `${url.origin}${url.pathname}`;
|
|
193
|
-
} catch {
|
|
194
|
-
return sanitizeURL(path);
|
|
195
|
-
}
|
|
389
|
+
var getBaseURL = (request) => {
|
|
390
|
+
const url = new URL(request.url);
|
|
391
|
+
return `${url.origin}${url.pathname}`;
|
|
196
392
|
};
|
|
197
393
|
var toISOString = (date) => {
|
|
198
394
|
return new Date(date).toISOString();
|
|
199
395
|
};
|
|
200
396
|
var useSecureCookies = (request, trustedProxyHeaders) => {
|
|
201
|
-
|
|
397
|
+
const headers = request instanceof Headers ? request : request.headers;
|
|
398
|
+
const url = request instanceof Headers ? null : request.url;
|
|
399
|
+
return trustedProxyHeaders ? url?.startsWith("https://") || headers.get("X-Forwarded-Proto") === "https" || (headers.get("Forwarded")?.includes("proto=https") ?? false) : url?.startsWith("https://") ?? false;
|
|
202
400
|
};
|
|
203
401
|
var formatZodError = (error) => {
|
|
204
402
|
if (!error.issues || error.issues.length === 0) {
|
|
@@ -215,95 +413,370 @@ var formatZodError = (error) => {
|
|
|
215
413
|
};
|
|
216
414
|
}, {});
|
|
217
415
|
};
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
const regex = /^https?:\/\/(?:[a-zA-Z0-9._-]+|localhost|\[[0-9a-fA-F:]+\])(?::\d{1,5})?(?:\/[a-zA-Z0-9._~!$&'()*+,;=:@-]*)*\/?$/;
|
|
223
|
-
return regex.test(value);
|
|
224
|
-
};
|
|
225
|
-
var isJWTPayloadWithToken = (payload) => {
|
|
226
|
-
return typeof payload === "object" && payload !== null && "token" in payload && typeof payload?.token === "string";
|
|
416
|
+
var extractPath = (url) => {
|
|
417
|
+
const pathRegex = /^https?:\/\/[a-zA-Z0-9_\-\.]+(:\d+)?(\/.*)$/;
|
|
418
|
+
const match = url.match(pathRegex);
|
|
419
|
+
return match && match[2] ? match[2] : "/";
|
|
227
420
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
var createHash = (data, base = "hex") => {
|
|
234
|
-
return import_crypto.default.createHash("sha256").update(data).digest().toString(base);
|
|
421
|
+
var createStructuredData = (data, sdID = "metadata") => {
|
|
422
|
+
const entries = Object.entries(data);
|
|
423
|
+
if (entries.length === 0) return `[${sdID}]`;
|
|
424
|
+
const values = entries.map(([key, value]) => `${key}="${String(value).replace(/(["\\\]])/g, "\\$1")}"`).join(" ");
|
|
425
|
+
return `[${sdID} ${values}]`;
|
|
235
426
|
};
|
|
236
|
-
var
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
return { codeVerifier, codeChallenge, method: "S256" };
|
|
240
|
-
};
|
|
241
|
-
var createCSRF = async (jose, csrfCookie) => {
|
|
242
|
-
try {
|
|
243
|
-
const token = generateSecure(32);
|
|
244
|
-
if (csrfCookie) {
|
|
245
|
-
await jose.verifyJWS(csrfCookie);
|
|
246
|
-
return csrfCookie;
|
|
247
|
-
}
|
|
248
|
-
return jose.signJWS({ token });
|
|
249
|
-
} catch {
|
|
250
|
-
const token = generateSecure(32);
|
|
251
|
-
return jose.signJWS({ token });
|
|
427
|
+
var getErrorName = (error) => {
|
|
428
|
+
if (error instanceof Error) {
|
|
429
|
+
return error.name;
|
|
252
430
|
}
|
|
431
|
+
return typeof error === "string" ? error : "UnknownError";
|
|
253
432
|
};
|
|
254
|
-
var
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
260
|
-
}
|
|
261
|
-
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
262
|
-
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
263
|
-
}
|
|
264
|
-
const cookieBuffer = Buffer.from(cookiePayload.token);
|
|
265
|
-
const headerBuffer = Buffer.from(headerPayload.token);
|
|
266
|
-
if (!equals(headerBuffer.length, cookieBuffer.length)) {
|
|
267
|
-
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
268
|
-
}
|
|
269
|
-
if (!import_crypto.default.timingSafeEqual(cookieBuffer, headerBuffer)) {
|
|
270
|
-
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
271
|
-
}
|
|
272
|
-
return true;
|
|
273
|
-
} catch {
|
|
274
|
-
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
433
|
+
var createBasicAuthHeader = (username, password) => {
|
|
434
|
+
const getUsername = getEnv(username.toUpperCase()) ?? username;
|
|
435
|
+
const getPassword = getEnv(password.toUpperCase()) ?? password;
|
|
436
|
+
if (!getUsername || !getPassword) {
|
|
437
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "Missing client credentials for OAuth provider configuration.");
|
|
275
438
|
}
|
|
439
|
+
const credentials = `${getUsername}:${getPassword}`;
|
|
440
|
+
return `Basic ${btoa(credentials)}`;
|
|
276
441
|
};
|
|
277
|
-
var
|
|
278
|
-
|
|
442
|
+
var validateRedirectTo = (url) => {
|
|
443
|
+
if (!isRelativeURL(url) && !isValidURL(url)) return "/";
|
|
444
|
+
if (isRelativeURL(url)) return url;
|
|
445
|
+
return "/";
|
|
279
446
|
};
|
|
280
447
|
|
|
281
|
-
// src/
|
|
282
|
-
var
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
448
|
+
// src/logger.ts
|
|
449
|
+
var logMessages = {
|
|
450
|
+
ROUTER_INTERNAL_ERROR: {
|
|
451
|
+
facility: 10,
|
|
452
|
+
severity: "error",
|
|
453
|
+
msgId: "ROUTER_INTERNAL_ERROR",
|
|
454
|
+
message: "Unhandled router error while processing the request"
|
|
455
|
+
},
|
|
456
|
+
INVALID_REQUEST: {
|
|
457
|
+
facility: 10,
|
|
458
|
+
severity: "warning",
|
|
459
|
+
msgId: "INVALID_REQUEST",
|
|
460
|
+
message: "Request validation failed against the expected schema"
|
|
461
|
+
},
|
|
462
|
+
SERVER_ERROR: {
|
|
463
|
+
facility: 10,
|
|
464
|
+
severity: "error",
|
|
465
|
+
msgId: "SERVER_ERROR",
|
|
466
|
+
message: "Unexpected internal server error during authentication"
|
|
467
|
+
},
|
|
468
|
+
OAUTH_PROTOCOL_ERROR: {
|
|
469
|
+
facility: 10,
|
|
470
|
+
severity: "warning",
|
|
471
|
+
msgId: "OAUTH_PROTOCOL_ERROR",
|
|
472
|
+
message: "OAuth provider returned an invalid or unexpected protocol response"
|
|
473
|
+
},
|
|
474
|
+
OAUTH_AUTHORIZATION_ERROR: {
|
|
475
|
+
facility: 10,
|
|
476
|
+
severity: "error",
|
|
477
|
+
msgId: "OAUTH_AUTHORIZATION_ERROR",
|
|
478
|
+
message: "OAuth authorization request was rejected or failed"
|
|
479
|
+
},
|
|
480
|
+
INVALID_OAUTH_CONFIGURATION: {
|
|
481
|
+
facility: 10,
|
|
482
|
+
severity: "error",
|
|
483
|
+
msgId: "INVALID_OAUTH_CONFIGURATION",
|
|
484
|
+
message: "The OAuth provider configuration is invalid or incomplete"
|
|
485
|
+
},
|
|
486
|
+
OAUTH_ACCESS_TOKEN_REQUEST_INITIATED: {
|
|
487
|
+
facility: 10,
|
|
488
|
+
severity: "debug",
|
|
489
|
+
msgId: "OAUTH_ACCESS_TOKEN_REQUEST_INITIATED",
|
|
490
|
+
message: "Starting OAuth access token request to the provider"
|
|
491
|
+
},
|
|
492
|
+
INVALID_OAUTH_ACCESS_TOKEN_RESPONSE: {
|
|
493
|
+
facility: 10,
|
|
494
|
+
severity: "error",
|
|
495
|
+
msgId: "INVALID_OAUTH_ACCESS_TOKEN_RESPONSE",
|
|
496
|
+
message: "OAuth access token endpoint returned an invalid or malformed response"
|
|
497
|
+
},
|
|
498
|
+
OAUTH_ACCESS_TOKEN_ERROR: {
|
|
499
|
+
facility: 10,
|
|
500
|
+
severity: "error",
|
|
501
|
+
msgId: "OAUTH_ACCESS_TOKEN_ERROR",
|
|
502
|
+
message: "OAuth access token endpoint returned an error response"
|
|
503
|
+
},
|
|
504
|
+
OAUTH_ACCESS_TOKEN_SUCCESS: {
|
|
505
|
+
facility: 10,
|
|
506
|
+
severity: "info",
|
|
507
|
+
msgId: "OAUTH_ACCESS_TOKEN_SUCCESS",
|
|
508
|
+
message: "Successfully retrieved OAuth access token from the provider"
|
|
509
|
+
},
|
|
510
|
+
OAUTH_ACCESS_TOKEN_REQUEST_FAILED: {
|
|
511
|
+
facility: 10,
|
|
512
|
+
severity: "error",
|
|
513
|
+
msgId: "OAUTH_ACCESS_TOKEN_REQUEST_FAILED",
|
|
514
|
+
message: "Network or server error while requesting OAuth access token"
|
|
515
|
+
},
|
|
516
|
+
OAUTH_USERINFO_REQUEST_INITIATED: {
|
|
517
|
+
facility: 10,
|
|
518
|
+
severity: "debug",
|
|
519
|
+
msgId: "OAUTH_USERINFO_REQUEST_INITIATED",
|
|
520
|
+
message: "Starting OAuth userinfo request to the provider"
|
|
521
|
+
},
|
|
522
|
+
OAUTH_USERINFO_INVALID_RESPONSE: {
|
|
523
|
+
facility: 10,
|
|
524
|
+
severity: "error",
|
|
525
|
+
msgId: "OAUTH_USERINFO_INVALID_RESPONSE",
|
|
526
|
+
message: "OAuth userinfo endpoint returned an invalid or malformed response"
|
|
527
|
+
},
|
|
528
|
+
OAUTH_USERINFO_ERROR: {
|
|
529
|
+
facility: 10,
|
|
530
|
+
severity: "error",
|
|
531
|
+
msgId: "OAUTH_USERINFO_ERROR",
|
|
532
|
+
message: "OAuth userinfo endpoint returned an error response"
|
|
533
|
+
},
|
|
534
|
+
OAUTH_USERINFO_SUCCESS: {
|
|
535
|
+
facility: 10,
|
|
536
|
+
severity: "info",
|
|
537
|
+
msgId: "OAUTH_USERINFO_SUCCESS",
|
|
538
|
+
message: "Successfully retrieved user information from the OAuth provider"
|
|
539
|
+
},
|
|
540
|
+
OAUTH_USERINFO_REQUEST_FAILED: {
|
|
541
|
+
facility: 10,
|
|
542
|
+
severity: "error",
|
|
543
|
+
msgId: "OAUTH_USERINFO_REQUEST_FAILED",
|
|
544
|
+
message: "Network or server error while requesting user information from the OAuth provider"
|
|
545
|
+
},
|
|
546
|
+
OAUTH_CALLBACK_SUCCESS: {
|
|
547
|
+
facility: 4,
|
|
548
|
+
severity: "info",
|
|
549
|
+
msgId: "OAUTH_CALLBACK_SUCCESS",
|
|
550
|
+
message: "OAuth callback completed successfully and session was created"
|
|
551
|
+
},
|
|
552
|
+
MISMATCHING_STATE: {
|
|
553
|
+
facility: 4,
|
|
554
|
+
severity: "critical",
|
|
555
|
+
msgId: "MISMATCHING_STATE",
|
|
556
|
+
message: "OAuth response state parameter does not match the stored state value"
|
|
557
|
+
},
|
|
558
|
+
POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED: {
|
|
559
|
+
facility: 4,
|
|
560
|
+
severity: "critical",
|
|
561
|
+
msgId: "POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
562
|
+
message: "Blocked redirect to untrusted or external URL (potential open redirect attack)"
|
|
563
|
+
},
|
|
564
|
+
OPEN_REDIRECT_ATTACK: {
|
|
565
|
+
facility: 4,
|
|
566
|
+
severity: "warning",
|
|
567
|
+
msgId: "OPEN_REDIRECT_ATTACK",
|
|
568
|
+
message: "Detected redirect target that does not match the trusted origin"
|
|
569
|
+
},
|
|
570
|
+
SESSION_TOKEN_MISSING: {
|
|
571
|
+
facility: 4,
|
|
572
|
+
severity: "warning",
|
|
573
|
+
msgId: "SESSION_TOKEN_MISSING",
|
|
574
|
+
message: "Session cookie is missing from the request"
|
|
575
|
+
},
|
|
576
|
+
CSRF_TOKEN_MISSING: {
|
|
577
|
+
facility: 4,
|
|
578
|
+
severity: "warning",
|
|
579
|
+
msgId: "CSRF_TOKEN_MISSING",
|
|
580
|
+
message: "CSRF token cookie is missing from the request"
|
|
581
|
+
},
|
|
582
|
+
CSRF_HEADER_MISSING: {
|
|
583
|
+
facility: 4,
|
|
584
|
+
severity: "warning",
|
|
585
|
+
msgId: "CSRF_HEADER_MISSING",
|
|
586
|
+
message: "CSRF header is missing from the request"
|
|
587
|
+
},
|
|
588
|
+
CSRF_TOKEN_INVALID: {
|
|
589
|
+
facility: 4,
|
|
590
|
+
severity: "error",
|
|
591
|
+
msgId: "CSRF_TOKEN_INVALID",
|
|
592
|
+
message: "CSRF token verification failed or token is invalid"
|
|
593
|
+
},
|
|
594
|
+
SIGN_IN_INITIATED: {
|
|
595
|
+
facility: 4,
|
|
596
|
+
severity: "info",
|
|
597
|
+
msgId: "SIGN_IN_INITIATED",
|
|
598
|
+
message: "Starting OAuth sign-in flow for the selected provider"
|
|
599
|
+
},
|
|
600
|
+
SIGN_OUT_ATTEMPT: {
|
|
601
|
+
facility: 4,
|
|
602
|
+
severity: "debug",
|
|
603
|
+
msgId: "SIGN_OUT_ATTEMPT",
|
|
604
|
+
message: "Received sign-out request from client"
|
|
605
|
+
},
|
|
606
|
+
SIGN_OUT_CSRF_VERIFIED: {
|
|
607
|
+
facility: 4,
|
|
608
|
+
severity: "info",
|
|
609
|
+
msgId: "SIGN_OUT_CSRF_VERIFIED",
|
|
610
|
+
message: "CSRF token was successfully verified during sign-out"
|
|
611
|
+
},
|
|
612
|
+
SIGN_OUT_SUCCESS: {
|
|
613
|
+
facility: 4,
|
|
614
|
+
severity: "info",
|
|
615
|
+
msgId: "SIGN_OUT_SUCCESS",
|
|
616
|
+
message: "User session was cleared and sign-out completed successfully"
|
|
617
|
+
},
|
|
618
|
+
SIGN_OUT_REDIRECT: {
|
|
619
|
+
facility: 4,
|
|
620
|
+
severity: "debug",
|
|
621
|
+
msgId: "SIGN_OUT_REDIRECT",
|
|
622
|
+
message: "Redirecting client after successful sign-out"
|
|
623
|
+
},
|
|
624
|
+
AUTH_SESSION_VALID: {
|
|
625
|
+
facility: 4,
|
|
626
|
+
severity: "info",
|
|
627
|
+
msgId: "AUTH_SESSION_VALID",
|
|
628
|
+
message: "Session token is valid and user session was returned"
|
|
629
|
+
},
|
|
630
|
+
AUTH_SESSION_INVALID: {
|
|
631
|
+
facility: 4,
|
|
632
|
+
severity: "notice",
|
|
633
|
+
msgId: "AUTH_SESSION_INVALID",
|
|
634
|
+
message: "Session token is missing, expired, or invalid"
|
|
635
|
+
},
|
|
636
|
+
INVALID_JWT_TOKEN: {
|
|
637
|
+
facility: 4,
|
|
638
|
+
severity: "warning",
|
|
639
|
+
msgId: "INVALID_JWT_TOKEN",
|
|
640
|
+
message: "JWT session token failed validation during sign-out"
|
|
641
|
+
},
|
|
642
|
+
CSRF_TOKEN_REQUESTED: {
|
|
643
|
+
facility: 4,
|
|
644
|
+
severity: "debug",
|
|
645
|
+
msgId: "CSRF_TOKEN_REQUESTED",
|
|
646
|
+
message: "Client requested a CSRF token"
|
|
647
|
+
},
|
|
648
|
+
CSRF_TOKEN_ISSUED: {
|
|
649
|
+
facility: 4,
|
|
650
|
+
severity: "debug",
|
|
651
|
+
msgId: "CSRF_TOKEN_ISSUED",
|
|
652
|
+
message: "Issued a new CSRF token to the client"
|
|
653
|
+
},
|
|
654
|
+
INVALID_URL: {
|
|
655
|
+
facility: 10,
|
|
656
|
+
severity: "error",
|
|
657
|
+
msgId: "INVALID_URL",
|
|
658
|
+
message: "Derived origin URL is invalid or malformed"
|
|
659
|
+
},
|
|
660
|
+
COOKIE_HTTPONLY_DISABLED: {
|
|
661
|
+
facility: 10,
|
|
662
|
+
severity: "critical",
|
|
663
|
+
msgId: "COOKIE_HTTPONLY_DISABLED",
|
|
664
|
+
message: "Cookie is configured without HttpOnly. This allows JavaScript access via document.cookie and increases XSS exposure."
|
|
665
|
+
},
|
|
666
|
+
COOKIE_WILDCARD_DOMAIN: {
|
|
667
|
+
facility: 10,
|
|
668
|
+
severity: "critical",
|
|
669
|
+
msgId: "COOKIE_WILDCARD_DOMAIN",
|
|
670
|
+
message: "Cookie 'Domain' is set to a wildcard, which is insecure and should be avoided."
|
|
671
|
+
},
|
|
672
|
+
COOKIE_SECURE_DISABLED: {
|
|
673
|
+
facility: 10,
|
|
674
|
+
severity: "warning",
|
|
675
|
+
msgId: "COOKIE_SECURE_DISABLED",
|
|
676
|
+
message: "Cookie is configured with 'Secure' but the request is not HTTPS. The 'Secure' attribute will be ignored by the browser."
|
|
677
|
+
},
|
|
678
|
+
COOKIE_SAMESITE_NONE_WITHOUT_SECURE: {
|
|
679
|
+
facility: 10,
|
|
680
|
+
severity: "warning",
|
|
681
|
+
msgId: "COOKIE_SAMESITE_NONE_WITHOUT_SECURE",
|
|
682
|
+
message: "Cookie uses SameSite=None without Secure. Falling back to SameSite=Lax for safer defaults."
|
|
683
|
+
},
|
|
684
|
+
COOKIE_INSECURE_IN_PRODUCTION: {
|
|
685
|
+
facility: 10,
|
|
686
|
+
severity: "critical",
|
|
687
|
+
msgId: "COOKIE_INSECURE_IN_PRODUCTION",
|
|
688
|
+
message: "Cookies are being served over an insecure connection in production, which is a serious security risk."
|
|
689
|
+
},
|
|
690
|
+
COOKIE_HOST_STRATEGY_INSECURE: {
|
|
691
|
+
facility: 10,
|
|
692
|
+
severity: "critical",
|
|
693
|
+
msgId: "COOKIE_HOST_STRATEGY_INSECURE",
|
|
694
|
+
message: "__Host- cookies require a secure HTTPS context. Falling back to standard cookie settings."
|
|
695
|
+
},
|
|
696
|
+
UNTRUSTED_ORIGIN: {
|
|
697
|
+
facility: 10,
|
|
698
|
+
severity: "error",
|
|
699
|
+
msgId: "UNTRUSTED_ORIGIN",
|
|
700
|
+
message: "The constructed origin URL is not trusted."
|
|
290
701
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const
|
|
294
|
-
const { derivedKey: derivedCsrfTokenKey } = (0, import_jose.createDeriveKey)(secret, salt, "csrfToken");
|
|
295
|
-
const { decodeJWT, encodeJWT } = (0, import_jose.createJWT)({ jws: derivedSigningKey, jwe: derivedEncryptionKey });
|
|
296
|
-
const { signJWS, verifyJWS } = (0, import_jose.createJWS)(derivedCsrfTokenKey);
|
|
297
|
-
const { encryptJWE, decryptJWE } = (0, import_jose.createJWE)(derivedEncryptionKey);
|
|
702
|
+
};
|
|
703
|
+
var createLogEntry = (key, overrides) => {
|
|
704
|
+
const message = logMessages[key];
|
|
298
705
|
return {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
decryptJWE
|
|
706
|
+
...message,
|
|
707
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
708
|
+
hostname: "aura-auth",
|
|
709
|
+
procId: typeof process !== "undefined" && process.pid ? process.pid.toString() : "-",
|
|
710
|
+
...overrides
|
|
305
711
|
};
|
|
306
712
|
};
|
|
713
|
+
var logLevelToSeverity = {
|
|
714
|
+
debug: ["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"],
|
|
715
|
+
info: ["info", "notice", "warning", "error", "critical", "alert", "emergency"],
|
|
716
|
+
warn: ["warning", "error", "critical", "alert", "emergency"],
|
|
717
|
+
error: ["error", "critical", "alert", "emergency"]
|
|
718
|
+
};
|
|
719
|
+
var isValidLogLevel = (value) => {
|
|
720
|
+
return value === "debug" || value === "info" || value === "warn" || value === "error";
|
|
721
|
+
};
|
|
722
|
+
var getSeverityLevel = (severity) => {
|
|
723
|
+
const severities = {
|
|
724
|
+
emergency: 0,
|
|
725
|
+
alert: 1,
|
|
726
|
+
critical: 2,
|
|
727
|
+
error: 3,
|
|
728
|
+
warning: 4,
|
|
729
|
+
notice: 5,
|
|
730
|
+
info: 6,
|
|
731
|
+
debug: 7
|
|
732
|
+
};
|
|
733
|
+
return severities[severity] ?? 6;
|
|
734
|
+
};
|
|
735
|
+
var createSyslogMessage = (options2) => {
|
|
736
|
+
const { timestamp, hostname, appName = "aura-auth", procId = "-", msgId, structuredData, message } = options2;
|
|
737
|
+
const pri = (options2.facility ?? 16) * 8 + getSeverityLevel(options2.severity);
|
|
738
|
+
const structuredDataStr = createStructuredData(structuredData ?? {});
|
|
739
|
+
return `<${pri}>1 ${timestamp} ${hostname} ${appName} ${procId} ${msgId} ${structuredDataStr} ${message}`;
|
|
740
|
+
};
|
|
741
|
+
var createLogger = (logger) => {
|
|
742
|
+
if (!logger) return void 0;
|
|
743
|
+
const level = logger.level;
|
|
744
|
+
const allowedSeverities = logLevelToSeverity[level] ?? [];
|
|
745
|
+
return {
|
|
746
|
+
level,
|
|
747
|
+
log(key, overrides) {
|
|
748
|
+
const entry = createLogEntry(key, overrides);
|
|
749
|
+
if (!allowedSeverities.includes(entry.severity)) return entry;
|
|
750
|
+
logger.log({
|
|
751
|
+
timestamp: entry.timestamp,
|
|
752
|
+
appName: entry.appName ?? "aura-auth",
|
|
753
|
+
hostname: entry.hostname ?? "aura-auth",
|
|
754
|
+
...entry
|
|
755
|
+
});
|
|
756
|
+
return entry;
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
};
|
|
760
|
+
var createProxyLogger = (config2) => {
|
|
761
|
+
const level = getEnv("LOG_LEVEL");
|
|
762
|
+
const debug = getEnvBoolean("DEBUG");
|
|
763
|
+
if (typeof config2?.logger === "object") {
|
|
764
|
+
return createLogger({
|
|
765
|
+
log: config2.logger?.log || createSyslogMessage,
|
|
766
|
+
level: isValidLogLevel(config2.logger?.level) ? config2.logger?.level : isValidLogLevel(level) ? level : "error"
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
if (debug || config2?.logger === true || level) {
|
|
770
|
+
return createLogger({
|
|
771
|
+
level: isValidLogLevel(level) ? level : "debug",
|
|
772
|
+
log: (options2) => {
|
|
773
|
+
const message = createSyslogMessage(options2);
|
|
774
|
+
console.log(message);
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
return void 0;
|
|
779
|
+
};
|
|
307
780
|
|
|
308
781
|
// src/cookie.ts
|
|
309
782
|
var import_cookie = require("@aura-stack/router/cookie");
|
|
@@ -340,10 +813,11 @@ var setCookie = (cookieName, value, options2) => {
|
|
|
340
813
|
var expiredCookieAttributes = {
|
|
341
814
|
...defaultCookieOptions,
|
|
342
815
|
expires: /* @__PURE__ */ new Date(0),
|
|
343
|
-
maxAge: 0
|
|
816
|
+
maxAge: 0,
|
|
817
|
+
secure: true
|
|
344
818
|
};
|
|
345
819
|
var getCookie = (request, cookieName) => {
|
|
346
|
-
const cookies = request.headers.get("Cookie");
|
|
820
|
+
const cookies = request instanceof Request ? request.headers.get("Cookie") : request.get("Cookie");
|
|
347
821
|
if (!cookies) {
|
|
348
822
|
throw new AuthInternalError("COOKIE_NOT_FOUND", "No cookies found. There is no active session");
|
|
349
823
|
}
|
|
@@ -361,31 +835,27 @@ var createSessionCookie = async (jose, session) => {
|
|
|
361
835
|
throw new AuthInternalError("INVALID_JWT_TOKEN", "Failed to create session cookie", { cause: error });
|
|
362
836
|
}
|
|
363
837
|
};
|
|
364
|
-
var defineSecureCookieOptions = (useSecure, attributes, strategy) => {
|
|
838
|
+
var defineSecureCookieOptions = (useSecure, attributes, strategy, logger) => {
|
|
365
839
|
if (!attributes.httpOnly) {
|
|
366
|
-
|
|
367
|
-
"[WARNING]: Cookie is configured without HttpOnly. This allows JavaScript access via document.cookie and increases XSS risk."
|
|
368
|
-
);
|
|
840
|
+
logger?.log("COOKIE_HTTPONLY_DISABLED");
|
|
369
841
|
}
|
|
370
842
|
if (attributes.domain === "*") {
|
|
371
843
|
attributes.domain = void 0;
|
|
372
|
-
|
|
844
|
+
logger?.log("COOKIE_WILDCARD_DOMAIN");
|
|
373
845
|
}
|
|
374
846
|
if (!useSecure) {
|
|
375
847
|
if (attributes.secure) {
|
|
376
|
-
|
|
377
|
-
"[WARNING]: The 'Secure' attribute will be disabled for this cookie. Serve over HTTPS to enforce Secure cookies."
|
|
378
|
-
);
|
|
848
|
+
logger?.log("COOKIE_SECURE_DISABLED");
|
|
379
849
|
}
|
|
380
850
|
if (attributes.sameSite == "none") {
|
|
381
851
|
attributes.sameSite = "lax";
|
|
382
|
-
|
|
852
|
+
logger?.log("COOKIE_SAMESITE_NONE_WITHOUT_SECURE");
|
|
383
853
|
}
|
|
384
|
-
if (
|
|
385
|
-
|
|
854
|
+
if (env.NODE_ENV === "production") {
|
|
855
|
+
logger?.log("COOKIE_INSECURE_IN_PRODUCTION");
|
|
386
856
|
}
|
|
387
857
|
if (strategy === "host") {
|
|
388
|
-
|
|
858
|
+
logger?.log("COOKIE_HOST_STRATEGY_INSECURE");
|
|
389
859
|
}
|
|
390
860
|
return {
|
|
391
861
|
...defaultCookieOptions,
|
|
@@ -399,7 +869,7 @@ var defineSecureCookieOptions = (useSecure, attributes, strategy) => {
|
|
|
399
869
|
...defaultHostCookieConfig
|
|
400
870
|
} : { ...defaultCookieOptions, ...attributes, ...defaultSecureCookieConfig };
|
|
401
871
|
};
|
|
402
|
-
var createCookieStore = (useSecure, prefix, overrides) => {
|
|
872
|
+
var createCookieStore = (useSecure, prefix, overrides, logger) => {
|
|
403
873
|
prefix ??= COOKIE_NAME;
|
|
404
874
|
const securePrefix = useSecure ? "__Secure-" : "";
|
|
405
875
|
const hostPrefix = useSecure ? "__Host-" : "";
|
|
@@ -412,7 +882,8 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
412
882
|
...defaultCookieOptions,
|
|
413
883
|
...overrides?.sessionToken?.attributes
|
|
414
884
|
},
|
|
415
|
-
overrides?.sessionToken?.attributes?.strategy ?? "secure"
|
|
885
|
+
overrides?.sessionToken?.attributes?.strategy ?? "secure",
|
|
886
|
+
logger
|
|
416
887
|
)
|
|
417
888
|
},
|
|
418
889
|
state: {
|
|
@@ -423,7 +894,8 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
423
894
|
...oauthCookieOptions,
|
|
424
895
|
...overrides?.state?.attributes
|
|
425
896
|
},
|
|
426
|
-
overrides?.state?.attributes?.strategy ?? "secure"
|
|
897
|
+
overrides?.state?.attributes?.strategy ?? "secure",
|
|
898
|
+
logger
|
|
427
899
|
)
|
|
428
900
|
},
|
|
429
901
|
csrfToken: {
|
|
@@ -432,9 +904,11 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
432
904
|
useSecure,
|
|
433
905
|
{
|
|
434
906
|
...overrides?.csrfToken?.attributes,
|
|
435
|
-
...defaultHostCookieConfig
|
|
907
|
+
...defaultHostCookieConfig,
|
|
908
|
+
sameSite: "strict"
|
|
436
909
|
},
|
|
437
|
-
overrides?.csrfToken?.attributes?.strategy ?? "host"
|
|
910
|
+
overrides?.csrfToken?.attributes?.strategy ?? "host",
|
|
911
|
+
logger
|
|
438
912
|
)
|
|
439
913
|
},
|
|
440
914
|
redirectTo: {
|
|
@@ -445,7 +919,8 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
445
919
|
...oauthCookieOptions,
|
|
446
920
|
...overrides?.redirectTo?.attributes
|
|
447
921
|
},
|
|
448
|
-
overrides?.redirectTo?.attributes?.strategy ?? "secure"
|
|
922
|
+
overrides?.redirectTo?.attributes?.strategy ?? "secure",
|
|
923
|
+
logger
|
|
449
924
|
)
|
|
450
925
|
},
|
|
451
926
|
redirectURI: {
|
|
@@ -456,7 +931,8 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
456
931
|
...oauthCookieOptions,
|
|
457
932
|
...overrides?.redirectURI?.attributes
|
|
458
933
|
},
|
|
459
|
-
overrides?.redirectURI?.attributes?.strategy ?? "secure"
|
|
934
|
+
overrides?.redirectURI?.attributes?.strategy ?? "secure",
|
|
935
|
+
logger
|
|
460
936
|
)
|
|
461
937
|
},
|
|
462
938
|
codeVerifier: {
|
|
@@ -467,224 +943,438 @@ var createCookieStore = (useSecure, prefix, overrides) => {
|
|
|
467
943
|
...oauthCookieOptions,
|
|
468
944
|
...overrides?.codeVerifier?.attributes
|
|
469
945
|
},
|
|
470
|
-
overrides?.codeVerifier?.attributes?.strategy ?? "secure"
|
|
946
|
+
overrides?.codeVerifier?.attributes?.strategy ?? "secure",
|
|
947
|
+
logger
|
|
471
948
|
)
|
|
472
949
|
}
|
|
473
950
|
};
|
|
474
951
|
};
|
|
475
952
|
|
|
476
953
|
// src/oauth/github.ts
|
|
477
|
-
var github = {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
954
|
+
var github = (options2) => {
|
|
955
|
+
return {
|
|
956
|
+
id: "github",
|
|
957
|
+
name: "GitHub",
|
|
958
|
+
authorizeURL: "https://github.com/login/oauth/authorize",
|
|
959
|
+
accessToken: "https://github.com/login/oauth/access_token",
|
|
960
|
+
userInfo: "https://api.github.com/user",
|
|
961
|
+
scope: "read:user user:email",
|
|
962
|
+
responseType: "code",
|
|
963
|
+
profile: (profile) => {
|
|
964
|
+
return {
|
|
965
|
+
sub: profile.id.toString(),
|
|
966
|
+
name: profile.name ?? profile.login,
|
|
967
|
+
email: profile.email ?? void 0,
|
|
968
|
+
image: profile.avatar_url
|
|
969
|
+
};
|
|
970
|
+
},
|
|
971
|
+
...options2
|
|
972
|
+
};
|
|
485
973
|
};
|
|
486
974
|
|
|
487
975
|
// src/oauth/bitbucket.ts
|
|
488
|
-
var bitbucket = {
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
976
|
+
var bitbucket = (options2) => {
|
|
977
|
+
return {
|
|
978
|
+
id: "bitbucket",
|
|
979
|
+
name: "Bitbucket",
|
|
980
|
+
authorizeURL: "https://bitbucket.org/site/oauth2/authorize",
|
|
981
|
+
accessToken: "https://bitbucket.org/site/oauth2/access_token",
|
|
982
|
+
userInfo: "https://api.bitbucket.org/2.0/user",
|
|
983
|
+
scope: "account email",
|
|
984
|
+
responseType: "code",
|
|
985
|
+
profile(profile) {
|
|
986
|
+
return {
|
|
987
|
+
sub: profile.uuid ?? profile.account_id,
|
|
988
|
+
name: profile.display_name ?? profile.nickname,
|
|
989
|
+
image: profile.links.avatar?.href,
|
|
990
|
+
email: void 0
|
|
991
|
+
};
|
|
992
|
+
},
|
|
993
|
+
...options2
|
|
994
|
+
};
|
|
503
995
|
};
|
|
504
996
|
|
|
505
997
|
// src/oauth/figma.ts
|
|
506
|
-
var figma = {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
998
|
+
var figma = (options2) => {
|
|
999
|
+
return {
|
|
1000
|
+
id: "figma",
|
|
1001
|
+
name: "Figma",
|
|
1002
|
+
authorizeURL: "https://www.figma.com/oauth",
|
|
1003
|
+
accessToken: "https://api.figma.com/v1/oauth/token",
|
|
1004
|
+
userInfo: "https://api.figma.com/v1/me",
|
|
1005
|
+
scope: "current_user:read",
|
|
1006
|
+
responseType: "code",
|
|
1007
|
+
profile(profile) {
|
|
1008
|
+
return {
|
|
1009
|
+
sub: profile.id,
|
|
1010
|
+
name: profile.handle,
|
|
1011
|
+
email: profile.email,
|
|
1012
|
+
image: profile.img_url
|
|
1013
|
+
};
|
|
1014
|
+
},
|
|
1015
|
+
...options2
|
|
1016
|
+
};
|
|
522
1017
|
};
|
|
523
1018
|
|
|
524
1019
|
// src/oauth/discord.ts
|
|
525
|
-
var discord = {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
1020
|
+
var discord = (options2) => {
|
|
1021
|
+
return {
|
|
1022
|
+
id: "discord",
|
|
1023
|
+
name: "Discord",
|
|
1024
|
+
authorizeURL: "https://discord.com/oauth2/authorize",
|
|
1025
|
+
accessToken: "https://discord.com/api/oauth2/token",
|
|
1026
|
+
userInfo: "https://discord.com/api/users/@me",
|
|
1027
|
+
scope: "identify email",
|
|
1028
|
+
responseType: "code",
|
|
1029
|
+
profile(profile) {
|
|
1030
|
+
let image = "";
|
|
1031
|
+
if (profile.avatar === null) {
|
|
1032
|
+
const index = profile.discriminator === "0" ? (BigInt(profile.id) >> 22n) % 6n : Number(profile.discriminator) % 5;
|
|
1033
|
+
image = `https://cdn.discordapp.com/embed/avatars/${index}.png`;
|
|
1034
|
+
} else {
|
|
1035
|
+
const format = profile.avatar.startsWith("a_") ? "gif" : "png";
|
|
1036
|
+
image = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
|
|
1037
|
+
}
|
|
1038
|
+
return {
|
|
1039
|
+
sub: profile.id,
|
|
1040
|
+
name: profile.global_name ?? profile.username,
|
|
1041
|
+
email: profile.email ?? "",
|
|
1042
|
+
image
|
|
1043
|
+
};
|
|
1044
|
+
},
|
|
1045
|
+
...options2
|
|
1046
|
+
};
|
|
549
1047
|
};
|
|
550
1048
|
|
|
551
1049
|
// src/oauth/gitlab.ts
|
|
552
|
-
var gitlab = {
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
1050
|
+
var gitlab = (options2) => {
|
|
1051
|
+
return {
|
|
1052
|
+
id: "gitlab",
|
|
1053
|
+
name: "GitLab",
|
|
1054
|
+
authorizeURL: "https://gitlab.com/oauth/authorize",
|
|
1055
|
+
accessToken: "https://gitlab.com/oauth/token",
|
|
1056
|
+
userInfo: "https://gitlab.com/api/v4/user",
|
|
1057
|
+
scope: "read_user",
|
|
1058
|
+
responseType: "code",
|
|
1059
|
+
profile(profile) {
|
|
1060
|
+
return {
|
|
1061
|
+
sub: profile.id.toString(),
|
|
1062
|
+
name: profile.name ?? profile.username,
|
|
1063
|
+
email: profile.email,
|
|
1064
|
+
image: profile.avatar_url
|
|
1065
|
+
};
|
|
1066
|
+
},
|
|
1067
|
+
...options2
|
|
1068
|
+
};
|
|
568
1069
|
};
|
|
569
1070
|
|
|
570
1071
|
// src/oauth/spotify.ts
|
|
571
|
-
var spotify = {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1072
|
+
var spotify = (options2) => {
|
|
1073
|
+
return {
|
|
1074
|
+
id: "spotify",
|
|
1075
|
+
name: "Spotify",
|
|
1076
|
+
authorizeURL: "https://accounts.spotify.com/authorize",
|
|
1077
|
+
accessToken: "https://accounts.spotify.com/api/token",
|
|
1078
|
+
userInfo: "https://api.spotify.com/v1/me",
|
|
1079
|
+
scope: "user-read-private user-read-email",
|
|
1080
|
+
responseType: "code",
|
|
1081
|
+
profile(profile) {
|
|
1082
|
+
return {
|
|
1083
|
+
sub: profile.id,
|
|
1084
|
+
name: profile.display_name,
|
|
1085
|
+
email: profile.email,
|
|
1086
|
+
image: profile.images[0]?.url ?? void 0
|
|
1087
|
+
};
|
|
1088
|
+
},
|
|
1089
|
+
...options2
|
|
1090
|
+
};
|
|
1091
|
+
};
|
|
1092
|
+
|
|
1093
|
+
// src/oauth/x.ts
|
|
1094
|
+
var x = (options2) => {
|
|
1095
|
+
return {
|
|
1096
|
+
id: "x",
|
|
1097
|
+
name: "X",
|
|
1098
|
+
authorizeURL: "https://twitter.com/i/oauth2/authorize",
|
|
1099
|
+
accessToken: "https://api.twitter.com/2/oauth2/token",
|
|
1100
|
+
userInfo: "https://api.twitter.com/2/users/me?user.fields=profile_image_url",
|
|
1101
|
+
scope: "tweet.read users.read offline.access",
|
|
1102
|
+
responseType: "code",
|
|
1103
|
+
profile(profile) {
|
|
1104
|
+
return {
|
|
1105
|
+
sub: profile.data.id,
|
|
1106
|
+
name: profile.data.name,
|
|
1107
|
+
image: profile.data.profile_image_url,
|
|
1108
|
+
email: void 0
|
|
1109
|
+
};
|
|
1110
|
+
},
|
|
1111
|
+
...options2
|
|
1112
|
+
};
|
|
1113
|
+
};
|
|
1114
|
+
|
|
1115
|
+
// src/oauth/strava.ts
|
|
1116
|
+
var strava = (options2) => {
|
|
1117
|
+
return {
|
|
1118
|
+
id: "strava",
|
|
1119
|
+
name: "Strava",
|
|
1120
|
+
authorizeURL: "https://www.strava.com/oauth/authorize",
|
|
1121
|
+
accessToken: "https://www.strava.com/oauth/token",
|
|
1122
|
+
userInfo: "https://www.strava.com/api/v3/athlete",
|
|
1123
|
+
scope: "read",
|
|
1124
|
+
responseType: "code",
|
|
1125
|
+
profile(profile) {
|
|
1126
|
+
return {
|
|
1127
|
+
sub: profile.id.toString(),
|
|
1128
|
+
name: `${profile.firstname} ${profile.lastname}`,
|
|
1129
|
+
image: profile.profile,
|
|
1130
|
+
email: void 0
|
|
1131
|
+
};
|
|
1132
|
+
},
|
|
1133
|
+
...options2
|
|
1134
|
+
};
|
|
1135
|
+
};
|
|
1136
|
+
|
|
1137
|
+
// src/oauth/mailchimp.ts
|
|
1138
|
+
var mailchimp = (options2) => {
|
|
1139
|
+
return {
|
|
1140
|
+
id: "mailchimp",
|
|
1141
|
+
name: "Mailchimp",
|
|
1142
|
+
authorizeURL: "https://login.mailchimp.com/oauth2/authorize",
|
|
1143
|
+
accessToken: "https://login.mailchimp.com/oauth2/token",
|
|
1144
|
+
userInfo: "https://login.mailchimp.com/oauth2/metadata",
|
|
1145
|
+
scope: "",
|
|
1146
|
+
responseType: "code",
|
|
1147
|
+
profile(profile) {
|
|
1148
|
+
return {
|
|
1149
|
+
sub: profile.user_id,
|
|
1150
|
+
name: profile.accountname,
|
|
1151
|
+
email: profile.login.email,
|
|
1152
|
+
image: profile.login.avatar
|
|
1153
|
+
};
|
|
1154
|
+
},
|
|
1155
|
+
...options2
|
|
1156
|
+
};
|
|
1157
|
+
};
|
|
1158
|
+
|
|
1159
|
+
// src/oauth/pinterest.ts
|
|
1160
|
+
var pinterest = (options2) => {
|
|
1161
|
+
return {
|
|
1162
|
+
id: "pinterest",
|
|
1163
|
+
name: "Pinterest",
|
|
1164
|
+
authorizeURL: "https://www.pinterest.com/oauth",
|
|
1165
|
+
accessToken: "https://api.pinterest.com/v5/oauth/token",
|
|
1166
|
+
userInfo: "https://api.pinterest.com/v5/user_account",
|
|
1167
|
+
scope: "user_accounts:read",
|
|
1168
|
+
responseType: "code",
|
|
1169
|
+
profile(profile) {
|
|
1170
|
+
return {
|
|
1171
|
+
sub: profile.id,
|
|
1172
|
+
name: profile.username,
|
|
1173
|
+
image: profile.profile_image,
|
|
1174
|
+
email: void 0
|
|
1175
|
+
};
|
|
1176
|
+
},
|
|
1177
|
+
...options2
|
|
1178
|
+
};
|
|
587
1179
|
};
|
|
588
1180
|
|
|
589
|
-
// src/oauth/
|
|
590
|
-
var
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
1181
|
+
// src/oauth/twitch.ts
|
|
1182
|
+
var twitch = (options2) => {
|
|
1183
|
+
const clientId = options2?.clientId ?? getEnv("TWITCH_CLIENT_ID");
|
|
1184
|
+
return {
|
|
1185
|
+
id: "twitch",
|
|
1186
|
+
name: "Twitch",
|
|
1187
|
+
authorize: {
|
|
1188
|
+
url: "https://id.twitch.tv/oauth2/authorize",
|
|
1189
|
+
params: { scope: "user:read:email", responseType: "code" }
|
|
1190
|
+
},
|
|
1191
|
+
accessToken: "https://id.twitch.tv/oauth2/token",
|
|
1192
|
+
userInfo: {
|
|
1193
|
+
url: "https://api.twitch.tv/helix/users",
|
|
1194
|
+
headers: {
|
|
1195
|
+
"Client-ID": clientId
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
profile(profile) {
|
|
1199
|
+
const user = profile.data[0];
|
|
1200
|
+
if (!user) {
|
|
1201
|
+
throw new Error("No user data found in Twitch profile response");
|
|
1202
|
+
}
|
|
1203
|
+
return {
|
|
1204
|
+
sub: user.id,
|
|
1205
|
+
name: user.display_name,
|
|
1206
|
+
email: user.email,
|
|
1207
|
+
picture: user.profile_image_url
|
|
1208
|
+
};
|
|
1209
|
+
},
|
|
1210
|
+
...options2
|
|
1211
|
+
};
|
|
606
1212
|
};
|
|
607
1213
|
|
|
608
|
-
// src/oauth/
|
|
609
|
-
var
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
1214
|
+
// src/oauth/notion.ts
|
|
1215
|
+
var notion = (options2) => {
|
|
1216
|
+
return {
|
|
1217
|
+
id: "notion",
|
|
1218
|
+
name: "Notion",
|
|
1219
|
+
authorize: {
|
|
1220
|
+
url: "https://api.notion.com/v1/oauth/authorize",
|
|
1221
|
+
params: {
|
|
1222
|
+
owner: "user",
|
|
1223
|
+
scope: "user:read",
|
|
1224
|
+
responseType: "code"
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
accessToken: {
|
|
1228
|
+
url: "https://api.notion.com/v1/oauth/token",
|
|
1229
|
+
headers: {
|
|
1230
|
+
Authorization: createBasicAuthHeader(
|
|
1231
|
+
options2?.clientId ?? "NOTION_CLIENT_ID",
|
|
1232
|
+
options2?.clientSecret ?? "NOTION_CLIENT_SECRET"
|
|
1233
|
+
)
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
userInfo: {
|
|
1237
|
+
url: "https://api.notion.com/v1/users/me",
|
|
1238
|
+
headers: {
|
|
1239
|
+
"Notion-Version": "2022-06-28"
|
|
1240
|
+
}
|
|
1241
|
+
},
|
|
1242
|
+
profile(profile) {
|
|
1243
|
+
return {
|
|
1244
|
+
sub: profile.id,
|
|
1245
|
+
name: profile.name,
|
|
1246
|
+
image: profile.avatar_url ?? "",
|
|
1247
|
+
email: profile?.bot?.owner?.user?.person?.email
|
|
1248
|
+
};
|
|
1249
|
+
},
|
|
1250
|
+
...options2
|
|
1251
|
+
};
|
|
625
1252
|
};
|
|
626
1253
|
|
|
627
|
-
// src/oauth/
|
|
628
|
-
var
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
1254
|
+
// src/oauth/dropbox.ts
|
|
1255
|
+
var dropbox = (options2) => {
|
|
1256
|
+
return {
|
|
1257
|
+
id: "dropbox",
|
|
1258
|
+
name: "Dropbox",
|
|
1259
|
+
authorize: {
|
|
1260
|
+
url: "https://www.dropbox.com/oauth2/authorize",
|
|
1261
|
+
params: { scope: "account_info.read" }
|
|
1262
|
+
},
|
|
1263
|
+
accessToken: "https://api.dropboxapi.com/oauth2/token",
|
|
1264
|
+
userInfo: {
|
|
1265
|
+
method: "POST",
|
|
1266
|
+
url: "https://api.dropboxapi.com/2/users/get_current_account"
|
|
1267
|
+
},
|
|
1268
|
+
profile(profile) {
|
|
1269
|
+
return {
|
|
1270
|
+
sub: profile.account_id,
|
|
1271
|
+
name: profile.name.display_name,
|
|
1272
|
+
email: profile.email,
|
|
1273
|
+
image: profile.profile_photo_url
|
|
1274
|
+
};
|
|
1275
|
+
},
|
|
1276
|
+
...options2
|
|
1277
|
+
};
|
|
644
1278
|
};
|
|
645
1279
|
|
|
646
|
-
// src/oauth/
|
|
647
|
-
var
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1280
|
+
// src/oauth/atlassian.ts
|
|
1281
|
+
var atlassian = (options2) => {
|
|
1282
|
+
return {
|
|
1283
|
+
id: "atlassian",
|
|
1284
|
+
name: "Atlassian",
|
|
1285
|
+
authorize: {
|
|
1286
|
+
url: "https://auth.atlassian.com/authorize",
|
|
1287
|
+
params: {
|
|
1288
|
+
audience: "api.atlassian.com",
|
|
1289
|
+
scope: "read:me read:account",
|
|
1290
|
+
prompt: "consent"
|
|
1291
|
+
}
|
|
1292
|
+
},
|
|
1293
|
+
authorizeURL: "https://auth.atlassian.com/authorize",
|
|
1294
|
+
accessToken: "https://auth.atlassian.com/oauth/token",
|
|
1295
|
+
userInfo: "https://api.atlassian.com/me",
|
|
1296
|
+
scope: "read:me read:account",
|
|
1297
|
+
responseType: "code",
|
|
1298
|
+
profile(profile) {
|
|
1299
|
+
return {
|
|
1300
|
+
sub: profile.account_id,
|
|
1301
|
+
name: profile.name,
|
|
1302
|
+
email: profile.email,
|
|
1303
|
+
image: profile.picture
|
|
1304
|
+
};
|
|
1305
|
+
},
|
|
1306
|
+
...options2
|
|
1307
|
+
};
|
|
663
1308
|
};
|
|
664
1309
|
|
|
665
1310
|
// src/schemas.ts
|
|
666
|
-
var
|
|
667
|
-
var
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1311
|
+
var import_v4 = require("zod/v4");
|
|
1312
|
+
var AuthorizeConfigSchema = import_v4.z.union([
|
|
1313
|
+
(0, import_v4.string)().url(),
|
|
1314
|
+
(0, import_v4.object)({
|
|
1315
|
+
url: (0, import_v4.string)().url(),
|
|
1316
|
+
params: (0, import_v4.object)({
|
|
1317
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
1318
|
+
scope: (0, import_v4.string)().optional()
|
|
1319
|
+
})
|
|
1320
|
+
})
|
|
1321
|
+
]);
|
|
1322
|
+
var AccessTokenConfigSchema = import_v4.z.union([
|
|
1323
|
+
(0, import_v4.string)().url(),
|
|
1324
|
+
(0, import_v4.object)({
|
|
1325
|
+
url: (0, import_v4.string)().url(),
|
|
1326
|
+
headers: import_v4.z.record((0, import_v4.string)(), (0, import_v4.string)()).optional()
|
|
1327
|
+
})
|
|
1328
|
+
]);
|
|
1329
|
+
var UserInfoConfigSchema = import_v4.z.union([
|
|
1330
|
+
(0, import_v4.string)().url(),
|
|
1331
|
+
(0, import_v4.object)({
|
|
1332
|
+
url: (0, import_v4.string)().url(),
|
|
1333
|
+
headers: import_v4.z.record((0, import_v4.string)(), (0, import_v4.string)()).optional(),
|
|
1334
|
+
method: (0, import_v4.string)().optional()
|
|
1335
|
+
})
|
|
1336
|
+
]);
|
|
1337
|
+
var OAuthProviderCredentialsSchema = (0, import_v4.object)({
|
|
1338
|
+
id: (0, import_v4.string)(),
|
|
1339
|
+
name: (0, import_v4.string)(),
|
|
1340
|
+
authorize: AuthorizeConfigSchema.optional(),
|
|
1341
|
+
/** @deprecated */
|
|
1342
|
+
authorizeURL: (0, import_v4.string)().url().optional(),
|
|
1343
|
+
accessToken: AccessTokenConfigSchema,
|
|
1344
|
+
/** @deprecated */
|
|
1345
|
+
scope: (0, import_v4.string)().optional(),
|
|
1346
|
+
userInfo: UserInfoConfigSchema,
|
|
1347
|
+
/** @deprecated */
|
|
1348
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
1349
|
+
clientId: (0, import_v4.string)(),
|
|
1350
|
+
clientSecret: (0, import_v4.string)(),
|
|
1351
|
+
profile: import_v4.z.function().optional()
|
|
1352
|
+
});
|
|
1353
|
+
var OAuthProviderConfigSchema = (0, import_v4.object)({
|
|
1354
|
+
authorize: AuthorizeConfigSchema.optional(),
|
|
1355
|
+
/** @deprecated */
|
|
1356
|
+
authorizeURL: (0, import_v4.string)().url().optional(),
|
|
1357
|
+
accessToken: AccessTokenConfigSchema,
|
|
1358
|
+
/** @deprecated */
|
|
1359
|
+
scope: (0, import_v4.string)().optional(),
|
|
1360
|
+
userInfo: UserInfoConfigSchema,
|
|
1361
|
+
/** @deprecated */
|
|
1362
|
+
responseType: (0, import_v4.enum)(["code", "token", "id_token", "refresh_token"]).optional(),
|
|
1363
|
+
clientId: (0, import_v4.string)(),
|
|
1364
|
+
clientSecret: (0, import_v4.string)()
|
|
675
1365
|
});
|
|
676
1366
|
var OAuthAuthorization = OAuthProviderConfigSchema.extend({
|
|
677
|
-
redirectURI: (0,
|
|
678
|
-
state: (0,
|
|
679
|
-
codeChallenge: (0,
|
|
680
|
-
codeChallengeMethod: (0,
|
|
1367
|
+
redirectURI: (0, import_v4.string)(),
|
|
1368
|
+
state: (0, import_v4.string)(),
|
|
1369
|
+
codeChallenge: (0, import_v4.string)(),
|
|
1370
|
+
codeChallengeMethod: (0, import_v4.enum)(["plain", "S256"])
|
|
681
1371
|
});
|
|
682
|
-
var OAuthAuthorizationResponse = (0,
|
|
683
|
-
state: (0,
|
|
684
|
-
code: (0,
|
|
1372
|
+
var OAuthAuthorizationResponse = (0, import_v4.object)({
|
|
1373
|
+
state: (0, import_v4.string)({ message: "Missing state parameter in the OAuth authorization response." }),
|
|
1374
|
+
code: (0, import_v4.string)({ message: "Missing code parameter in the OAuth authorization response." })
|
|
685
1375
|
});
|
|
686
|
-
var OAuthAuthorizationErrorResponse = (0,
|
|
687
|
-
error: (0,
|
|
1376
|
+
var OAuthAuthorizationErrorResponse = (0, import_v4.object)({
|
|
1377
|
+
error: (0, import_v4.enum)([
|
|
688
1378
|
"invalid_request",
|
|
689
1379
|
"unauthorized_client",
|
|
690
1380
|
"access_denied",
|
|
@@ -693,24 +1383,24 @@ var OAuthAuthorizationErrorResponse = (0, import_zod.object)({
|
|
|
693
1383
|
"server_error",
|
|
694
1384
|
"temporarily_unavailable"
|
|
695
1385
|
]),
|
|
696
|
-
error_description: (0,
|
|
697
|
-
error_uri: (0,
|
|
698
|
-
state: (0,
|
|
1386
|
+
error_description: (0, import_v4.string)().optional(),
|
|
1387
|
+
error_uri: (0, import_v4.string)().optional(),
|
|
1388
|
+
state: (0, import_v4.string)()
|
|
699
1389
|
});
|
|
700
1390
|
var OAuthAccessToken = OAuthProviderConfigSchema.extend({
|
|
701
|
-
redirectURI: (0,
|
|
702
|
-
code: (0,
|
|
703
|
-
codeVerifier: (0,
|
|
1391
|
+
redirectURI: (0, import_v4.string)(),
|
|
1392
|
+
code: (0, import_v4.string)(),
|
|
1393
|
+
codeVerifier: (0, import_v4.string)().min(43).max(128)
|
|
704
1394
|
});
|
|
705
|
-
var OAuthAccessTokenResponse = (0,
|
|
706
|
-
access_token: (0,
|
|
707
|
-
token_type: (0,
|
|
708
|
-
expires_in: (0,
|
|
709
|
-
refresh_token: (0,
|
|
710
|
-
scope: (0,
|
|
1395
|
+
var OAuthAccessTokenResponse = (0, import_v4.object)({
|
|
1396
|
+
access_token: (0, import_v4.string)(),
|
|
1397
|
+
token_type: (0, import_v4.string)().optional(),
|
|
1398
|
+
expires_in: (0, import_v4.number)().optional(),
|
|
1399
|
+
refresh_token: (0, import_v4.string)().optional(),
|
|
1400
|
+
scope: (0, import_v4.union)([(0, import_v4.string)().optional().or((0, import_v4.null)()), (0, import_v4.array)((0, import_v4.string)()).optional()])
|
|
711
1401
|
});
|
|
712
|
-
var OAuthAccessTokenErrorResponse = (0,
|
|
713
|
-
error: (0,
|
|
1402
|
+
var OAuthAccessTokenErrorResponse = (0, import_v4.object)({
|
|
1403
|
+
error: (0, import_v4.enum)([
|
|
714
1404
|
"invalid_request",
|
|
715
1405
|
"invalid_client",
|
|
716
1406
|
"invalid_grant",
|
|
@@ -718,16 +1408,16 @@ var OAuthAccessTokenErrorResponse = (0, import_zod.object)({
|
|
|
718
1408
|
"unsupported_grant_type",
|
|
719
1409
|
"invalid_scope"
|
|
720
1410
|
]),
|
|
721
|
-
error_description: (0,
|
|
722
|
-
error_uri: (0,
|
|
1411
|
+
error_description: (0, import_v4.string)().optional(),
|
|
1412
|
+
error_uri: (0, import_v4.string)().optional()
|
|
723
1413
|
});
|
|
724
|
-
var OAuthErrorResponse = (0,
|
|
725
|
-
error: (0,
|
|
726
|
-
error_description: (0,
|
|
1414
|
+
var OAuthErrorResponse = (0, import_v4.object)({
|
|
1415
|
+
error: (0, import_v4.string)(),
|
|
1416
|
+
error_description: (0, import_v4.string)().optional()
|
|
727
1417
|
});
|
|
728
|
-
var OAuthEnvSchema = (0,
|
|
729
|
-
clientId:
|
|
730
|
-
clientSecret:
|
|
1418
|
+
var OAuthEnvSchema = (0, import_v4.object)({
|
|
1419
|
+
clientId: import_v4.z.string().min(1, "OAuth Client ID is required in the environment variables."),
|
|
1420
|
+
clientSecret: import_v4.z.string().min(1, "OAuth Client Secret is required in the environment variables.")
|
|
731
1421
|
});
|
|
732
1422
|
|
|
733
1423
|
// src/oauth/index.ts
|
|
@@ -741,18 +1431,19 @@ var builtInOAuthProviders = {
|
|
|
741
1431
|
x,
|
|
742
1432
|
strava,
|
|
743
1433
|
mailchimp,
|
|
744
|
-
pinterest
|
|
1434
|
+
pinterest,
|
|
1435
|
+
twitch,
|
|
1436
|
+
notion,
|
|
1437
|
+
dropbox,
|
|
1438
|
+
atlassian
|
|
745
1439
|
};
|
|
746
1440
|
var defineOAuthEnvironment = (oauth) => {
|
|
747
|
-
const env = process.env;
|
|
748
|
-
const clientIdSuffix = `${oauth.toUpperCase()}_CLIENT_ID`;
|
|
749
|
-
const clientSecretSuffix = `${oauth.toUpperCase()}_CLIENT_SECRET`;
|
|
750
1441
|
const loadEnvs = OAuthEnvSchema.safeParse({
|
|
751
|
-
clientId:
|
|
752
|
-
clientSecret:
|
|
1442
|
+
clientId: getEnv(`${oauth.toUpperCase()}_CLIENT_ID`),
|
|
1443
|
+
clientSecret: getEnv(`${oauth.toUpperCase()}_CLIENT_SECRET`)
|
|
753
1444
|
});
|
|
754
1445
|
if (!loadEnvs.success) {
|
|
755
|
-
const msg = JSON.stringify(formatZodError(loadEnvs.error), null, 2);
|
|
1446
|
+
const msg = JSON.stringify({ [oauth]: formatZodError(loadEnvs.error) }, null, 2);
|
|
756
1447
|
throw new AuthInternalError("INVALID_ENVIRONMENT_CONFIGURATION", msg);
|
|
757
1448
|
}
|
|
758
1449
|
return loadEnvs.data;
|
|
@@ -760,24 +1451,64 @@ var defineOAuthEnvironment = (oauth) => {
|
|
|
760
1451
|
var defineOAuthProviderConfig = (config2) => {
|
|
761
1452
|
if (typeof config2 === "string") {
|
|
762
1453
|
const definition = defineOAuthEnvironment(config2);
|
|
763
|
-
const oauthConfig = builtInOAuthProviders[config2];
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
1454
|
+
const oauthConfig = builtInOAuthProviders[config2]();
|
|
1455
|
+
const parsed2 = OAuthProviderCredentialsSchema.safeParse({ ...oauthConfig, ...definition });
|
|
1456
|
+
if (!parsed2.success) {
|
|
1457
|
+
const details = JSON.stringify({ [config2]: formatZodError(parsed2.error) }, null, 2);
|
|
1458
|
+
throw new AuthInternalError(
|
|
1459
|
+
"INVALID_OAUTH_PROVIDER_CONFIGURATION",
|
|
1460
|
+
`Invalid configuration for OAuth provider "${config2}": ${details}`
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
return parsed2.data;
|
|
1464
|
+
}
|
|
1465
|
+
const hasCredentials = config2.clientId && config2.clientSecret;
|
|
1466
|
+
const envConfig = hasCredentials ? {} : defineOAuthEnvironment(config2.id);
|
|
1467
|
+
const parsed = OAuthProviderCredentialsSchema.safeParse({ ...envConfig, ...config2 });
|
|
1468
|
+
if (!parsed.success) {
|
|
1469
|
+
const details = JSON.stringify({ [config2.id]: formatZodError(parsed.error) }, null, 2);
|
|
1470
|
+
throw new AuthInternalError(
|
|
1471
|
+
"INVALID_OAUTH_PROVIDER_CONFIGURATION",
|
|
1472
|
+
`Invalid configuration for OAuth provider "${config2.id}": ${details}`
|
|
1473
|
+
);
|
|
768
1474
|
}
|
|
769
|
-
return
|
|
1475
|
+
return parsed.data;
|
|
770
1476
|
};
|
|
771
1477
|
var createBuiltInOAuthProviders = (oauth = []) => {
|
|
772
1478
|
return oauth.reduce((previous, config2) => {
|
|
773
1479
|
const oauthConfig = defineOAuthProviderConfig(config2);
|
|
1480
|
+
if (oauthConfig.id in previous) {
|
|
1481
|
+
throw new AuthInternalError(
|
|
1482
|
+
"DUPLICATED_OAUTH_PROVIDER_ID",
|
|
1483
|
+
`Duplicate OAuth provider id "${oauthConfig.id}" found. Each provider must have a unique id.`
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
774
1486
|
return { ...previous, [oauthConfig.id]: oauthConfig };
|
|
775
1487
|
}, {});
|
|
776
1488
|
};
|
|
777
1489
|
|
|
778
|
-
// src/
|
|
779
|
-
var
|
|
780
|
-
|
|
1490
|
+
// src/context.ts
|
|
1491
|
+
var createContext = (config2) => {
|
|
1492
|
+
const trustedProxyHeadersEnv = getEnv("TRUSTED_PROXY_HEADERS");
|
|
1493
|
+
const useProxyHeaders = trustedProxyHeadersEnv === void 0 ? config2?.trustedProxyHeaders ?? false : getEnvBoolean("TRUSTED_PROXY_HEADERS");
|
|
1494
|
+
const logger = createProxyLogger(config2);
|
|
1495
|
+
const cookiePrefix = config2?.cookies?.prefix;
|
|
1496
|
+
const cookieOverrides = config2?.cookies?.overrides ?? {};
|
|
1497
|
+
const secureCookieStore = createCookieStore(true, cookiePrefix, cookieOverrides, logger);
|
|
1498
|
+
const standardCookieStore = createCookieStore(false, cookiePrefix, cookieOverrides, logger);
|
|
1499
|
+
return {
|
|
1500
|
+
oauth: createBuiltInOAuthProviders(config2?.oauth),
|
|
1501
|
+
cookies: standardCookieStore,
|
|
1502
|
+
jose: createJoseInstance(config2?.secret),
|
|
1503
|
+
secret: config2?.secret,
|
|
1504
|
+
basePath: config2?.basePath ?? "/auth",
|
|
1505
|
+
trustedProxyHeaders: useProxyHeaders,
|
|
1506
|
+
trustedOrigins: getEnvArray("TRUSTED_ORIGINS").length > 0 ? getEnvArray("TRUSTED_ORIGINS") : config2?.trustedOrigins,
|
|
1507
|
+
logger,
|
|
1508
|
+
cookieConfig: { secure: secureCookieStore, standard: standardCookieStore },
|
|
1509
|
+
baseURL: config2?.baseURL
|
|
1510
|
+
};
|
|
1511
|
+
};
|
|
781
1512
|
|
|
782
1513
|
// src/headers.ts
|
|
783
1514
|
var cacheControl = {
|
|
@@ -786,126 +1517,442 @@ var cacheControl = {
|
|
|
786
1517
|
Expires: "0",
|
|
787
1518
|
Vary: "Cookie"
|
|
788
1519
|
};
|
|
1520
|
+
var contentSecurityPolicy = {
|
|
1521
|
+
"Content-Security-Policy": [
|
|
1522
|
+
"default-src 'none'",
|
|
1523
|
+
"script-src 'self'",
|
|
1524
|
+
"frame-src 'none'",
|
|
1525
|
+
"object-src 'none'",
|
|
1526
|
+
"frame-ancestors 'none'",
|
|
1527
|
+
"base-uri 'none'"
|
|
1528
|
+
].join("; ")
|
|
1529
|
+
};
|
|
1530
|
+
var secureHeaders = {
|
|
1531
|
+
"X-Content-Type-Options": "nosniff",
|
|
1532
|
+
"X-Frame-Options": "DENY",
|
|
1533
|
+
"Referrer-Policy": "strict-origin-when-cross-origin"
|
|
1534
|
+
};
|
|
1535
|
+
var secureApiHeaders = {
|
|
1536
|
+
...cacheControl,
|
|
1537
|
+
...contentSecurityPolicy,
|
|
1538
|
+
...secureHeaders
|
|
1539
|
+
};
|
|
789
1540
|
|
|
790
|
-
// src/
|
|
791
|
-
var
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1541
|
+
// src/api/signIn.ts
|
|
1542
|
+
var import_router2 = require("@aura-stack/router");
|
|
1543
|
+
|
|
1544
|
+
// src/secure.ts
|
|
1545
|
+
var generateSecure = (length = 32) => {
|
|
1546
|
+
return import_jose2.base64url.encode((0, import_crypto.getRandomBytes)(length));
|
|
1547
|
+
};
|
|
1548
|
+
var createSecretValue = (length = 32) => {
|
|
1549
|
+
return import_jose2.base64url.encode((0, import_crypto.getRandomBytes)(length));
|
|
1550
|
+
};
|
|
1551
|
+
var createHash = async (data) => {
|
|
1552
|
+
const subtle = (0, import_crypto.getSubtleCrypto)();
|
|
1553
|
+
const digest = await subtle.digest("SHA-256", import_crypto.encoder.encode(data));
|
|
1554
|
+
return import_jose2.base64url.encode(new Uint8Array(digest));
|
|
1555
|
+
};
|
|
1556
|
+
var createPKCE = async (verifier) => {
|
|
1557
|
+
const byteLength = verifier ? void 0 : Math.floor(Math.random() * (96 - 32 + 1) + 32);
|
|
1558
|
+
const codeVerifier = verifier ?? generateSecure(byteLength ?? 64);
|
|
1559
|
+
if (codeVerifier.length < 43 || codeVerifier.length > 128) {
|
|
1560
|
+
throw new AuthSecurityError("PKCE_VERIFIER_INVALID", "The code verifier must be between 43 and 128 characters in length.");
|
|
796
1561
|
}
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
return
|
|
808
|
-
}
|
|
809
|
-
|
|
1562
|
+
const codeChallenge = await createHash(codeVerifier);
|
|
1563
|
+
return { codeVerifier, codeChallenge, method: "S256" };
|
|
1564
|
+
};
|
|
1565
|
+
var createCSRF = async (jose, csrfCookie) => {
|
|
1566
|
+
try {
|
|
1567
|
+
const token = generateSecure(32);
|
|
1568
|
+
if (csrfCookie) {
|
|
1569
|
+
await jose.verifyJWS(csrfCookie, jwtVerificationOptions);
|
|
1570
|
+
return csrfCookie;
|
|
1571
|
+
}
|
|
1572
|
+
return jose.signJWS({ token });
|
|
1573
|
+
} catch {
|
|
1574
|
+
const token = generateSecure(32);
|
|
1575
|
+
return jose.signJWS({ token });
|
|
1576
|
+
}
|
|
1577
|
+
};
|
|
1578
|
+
var verifyCSRF = async (jose, cookie, header) => {
|
|
1579
|
+
try {
|
|
1580
|
+
const cookiePayload = await jose.verifyJWS(cookie, jwtVerificationOptions);
|
|
1581
|
+
const headerPayload = await jose.verifyJWS(header, jwtVerificationOptions);
|
|
1582
|
+
if (!isJWTPayloadWithToken(cookiePayload)) {
|
|
1583
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Cookie payload missing token field.");
|
|
1584
|
+
}
|
|
1585
|
+
if (!isJWTPayloadWithToken(headerPayload)) {
|
|
1586
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "Header payload missing token field.");
|
|
1587
|
+
}
|
|
1588
|
+
if (!equals(cookiePayload.token.length, headerPayload.token.length)) {
|
|
1589
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
1590
|
+
}
|
|
1591
|
+
if (!timingSafeEqual(cookiePayload.token, headerPayload.token)) {
|
|
1592
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
1593
|
+
}
|
|
1594
|
+
return true;
|
|
1595
|
+
} catch {
|
|
1596
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "The CSRF tokens do not match.");
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1600
|
+
// src/actions/signIn/authorization-url.ts
|
|
1601
|
+
var setSearchParams = (url, params) => {
|
|
1602
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1603
|
+
if (value !== void 0 && value !== "") {
|
|
1604
|
+
url.searchParams.set(key, value);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
var buildAuthorizationURL = (oauth, redirect_uri, state, code_challenge, code_challenge_method) => {
|
|
1609
|
+
const authorizeConfig = oauth.authorize;
|
|
1610
|
+
const baseURL = typeof authorizeConfig === "string" ? authorizeConfig : authorizeConfig?.url ?? oauth.authorizeURL;
|
|
1611
|
+
if (!baseURL) {
|
|
1612
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "Missing authorization URL in OAuth provider configuration.");
|
|
1613
|
+
}
|
|
1614
|
+
const url = new URL(baseURL);
|
|
1615
|
+
const authorizeParams = typeof authorizeConfig === "string" ? void 0 : authorizeConfig?.params;
|
|
1616
|
+
setSearchParams(url, {
|
|
1617
|
+
response_type: authorizeParams?.responseType ?? oauth.responseType ?? "code",
|
|
1618
|
+
client_id: oauth.clientId,
|
|
1619
|
+
redirect_uri,
|
|
1620
|
+
state,
|
|
1621
|
+
code_challenge,
|
|
1622
|
+
code_challenge_method,
|
|
1623
|
+
scope: authorizeParams?.scope ?? oauth.scope,
|
|
1624
|
+
prompt: authorizeParams?.prompt,
|
|
1625
|
+
response_mode: authorizeParams?.responseMode,
|
|
1626
|
+
login_hint: authorizeParams?.loginHint,
|
|
1627
|
+
nonce: authorizeParams?.nonce,
|
|
1628
|
+
display: authorizeParams?.display,
|
|
1629
|
+
audience: authorizeParams?.audience
|
|
1630
|
+
});
|
|
1631
|
+
return url.toString();
|
|
1632
|
+
};
|
|
1633
|
+
var createAuthorizationURL = async (oauth, redirectURI, ctx) => {
|
|
1634
|
+
const state = createSecretValue();
|
|
1635
|
+
const { codeVerifier, codeChallenge, method } = await createPKCE();
|
|
1636
|
+
const authorization = buildAuthorizationURL(oauth, redirectURI, state, codeChallenge, method);
|
|
1637
|
+
const parsed = OAuthAuthorization.safeParse({ ...oauth, redirectURI, state, codeChallenge, codeChallengeMethod: method });
|
|
1638
|
+
if (!parsed.success) {
|
|
1639
|
+
ctx?.logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
1640
|
+
structuredData: {
|
|
1641
|
+
scope: oauth?.scope ?? "",
|
|
1642
|
+
redirect_uri: redirectURI,
|
|
1643
|
+
has_state: Boolean(state),
|
|
1644
|
+
has_code_challenge: Boolean(codeChallenge),
|
|
1645
|
+
code_challenge_method: method
|
|
1646
|
+
}
|
|
1647
|
+
});
|
|
1648
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "The OAuth provider configuration is invalid.");
|
|
810
1649
|
}
|
|
1650
|
+
return {
|
|
1651
|
+
authorization,
|
|
1652
|
+
state,
|
|
1653
|
+
codeVerifier,
|
|
1654
|
+
method
|
|
1655
|
+
};
|
|
811
1656
|
};
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1657
|
+
|
|
1658
|
+
// src/actions/signIn/authorization.ts
|
|
1659
|
+
var getTrustedOrigins = async (request, trustedOrigins) => {
|
|
1660
|
+
if (!trustedOrigins) return [];
|
|
1661
|
+
const raw = typeof trustedOrigins === "function" ? await trustedOrigins(request) : trustedOrigins;
|
|
1662
|
+
return Array.isArray(raw) ? raw : typeof raw === "string" ? [raw] : [];
|
|
1663
|
+
};
|
|
1664
|
+
var getBaseURL2 = async ({
|
|
1665
|
+
ctx,
|
|
1666
|
+
request,
|
|
1667
|
+
headers: headersInit
|
|
1668
|
+
}) => {
|
|
1669
|
+
const origin = getEnv("BASE_URL") || ctx?.baseURL;
|
|
1670
|
+
if (origin && origin !== "/") return origin;
|
|
1671
|
+
if (ctx?.trustedProxyHeaders) {
|
|
1672
|
+
const headers = headersInit && new Headers(headersInit) || request?.headers;
|
|
1673
|
+
const protocol = headers?.get("Forwarded")?.match(/proto=([^;]+)/i)?.[1] ?? headers?.get("X-Forwarded-Proto") ?? "http";
|
|
1674
|
+
const host = headers?.get("Host") ?? headers?.get("Forwarded")?.match(/host=([^;]+)/i)?.[1] ?? headers?.get("X-Forwarded-Host") ?? null;
|
|
1675
|
+
if (host) return `${protocol}://${host}`;
|
|
1676
|
+
throw new AuthInternalError(
|
|
1677
|
+
"INVALID_OAUTH_CONFIGURATION",
|
|
1678
|
+
"The URL cannot be constructed. Please set the BASE_URL environment variable or provide trusted proxy host headers."
|
|
1679
|
+
);
|
|
1680
|
+
}
|
|
1681
|
+
try {
|
|
1682
|
+
return new URL(request?.url ?? "not-found").origin;
|
|
1683
|
+
} catch (error) {
|
|
1684
|
+
throw new AuthInternalError(
|
|
1685
|
+
"INVALID_OAUTH_CONFIGURATION",
|
|
1686
|
+
"The URL cannot be constructed. Please set the BASE_URL environment variable or enable trustedProxyHeaders.",
|
|
1687
|
+
{ cause: error }
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
815
1690
|
};
|
|
816
|
-
var
|
|
1691
|
+
var getOriginURL = async (request, context) => {
|
|
1692
|
+
const trustedOrigins = await getTrustedOrigins(request, context?.trustedOrigins);
|
|
1693
|
+
trustedOrigins.push(new URL(request.url).origin);
|
|
1694
|
+
const origin = await getBaseURL2({ request, ctx: context });
|
|
1695
|
+
if (!isTrustedOrigin(origin, trustedOrigins)) {
|
|
1696
|
+
context?.logger?.log("UNTRUSTED_ORIGIN", { structuredData: { origin } });
|
|
1697
|
+
throw new AuthInternalError("UNTRUSTED_ORIGIN", "The constructed origin URL is not trusted.");
|
|
1698
|
+
}
|
|
1699
|
+
return origin;
|
|
1700
|
+
};
|
|
1701
|
+
var createRedirectURI = async (request, oauth, context) => {
|
|
1702
|
+
const origin = await getOriginURL(request, context);
|
|
1703
|
+
return `${origin}${context.basePath}/callback/${oauth}`;
|
|
1704
|
+
};
|
|
1705
|
+
var createSignInURL = async ({
|
|
1706
|
+
request,
|
|
1707
|
+
oauth,
|
|
1708
|
+
ctx,
|
|
1709
|
+
redirectTo
|
|
1710
|
+
}) => {
|
|
1711
|
+
const origin = await getOriginURL(request, ctx);
|
|
1712
|
+
const searchParams = new URLSearchParams();
|
|
1713
|
+
if (redirectTo !== void 0) searchParams.set("redirectTo", String(redirectTo));
|
|
1714
|
+
return `${origin}${ctx.basePath}/signIn/${oauth}?${searchParams.toString()}`;
|
|
1715
|
+
};
|
|
1716
|
+
var createRedirectTo = async (request, redirectTo, context) => {
|
|
817
1717
|
try {
|
|
818
1718
|
const headers = request.headers;
|
|
819
|
-
const
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
if (
|
|
824
|
-
|
|
1719
|
+
const requestOrigin = await getOriginURL(request, context);
|
|
1720
|
+
const origins = await getTrustedOrigins(request, context?.trustedOrigins);
|
|
1721
|
+
const validateURL = (url) => {
|
|
1722
|
+
if (!isRelativeURL(url) && !isValidURL(url)) return "/";
|
|
1723
|
+
if (isRelativeURL(url)) return url;
|
|
1724
|
+
if (origins.length > 0) {
|
|
1725
|
+
if (isTrustedOrigin(url, origins)) {
|
|
1726
|
+
const urlOrigin = new URL(url).origin;
|
|
1727
|
+
for (const pattern of origins) {
|
|
1728
|
+
const regex = patternToRegex(pattern);
|
|
1729
|
+
if (regex?.test(urlOrigin)) {
|
|
1730
|
+
return isSameOrigin(url, request.url) ? extractPath(url) : url;
|
|
1731
|
+
}
|
|
1732
|
+
if (isValidURL(pattern) && equals(new URL(pattern).origin, urlOrigin)) return url;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
context?.logger?.log("OPEN_REDIRECT_ATTACK");
|
|
1736
|
+
return "/";
|
|
825
1737
|
}
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
throw new AuthSecurityError(
|
|
829
|
-
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
830
|
-
"The redirectTo parameter does not match the hosted origin."
|
|
831
|
-
);
|
|
1738
|
+
if (isSameOrigin(url, requestOrigin)) {
|
|
1739
|
+
return extractPath(url);
|
|
832
1740
|
}
|
|
833
|
-
|
|
1741
|
+
context?.logger?.log("OPEN_REDIRECT_ATTACK");
|
|
1742
|
+
return "/";
|
|
1743
|
+
};
|
|
1744
|
+
return validateURL(redirectTo ?? headers.get("Referer") ?? headers.get("Origin") ?? "/");
|
|
1745
|
+
} catch (error) {
|
|
1746
|
+
context?.logger?.log("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED");
|
|
1747
|
+
return "/";
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// src/api/signIn.ts
|
|
1752
|
+
var signIn = async (oauth, {
|
|
1753
|
+
ctx,
|
|
1754
|
+
headers: headersInit,
|
|
1755
|
+
redirectTo = "/",
|
|
1756
|
+
redirect,
|
|
1757
|
+
request: requestInit
|
|
1758
|
+
}) => {
|
|
1759
|
+
const headers = new Headers(headersInit);
|
|
1760
|
+
const provider = ctx.oauth[oauth];
|
|
1761
|
+
if (!provider) {
|
|
1762
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", `The OAuth provider "${oauth}" is not configured.`);
|
|
1763
|
+
}
|
|
1764
|
+
let request = requestInit;
|
|
1765
|
+
if (!request) {
|
|
1766
|
+
const origin = await getBaseURL2({ ctx, headers });
|
|
1767
|
+
const url = `${origin}${ctx.basePath}/signIn/${oauth}`;
|
|
1768
|
+
request = new Request(url, { headers });
|
|
1769
|
+
}
|
|
1770
|
+
if (redirect === false) {
|
|
1771
|
+
const signInURL = await createSignInURL({ request, oauth, ctx, redirectTo });
|
|
1772
|
+
return { redirect: false, signInURL };
|
|
1773
|
+
}
|
|
1774
|
+
const redirectURI = await createRedirectURI(request, oauth, ctx);
|
|
1775
|
+
const redirectToValue = await createRedirectTo(request, redirectTo, ctx);
|
|
1776
|
+
const { authorization, state, codeVerifier } = await createAuthorizationURL(provider, redirectURI, ctx);
|
|
1777
|
+
ctx?.logger?.log("SIGN_IN_INITIATED", {
|
|
1778
|
+
structuredData: { oauth_provider: oauth }
|
|
1779
|
+
});
|
|
1780
|
+
const headersList = new import_router2.HeadersBuilder(cacheControl).setHeader("Location", authorization).setCookie(ctx.cookies.state.name, state, ctx.cookies.state.attributes).setCookie(ctx.cookies.redirectURI.name, redirectURI, ctx.cookies.redirectURI.attributes).setCookie(ctx.cookies.redirectTo.name, redirectToValue, ctx.cookies.redirectTo.attributes).setCookie(ctx.cookies.codeVerifier.name, codeVerifier, ctx.cookies.codeVerifier.attributes).toHeaders();
|
|
1781
|
+
return Response.json(
|
|
1782
|
+
{ redirect: redirect ?? true, signInURL: authorization },
|
|
1783
|
+
{
|
|
1784
|
+
status: redirect ?? true ? 302 : 200,
|
|
1785
|
+
headers: headersList
|
|
834
1786
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1787
|
+
);
|
|
1788
|
+
};
|
|
1789
|
+
|
|
1790
|
+
// src/api/signOut.ts
|
|
1791
|
+
var import_router3 = require("@aura-stack/router");
|
|
1792
|
+
var signOut = async ({
|
|
1793
|
+
ctx,
|
|
1794
|
+
headers: headersInit,
|
|
1795
|
+
redirectTo = "/",
|
|
1796
|
+
skipCSRFCheck = false
|
|
1797
|
+
}) => {
|
|
1798
|
+
const headers = new Headers(headersInit);
|
|
1799
|
+
const header = headers.get("X-CSRF-Token");
|
|
1800
|
+
let session = null;
|
|
1801
|
+
let csrfToken = null;
|
|
1802
|
+
try {
|
|
1803
|
+
session = getCookie(headers, ctx.cookies.sessionToken.name);
|
|
1804
|
+
} catch {
|
|
1805
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1806
|
+
}
|
|
1807
|
+
try {
|
|
1808
|
+
csrfToken = getCookie(headers, ctx.cookies.csrfToken.name);
|
|
1809
|
+
} catch {
|
|
1810
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
1811
|
+
}
|
|
1812
|
+
ctx?.logger?.log("SIGN_OUT_ATTEMPT", {
|
|
1813
|
+
structuredData: {
|
|
1814
|
+
has_session: Boolean(session),
|
|
1815
|
+
has_csrf_token: Boolean(csrfToken),
|
|
1816
|
+
has_csrf_header: Boolean(header),
|
|
1817
|
+
skip_csrf_check: skipCSRFCheck
|
|
844
1818
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
1819
|
+
});
|
|
1820
|
+
if (!session) {
|
|
1821
|
+
ctx?.logger?.log("SESSION_TOKEN_MISSING");
|
|
1822
|
+
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1823
|
+
}
|
|
1824
|
+
if (!skipCSRFCheck) {
|
|
1825
|
+
if (!csrfToken) {
|
|
1826
|
+
ctx?.logger?.log("CSRF_TOKEN_MISSING");
|
|
1827
|
+
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
851
1828
|
}
|
|
852
|
-
|
|
1829
|
+
if (!header) {
|
|
1830
|
+
ctx?.logger?.log("CSRF_HEADER_MISSING");
|
|
1831
|
+
throw new AuthSecurityError("CSRF_HEADER_MISSING", "The CSRF header is missing.");
|
|
1832
|
+
}
|
|
1833
|
+
try {
|
|
1834
|
+
await verifyCSRF(ctx.jose, csrfToken, header);
|
|
1835
|
+
} catch (error) {
|
|
1836
|
+
ctx?.logger?.log("CSRF_TOKEN_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
1837
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "CSRF token verification failed");
|
|
1838
|
+
}
|
|
1839
|
+
ctx?.logger?.log("SIGN_OUT_CSRF_VERIFIED");
|
|
1840
|
+
} else {
|
|
1841
|
+
try {
|
|
1842
|
+
await ctx.jose.verifyJWS(csrfToken);
|
|
1843
|
+
} catch (error) {
|
|
1844
|
+
ctx?.logger?.log("CSRF_TOKEN_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
1845
|
+
throw new AuthSecurityError("CSRF_TOKEN_INVALID", "CSRF token verification failed");
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
try {
|
|
1849
|
+
await ctx.jose.decodeJWT(session);
|
|
1850
|
+
ctx?.logger?.log("SIGN_OUT_SUCCESS");
|
|
853
1851
|
} catch (error) {
|
|
854
|
-
|
|
855
|
-
|
|
1852
|
+
ctx?.logger?.log("INVALID_JWT_TOKEN", { structuredData: { error_type: getErrorName(error) } });
|
|
1853
|
+
}
|
|
1854
|
+
const headersList = new import_router3.HeadersBuilder(secureApiHeaders).setHeader("Location", redirectTo).setCookie(ctx.cookies.csrfToken.name, "", expiredCookieAttributes).setCookie(ctx.cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
1855
|
+
return Response.json(
|
|
1856
|
+
{ redirect: Boolean(redirectTo), url: redirectTo },
|
|
1857
|
+
{
|
|
1858
|
+
status: 202,
|
|
1859
|
+
headers: headersList
|
|
856
1860
|
}
|
|
857
|
-
|
|
1861
|
+
);
|
|
1862
|
+
};
|
|
1863
|
+
|
|
1864
|
+
// src/api/getSession.ts
|
|
1865
|
+
var getSession = async ({ ctx, headers }) => {
|
|
1866
|
+
try {
|
|
1867
|
+
const session = getCookie(new Headers(headers), ctx.cookies.sessionToken.name);
|
|
1868
|
+
const decoded = await ctx.jose.decodeJWT(session);
|
|
1869
|
+
ctx?.logger?.log("AUTH_SESSION_VALID");
|
|
1870
|
+
const { exp, iat, jti, nbf, aud, iss, ...user } = decoded;
|
|
1871
|
+
return {
|
|
1872
|
+
session: {
|
|
1873
|
+
user,
|
|
1874
|
+
expires: toISOString(exp * 1e3)
|
|
1875
|
+
},
|
|
1876
|
+
authenticated: true
|
|
1877
|
+
};
|
|
1878
|
+
} catch (error) {
|
|
1879
|
+
ctx?.logger?.log("AUTH_SESSION_INVALID", { structuredData: { error_type: getErrorName(error) } });
|
|
1880
|
+
return { session: null, authenticated: false };
|
|
858
1881
|
}
|
|
859
1882
|
};
|
|
860
1883
|
|
|
1884
|
+
// src/api/createApi.ts
|
|
1885
|
+
var createAuthAPI = (ctx) => {
|
|
1886
|
+
return {
|
|
1887
|
+
getSession: async (options2) => {
|
|
1888
|
+
const session = await getSession({ ctx, headers: options2.headers });
|
|
1889
|
+
return session;
|
|
1890
|
+
},
|
|
1891
|
+
signIn: async (oauth, options2) => {
|
|
1892
|
+
return signIn(oauth, {
|
|
1893
|
+
ctx,
|
|
1894
|
+
headers: options2?.headers,
|
|
1895
|
+
request: options2?.request,
|
|
1896
|
+
redirect: options2?.redirect,
|
|
1897
|
+
redirectTo: options2?.redirectTo
|
|
1898
|
+
});
|
|
1899
|
+
},
|
|
1900
|
+
signOut: async (options2) => {
|
|
1901
|
+
const redirectTo = validateRedirectTo(options2?.redirectTo ?? "/");
|
|
1902
|
+
return signOut({ ctx, headers: options2.headers, redirectTo, skipCSRFCheck: true });
|
|
1903
|
+
}
|
|
1904
|
+
};
|
|
1905
|
+
};
|
|
1906
|
+
|
|
861
1907
|
// src/actions/signIn/signIn.ts
|
|
1908
|
+
var import_v42 = require("zod/v4");
|
|
1909
|
+
var import_router4 = require("@aura-stack/router");
|
|
862
1910
|
var signInConfig = (oauth) => {
|
|
863
|
-
return (0,
|
|
1911
|
+
return (0, import_router4.createEndpointConfig)("/signIn/:oauth", {
|
|
864
1912
|
schemas: {
|
|
865
|
-
params:
|
|
866
|
-
oauth:
|
|
1913
|
+
params: import_v42.z.object({
|
|
1914
|
+
oauth: import_v42.z.enum(
|
|
867
1915
|
Object.keys(oauth),
|
|
868
1916
|
"The OAuth provider is not supported or invalid."
|
|
869
1917
|
)
|
|
870
1918
|
}),
|
|
871
|
-
searchParams:
|
|
872
|
-
|
|
1919
|
+
searchParams: import_v42.z.object({
|
|
1920
|
+
redirect: import_v42.z.stringbool().optional().default(true),
|
|
1921
|
+
redirectTo: import_v42.z.string().optional()
|
|
873
1922
|
})
|
|
874
1923
|
}
|
|
875
1924
|
});
|
|
876
1925
|
};
|
|
877
1926
|
var signInAction = (oauth) => {
|
|
878
|
-
return (0,
|
|
1927
|
+
return (0, import_router4.createEndpoint)(
|
|
879
1928
|
"GET",
|
|
880
1929
|
"/signIn/:oauth",
|
|
881
1930
|
async (ctx) => {
|
|
882
1931
|
const {
|
|
883
1932
|
request,
|
|
884
1933
|
params: { oauth: oauth2 },
|
|
885
|
-
searchParams: { redirectTo },
|
|
886
|
-
context
|
|
1934
|
+
searchParams: { redirectTo, redirect },
|
|
1935
|
+
context
|
|
887
1936
|
} = ctx;
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
{
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
}
|
|
900
|
-
);
|
|
1937
|
+
const signInResult = await signIn(oauth2, {
|
|
1938
|
+
ctx: context,
|
|
1939
|
+
headers: request.headers,
|
|
1940
|
+
redirect,
|
|
1941
|
+
redirectTo,
|
|
1942
|
+
request
|
|
1943
|
+
});
|
|
1944
|
+
if (!redirect) {
|
|
1945
|
+
return Response.json(signInResult, { status: 200 });
|
|
1946
|
+
}
|
|
1947
|
+
return signInResult;
|
|
901
1948
|
},
|
|
902
1949
|
signInConfig(oauth)
|
|
903
1950
|
);
|
|
904
1951
|
};
|
|
905
1952
|
|
|
906
1953
|
// src/actions/callback/callback.ts
|
|
907
|
-
var
|
|
908
|
-
var
|
|
1954
|
+
var import_v43 = require("zod/v4");
|
|
1955
|
+
var import_router5 = require("@aura-stack/router");
|
|
909
1956
|
|
|
910
1957
|
// src/request.ts
|
|
911
1958
|
var fetchAsync = async (url, options2 = {}, timeout = 5e3) => {
|
|
@@ -928,96 +1975,165 @@ var getDefaultUserInfo = (profile) => {
|
|
|
928
1975
|
image: profile?.image ?? profile?.picture
|
|
929
1976
|
};
|
|
930
1977
|
};
|
|
931
|
-
var getUserInfo = async (oauthConfig, accessToken) => {
|
|
932
|
-
const
|
|
1978
|
+
var getUserInfo = async (oauthConfig, accessToken, logger) => {
|
|
1979
|
+
const userInfoConfig = oauthConfig.userInfo;
|
|
1980
|
+
const userinfoURL = typeof userInfoConfig === "string" ? userInfoConfig : userInfoConfig.url;
|
|
1981
|
+
const extraHeaders = typeof userInfoConfig === "string" ? void 0 : userInfoConfig.headers;
|
|
1982
|
+
const method = typeof userInfoConfig === "string" ? "GET" : (userInfoConfig.method ?? "GET").toUpperCase();
|
|
933
1983
|
try {
|
|
934
|
-
|
|
935
|
-
|
|
1984
|
+
logger?.log("OAUTH_USERINFO_REQUEST_INITIATED", {
|
|
1985
|
+
structuredData: {
|
|
1986
|
+
endpoint: userinfoURL
|
|
1987
|
+
}
|
|
1988
|
+
});
|
|
1989
|
+
const response = await fetchAsync(userinfoURL, {
|
|
1990
|
+
method,
|
|
936
1991
|
headers: {
|
|
1992
|
+
"User-Agent": `Aura Auth/${AURA_AUTH_VERSION}`,
|
|
937
1993
|
Accept: "application/json",
|
|
938
|
-
Authorization: `Bearer ${accessToken}
|
|
1994
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1995
|
+
...extraHeaders ?? {}
|
|
939
1996
|
}
|
|
940
1997
|
});
|
|
1998
|
+
if (!response.ok) {
|
|
1999
|
+
logger?.log("OAUTH_USERINFO_INVALID_RESPONSE");
|
|
2000
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "Invalid userinfo response format");
|
|
2001
|
+
}
|
|
941
2002
|
const json = await response.json();
|
|
942
2003
|
const { success, data } = OAuthErrorResponse.safeParse(json);
|
|
943
2004
|
if (success) {
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
2005
|
+
logger?.log("OAUTH_USERINFO_ERROR", {
|
|
2006
|
+
message: "Error response received from OAuth userinfo endpoint",
|
|
2007
|
+
structuredData: {
|
|
2008
|
+
error: data.error,
|
|
2009
|
+
error_description: data.error_description ?? ""
|
|
2010
|
+
}
|
|
2011
|
+
});
|
|
2012
|
+
throw new OAuthProtocolError("INVALID_REQUEST", "An error was received from the OAuth userinfo endpoint.");
|
|
948
2013
|
}
|
|
2014
|
+
logger?.log("OAUTH_USERINFO_SUCCESS");
|
|
949
2015
|
return oauthConfig?.profile ? oauthConfig.profile(json) : getDefaultUserInfo(json);
|
|
950
2016
|
} catch (error) {
|
|
951
2017
|
if (isOAuthProtocolError(error)) {
|
|
952
2018
|
throw error;
|
|
953
2019
|
}
|
|
2020
|
+
logger?.log("OAUTH_USERINFO_REQUEST_FAILED");
|
|
954
2021
|
if (isNativeError(error)) {
|
|
955
|
-
throw new OAuthProtocolError("
|
|
2022
|
+
throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information from OAuth provider", "", {
|
|
2023
|
+
cause: error
|
|
2024
|
+
});
|
|
956
2025
|
}
|
|
957
|
-
throw new OAuthProtocolError("
|
|
2026
|
+
throw new OAuthProtocolError("SERVER_ERROR", "Failed to fetch user information", "", { cause: error });
|
|
958
2027
|
}
|
|
959
2028
|
};
|
|
960
2029
|
|
|
961
2030
|
// src/actions/callback/access-token.ts
|
|
962
|
-
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier) => {
|
|
963
|
-
const
|
|
964
|
-
if (!
|
|
965
|
-
|
|
966
|
-
|
|
2031
|
+
var createAccessToken = async (oauthConfig, redirectURI, code, codeVerifier, logger) => {
|
|
2032
|
+
const { accessToken, clientId, clientSecret } = oauthConfig;
|
|
2033
|
+
if (!clientId || !clientSecret || !redirectURI || !code || !codeVerifier || !accessToken) {
|
|
2034
|
+
logger?.log("INVALID_OAUTH_CONFIGURATION", {
|
|
2035
|
+
structuredData: {
|
|
2036
|
+
has_client_id: Boolean(clientId),
|
|
2037
|
+
has_client_secret: Boolean(clientSecret),
|
|
2038
|
+
has_access_token: Boolean(accessToken),
|
|
2039
|
+
has_redirect_uri: Boolean(redirectURI),
|
|
2040
|
+
has_code: Boolean(code),
|
|
2041
|
+
has_code_verifier: Boolean(codeVerifier)
|
|
2042
|
+
}
|
|
2043
|
+
});
|
|
2044
|
+
throw new AuthInternalError("INVALID_OAUTH_CONFIGURATION", "The OAuth provider configuration is invalid.");
|
|
967
2045
|
}
|
|
968
|
-
const
|
|
2046
|
+
const tokenURL = typeof accessToken === "string" ? accessToken : accessToken.url;
|
|
2047
|
+
const extraHeaders = typeof accessToken === "string" ? void 0 : accessToken.headers;
|
|
969
2048
|
try {
|
|
970
|
-
|
|
2049
|
+
logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_INITIATED", {
|
|
2050
|
+
structuredData: {
|
|
2051
|
+
has_client_id: Boolean(clientId),
|
|
2052
|
+
redirect_uri: redirectURI,
|
|
2053
|
+
grant_type: "authorization_code"
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
const response = await fetchAsync(tokenURL, {
|
|
971
2057
|
method: "POST",
|
|
972
2058
|
headers: {
|
|
2059
|
+
...extraHeaders ?? {},
|
|
973
2060
|
Accept: "application/json",
|
|
974
2061
|
"Content-Type": "application/x-www-form-urlencoded"
|
|
975
2062
|
},
|
|
976
2063
|
body: new URLSearchParams({
|
|
977
2064
|
client_id: clientId,
|
|
978
2065
|
client_secret: clientSecret,
|
|
979
|
-
code
|
|
980
|
-
redirect_uri:
|
|
2066
|
+
code,
|
|
2067
|
+
redirect_uri: redirectURI,
|
|
981
2068
|
grant_type: "authorization_code",
|
|
982
2069
|
code_verifier: codeVerifier
|
|
983
2070
|
}).toString()
|
|
984
2071
|
});
|
|
2072
|
+
if (!response.ok) {
|
|
2073
|
+
logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
|
|
2074
|
+
throw new OAuthProtocolError("invalid_request", "Invalid access token response");
|
|
2075
|
+
}
|
|
985
2076
|
const json = await response.json();
|
|
986
2077
|
const token = OAuthAccessTokenResponse.safeParse(json);
|
|
987
2078
|
if (!token.success) {
|
|
988
2079
|
const { success, data } = OAuthAccessTokenErrorResponse.safeParse(json);
|
|
989
2080
|
if (!success) {
|
|
990
|
-
|
|
2081
|
+
logger?.log("INVALID_OAUTH_ACCESS_TOKEN_RESPONSE");
|
|
2082
|
+
throw new OAuthProtocolError("invalid_request", "Invalid access token response format");
|
|
991
2083
|
}
|
|
992
|
-
|
|
2084
|
+
logger?.log("OAUTH_ACCESS_TOKEN_ERROR", {
|
|
2085
|
+
structuredData: {
|
|
2086
|
+
error: data.error,
|
|
2087
|
+
error_description: data.error_description ?? ""
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
throw new OAuthProtocolError("INVALID_ACCESS_TOKEN", "Failed to retrieve access token");
|
|
993
2091
|
}
|
|
2092
|
+
logger?.log("OAUTH_ACCESS_TOKEN_SUCCESS");
|
|
994
2093
|
return token.data;
|
|
995
2094
|
} catch (error) {
|
|
2095
|
+
logger?.log("OAUTH_ACCESS_TOKEN_REQUEST_FAILED");
|
|
2096
|
+
if (error instanceof Error) {
|
|
2097
|
+
throw new OAuthProtocolError("server_error", "Failed to communicate with OAuth provider", "", { cause: error });
|
|
2098
|
+
}
|
|
996
2099
|
throw error;
|
|
997
2100
|
}
|
|
998
2101
|
};
|
|
999
2102
|
|
|
1000
2103
|
// src/actions/callback/callback.ts
|
|
1001
2104
|
var callbackConfig = (oauth) => {
|
|
1002
|
-
return (0,
|
|
2105
|
+
return (0, import_router5.createEndpointConfig)("/callback/:oauth", {
|
|
1003
2106
|
schemas: {
|
|
1004
|
-
params:
|
|
1005
|
-
oauth:
|
|
2107
|
+
params: import_v43.z.object({
|
|
2108
|
+
oauth: import_v43.z.enum(
|
|
1006
2109
|
Object.keys(oauth),
|
|
1007
2110
|
"The OAuth provider is not supported or invalid."
|
|
1008
2111
|
)
|
|
1009
2112
|
}),
|
|
1010
|
-
searchParams:
|
|
1011
|
-
code:
|
|
1012
|
-
state:
|
|
2113
|
+
searchParams: import_v43.z.object({
|
|
2114
|
+
code: import_v43.z.string("Missing code parameter in the OAuth authorization response."),
|
|
2115
|
+
state: import_v43.z.string("Missing state parameter in the OAuth authorization response.")
|
|
1013
2116
|
})
|
|
1014
2117
|
},
|
|
1015
|
-
|
|
2118
|
+
use: [
|
|
1016
2119
|
(ctx) => {
|
|
1017
|
-
const
|
|
2120
|
+
const {
|
|
2121
|
+
searchParams,
|
|
2122
|
+
context: { logger }
|
|
2123
|
+
} = ctx;
|
|
2124
|
+
const response = OAuthAuthorizationErrorResponse.safeParse(searchParams);
|
|
1018
2125
|
if (response.success) {
|
|
1019
2126
|
const { error, error_description } = response.data;
|
|
1020
|
-
|
|
2127
|
+
const criticalAuthErrors = ["access_denied", "server_error"];
|
|
2128
|
+
const severity = criticalAuthErrors.includes(error.toLowerCase()) ? "critical" : "warning";
|
|
2129
|
+
logger?.log("OAUTH_AUTHORIZATION_ERROR", {
|
|
2130
|
+
severity,
|
|
2131
|
+
structuredData: {
|
|
2132
|
+
error,
|
|
2133
|
+
error_description: error_description ?? ""
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
throw new OAuthProtocolError(error, error_description || "OAuth Authorization Error");
|
|
1021
2137
|
}
|
|
1022
2138
|
return ctx;
|
|
1023
2139
|
}
|
|
@@ -1025,7 +2141,7 @@ var callbackConfig = (oauth) => {
|
|
|
1025
2141
|
});
|
|
1026
2142
|
};
|
|
1027
2143
|
var callbackAction = (oauth) => {
|
|
1028
|
-
return (0,
|
|
2144
|
+
return (0, import_router5.createEndpoint)(
|
|
1029
2145
|
"GET",
|
|
1030
2146
|
"/callback/:oauth",
|
|
1031
2147
|
async (ctx) => {
|
|
@@ -1033,31 +2149,54 @@ var callbackAction = (oauth) => {
|
|
|
1033
2149
|
request,
|
|
1034
2150
|
params: { oauth: oauth2 },
|
|
1035
2151
|
searchParams: { code, state },
|
|
1036
|
-
context
|
|
2152
|
+
context
|
|
1037
2153
|
} = ctx;
|
|
2154
|
+
const { oauth: providers, cookies, jose, logger, trustedOrigins } = context;
|
|
1038
2155
|
const oauthConfig = providers[oauth2];
|
|
1039
2156
|
const cookieState = getCookie(request, cookies.state.name);
|
|
2157
|
+
const codeVerifier = getCookie(request, cookies.codeVerifier.name);
|
|
1040
2158
|
const cookieRedirectTo = getCookie(request, cookies.redirectTo.name);
|
|
1041
2159
|
const cookieRedirectURI = getCookie(request, cookies.redirectURI.name);
|
|
1042
|
-
|
|
1043
|
-
|
|
2160
|
+
if (!timingSafeEqual(cookieState, state)) {
|
|
2161
|
+
logger?.log("MISMATCHING_STATE", {
|
|
2162
|
+
structuredData: {
|
|
2163
|
+
oauth_provider: oauth2
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
1044
2166
|
throw new AuthSecurityError(
|
|
1045
2167
|
"MISMATCHING_STATE",
|
|
1046
2168
|
"The provided state passed in the OAuth response does not match the stored state."
|
|
1047
2169
|
);
|
|
1048
2170
|
}
|
|
1049
|
-
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier);
|
|
1050
|
-
const
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
2171
|
+
const accessToken = await createAccessToken(oauthConfig, cookieRedirectURI, code, codeVerifier, logger);
|
|
2172
|
+
const origins = await getTrustedOrigins(request, trustedOrigins);
|
|
2173
|
+
const requestOrigin = await getOriginURL(request, context);
|
|
2174
|
+
if (!isRelativeURL(cookieRedirectTo)) {
|
|
2175
|
+
const isValid = origins.length > 0 ? isTrustedOrigin(cookieRedirectTo, origins) : isSameOrigin(cookieRedirectTo, requestOrigin);
|
|
2176
|
+
if (!isValid) {
|
|
2177
|
+
logger?.log("POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED", {
|
|
2178
|
+
structuredData: {
|
|
2179
|
+
redirect_path: cookieRedirectTo,
|
|
2180
|
+
provider: oauth2,
|
|
2181
|
+
has_trusted_origins: origins.length > 0,
|
|
2182
|
+
request_origin: requestOrigin
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
throw new AuthSecurityError(
|
|
2186
|
+
"POTENTIAL_OPEN_REDIRECT_ATTACK_DETECTED",
|
|
2187
|
+
"Invalid redirect path. Potential open redirect attack detected."
|
|
2188
|
+
);
|
|
2189
|
+
}
|
|
1056
2190
|
}
|
|
1057
|
-
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token);
|
|
2191
|
+
const userInfo = await getUserInfo(oauthConfig, accessToken.access_token, logger);
|
|
1058
2192
|
const sessionCookie = await createSessionCookie(jose, userInfo);
|
|
1059
2193
|
const csrfToken = await createCSRF(jose);
|
|
1060
|
-
|
|
2194
|
+
logger?.log("OAUTH_CALLBACK_SUCCESS", {
|
|
2195
|
+
structuredData: {
|
|
2196
|
+
provider: oauth2
|
|
2197
|
+
}
|
|
2198
|
+
});
|
|
2199
|
+
const headers = new import_router5.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();
|
|
1061
2200
|
return Response.json({ oauth: oauth2 }, { status: 302, headers });
|
|
1062
2201
|
},
|
|
1063
2202
|
callbackConfig(oauth)
|
|
@@ -1065,74 +2204,63 @@ var callbackAction = (oauth) => {
|
|
|
1065
2204
|
};
|
|
1066
2205
|
|
|
1067
2206
|
// src/actions/session/session.ts
|
|
1068
|
-
var
|
|
1069
|
-
var sessionAction = (0,
|
|
2207
|
+
var import_router6 = require("@aura-stack/router");
|
|
2208
|
+
var sessionAction = (0, import_router6.createEndpoint)("GET", "/session", async (ctx) => {
|
|
1070
2209
|
const {
|
|
1071
2210
|
request,
|
|
1072
|
-
context: {
|
|
2211
|
+
context: { cookies }
|
|
1073
2212
|
} = ctx;
|
|
1074
2213
|
try {
|
|
1075
|
-
const session =
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return Response.json(
|
|
2214
|
+
const session = await getSession({ ctx: ctx.context, headers: request.headers });
|
|
2215
|
+
if (!session.authenticated) {
|
|
2216
|
+
throw new AuthInternalError("INVALID_JWT_TOKEN", "Session not authenticated");
|
|
2217
|
+
}
|
|
2218
|
+
return Response.json(session, { headers: secureApiHeaders });
|
|
1080
2219
|
} catch (error) {
|
|
1081
|
-
const headers = new
|
|
1082
|
-
return Response.json({
|
|
2220
|
+
const headers = new import_router6.HeadersBuilder(secureApiHeaders).setCookie(cookies.sessionToken.name, "", expiredCookieAttributes).toHeaders();
|
|
2221
|
+
return Response.json({ session: null, authenticated: false }, { status: 401, headers });
|
|
1083
2222
|
}
|
|
1084
2223
|
});
|
|
1085
2224
|
|
|
1086
2225
|
// src/actions/signOut/signOut.ts
|
|
1087
|
-
var
|
|
1088
|
-
var
|
|
1089
|
-
var config = (0,
|
|
2226
|
+
var import_v44 = require("zod/v4");
|
|
2227
|
+
var import_router7 = require("@aura-stack/router");
|
|
2228
|
+
var config = (0, import_router7.createEndpointConfig)({
|
|
1090
2229
|
schemas: {
|
|
1091
|
-
searchParams:
|
|
1092
|
-
token_type_hint:
|
|
1093
|
-
redirectTo:
|
|
2230
|
+
searchParams: import_v44.z.object({
|
|
2231
|
+
token_type_hint: import_v44.z.literal("session_token"),
|
|
2232
|
+
redirectTo: import_v44.z.string().optional()
|
|
1094
2233
|
})
|
|
1095
2234
|
}
|
|
1096
2235
|
});
|
|
1097
|
-
var signOutAction = (0,
|
|
2236
|
+
var signOutAction = (0, import_router7.createEndpoint)(
|
|
1098
2237
|
"POST",
|
|
1099
2238
|
"/signOut",
|
|
1100
2239
|
async (ctx) => {
|
|
1101
2240
|
const {
|
|
1102
2241
|
request,
|
|
1103
|
-
headers,
|
|
1104
2242
|
searchParams: { redirectTo },
|
|
1105
|
-
context
|
|
2243
|
+
context
|
|
1106
2244
|
} = ctx;
|
|
1107
|
-
const
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
throw new AuthSecurityError("SESSION_TOKEN_MISSING", "The sessionToken is missing.");
|
|
1112
|
-
}
|
|
1113
|
-
if (!csrfToken) {
|
|
1114
|
-
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF token is missing.");
|
|
1115
|
-
}
|
|
1116
|
-
if (!header) {
|
|
1117
|
-
throw new AuthSecurityError("CSRF_TOKEN_MISSING", "The CSRF header is missing.");
|
|
1118
|
-
}
|
|
1119
|
-
await verifyCSRF(jose, csrfToken, header);
|
|
1120
|
-
await jose.decodeJWT(session);
|
|
1121
|
-
const normalizedOriginPath = getNormalizedOriginPath(request.url);
|
|
1122
|
-
const location = createRedirectTo(
|
|
1123
|
-
new Request(normalizedOriginPath, {
|
|
1124
|
-
headers: headers.toHeaders()
|
|
2245
|
+
const baseURL = getBaseURL(request);
|
|
2246
|
+
const location = await createRedirectTo(
|
|
2247
|
+
new Request(baseURL, {
|
|
2248
|
+
headers: request.headers
|
|
1125
2249
|
}),
|
|
1126
|
-
redirectTo
|
|
2250
|
+
redirectTo,
|
|
2251
|
+
context
|
|
1127
2252
|
);
|
|
1128
|
-
|
|
1129
|
-
|
|
2253
|
+
return await signOut({
|
|
2254
|
+
ctx: context,
|
|
2255
|
+
headers: request.headers,
|
|
2256
|
+
redirectTo: location
|
|
2257
|
+
});
|
|
1130
2258
|
},
|
|
1131
2259
|
config
|
|
1132
2260
|
);
|
|
1133
2261
|
|
|
1134
2262
|
// src/actions/csrfToken/csrfToken.ts
|
|
1135
|
-
var
|
|
2263
|
+
var import_router8 = require("@aura-stack/router");
|
|
1136
2264
|
var getCSRFToken = (request, cookieName) => {
|
|
1137
2265
|
try {
|
|
1138
2266
|
return getCookie(request, cookieName);
|
|
@@ -1140,55 +2268,160 @@ var getCSRFToken = (request, cookieName) => {
|
|
|
1140
2268
|
return void 0;
|
|
1141
2269
|
}
|
|
1142
2270
|
};
|
|
1143
|
-
var csrfTokenAction = (0,
|
|
2271
|
+
var csrfTokenAction = (0, import_router8.createEndpoint)("GET", "/csrfToken", async (ctx) => {
|
|
1144
2272
|
const {
|
|
1145
2273
|
request,
|
|
1146
|
-
context: { jose, cookies }
|
|
2274
|
+
context: { jose, cookies, logger }
|
|
1147
2275
|
} = ctx;
|
|
1148
2276
|
const token = getCSRFToken(request, cookies.csrfToken.name);
|
|
2277
|
+
logger?.log("CSRF_TOKEN_REQUESTED", { structuredData: { has_token: Boolean(token) } });
|
|
1149
2278
|
const csrfToken = await createCSRF(jose, token);
|
|
1150
|
-
|
|
2279
|
+
logger?.log("CSRF_TOKEN_ISSUED", { structuredData: { issued: Boolean(csrfToken) } });
|
|
2280
|
+
const headers = new Headers(secureApiHeaders);
|
|
1151
2281
|
headers.append("Set-Cookie", setCookie(cookies.csrfToken.name, csrfToken, cookies.csrfToken.attributes));
|
|
1152
2282
|
return Response.json({ csrfToken }, { headers });
|
|
1153
2283
|
});
|
|
1154
2284
|
|
|
1155
|
-
// src/
|
|
2285
|
+
// src/createAuth.ts
|
|
1156
2286
|
var createInternalConfig = (authConfig) => {
|
|
1157
|
-
const
|
|
2287
|
+
const context = createContext(authConfig);
|
|
1158
2288
|
return {
|
|
1159
2289
|
basePath: authConfig?.basePath ?? "/auth",
|
|
1160
|
-
onError:
|
|
1161
|
-
context
|
|
1162
|
-
|
|
1163
|
-
cookies: createCookieStore(useSecure, authConfig?.cookies?.prefix, authConfig?.cookies?.overrides ?? {}),
|
|
1164
|
-
jose: createJoseInstance(authConfig?.secret),
|
|
1165
|
-
secret: authConfig?.secret,
|
|
1166
|
-
basePath: authConfig?.basePath ?? "/auth",
|
|
1167
|
-
trustedProxyHeaders: useSecure
|
|
1168
|
-
},
|
|
1169
|
-
middlewares: [
|
|
2290
|
+
onError: createErrorHandler(context.logger),
|
|
2291
|
+
context,
|
|
2292
|
+
use: [
|
|
1170
2293
|
(ctx) => {
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
ctx.context.cookies = cookies;
|
|
2294
|
+
const useSecure = useSecureCookies(ctx.request, ctx.context.trustedProxyHeaders);
|
|
2295
|
+
ctx.context.cookies = useSecure ? context.cookieConfig.secure : context.cookieConfig.standard;
|
|
1174
2296
|
return ctx;
|
|
1175
2297
|
}
|
|
1176
2298
|
]
|
|
1177
2299
|
};
|
|
1178
2300
|
};
|
|
1179
|
-
var
|
|
2301
|
+
var createAuthInstance = (authConfig) => {
|
|
1180
2302
|
const config2 = createInternalConfig(authConfig);
|
|
1181
|
-
const router = (0,
|
|
2303
|
+
const router = (0, import_router9.createRouter)(
|
|
1182
2304
|
[signInAction(config2.context.oauth), callbackAction(config2.context.oauth), sessionAction, signOutAction, csrfTokenAction],
|
|
1183
2305
|
config2
|
|
1184
2306
|
);
|
|
1185
2307
|
return {
|
|
1186
2308
|
handlers: router,
|
|
1187
|
-
jose: config2.context.jose
|
|
2309
|
+
jose: config2.context.jose,
|
|
2310
|
+
api: createAuthAPI(config2.context)
|
|
2311
|
+
};
|
|
2312
|
+
};
|
|
2313
|
+
var createAuth = (config2) => {
|
|
2314
|
+
const authInstance = createAuthInstance(config2);
|
|
2315
|
+
authInstance.handlers.ALL = async (request) => {
|
|
2316
|
+
const method = request.method.toUpperCase();
|
|
2317
|
+
const methodHandlers = {
|
|
2318
|
+
GET: authInstance.handlers.GET,
|
|
2319
|
+
POST: authInstance.handlers.POST
|
|
2320
|
+
};
|
|
2321
|
+
if (method in methodHandlers) {
|
|
2322
|
+
return await methodHandlers[method](request);
|
|
2323
|
+
}
|
|
2324
|
+
return new Response("Method Not Allowed", {
|
|
2325
|
+
status: 405,
|
|
2326
|
+
headers: { Allow: Object.keys(methodHandlers).join(", ") }
|
|
2327
|
+
});
|
|
2328
|
+
};
|
|
2329
|
+
return authInstance;
|
|
2330
|
+
};
|
|
2331
|
+
|
|
2332
|
+
// src/client/client.ts
|
|
2333
|
+
var import_router10 = require("@aura-stack/router");
|
|
2334
|
+
var createClient = import_router10.createClient;
|
|
2335
|
+
var createAuthClient = (options2) => {
|
|
2336
|
+
if (typeof window === "undefined" && !options2.baseURL) {
|
|
2337
|
+
throw new AuthClientError("`baseURL` is required when createAuthClient is used outside the browser.");
|
|
2338
|
+
}
|
|
2339
|
+
const client = createClient({
|
|
2340
|
+
cache: "no-store",
|
|
2341
|
+
credentials: "include",
|
|
2342
|
+
baseURL: options2.baseURL ?? window.location.origin,
|
|
2343
|
+
...options2
|
|
2344
|
+
});
|
|
2345
|
+
const getCSRFToken2 = async () => {
|
|
2346
|
+
try {
|
|
2347
|
+
const response = await client.get("/csrfToken");
|
|
2348
|
+
if (!response.ok) return null;
|
|
2349
|
+
const data = await response.json();
|
|
2350
|
+
return data.csrfToken;
|
|
2351
|
+
} catch (error) {
|
|
2352
|
+
console.error("Error fetching CSRF token:", error);
|
|
2353
|
+
return null;
|
|
2354
|
+
}
|
|
2355
|
+
};
|
|
2356
|
+
const getSession2 = async () => {
|
|
2357
|
+
try {
|
|
2358
|
+
const response = await client.get("/session");
|
|
2359
|
+
if (!response.ok) return null;
|
|
2360
|
+
const session = await response.json();
|
|
2361
|
+
if (!session?.authenticated) return null;
|
|
2362
|
+
return session.session;
|
|
2363
|
+
} catch (error) {
|
|
2364
|
+
console.error("Error fetching session:", error);
|
|
2365
|
+
return null;
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
const signIn2 = async (oauth, options3) => {
|
|
2369
|
+
try {
|
|
2370
|
+
const response = await client.get("/signIn/:oauth", {
|
|
2371
|
+
params: {
|
|
2372
|
+
oauth
|
|
2373
|
+
},
|
|
2374
|
+
searchParams: {
|
|
2375
|
+
...options3,
|
|
2376
|
+
redirect: false
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
2379
|
+
const json = await response.json();
|
|
2380
|
+
if ((options3?.redirect ?? true) && typeof window !== "undefined" && json?.signInURL) {
|
|
2381
|
+
window.location.assign(json.signInURL);
|
|
2382
|
+
}
|
|
2383
|
+
return json;
|
|
2384
|
+
} catch (error) {
|
|
2385
|
+
console.error("Error during sign-in:", error);
|
|
2386
|
+
return { redirect: false, signInURL: "/" };
|
|
2387
|
+
}
|
|
2388
|
+
};
|
|
2389
|
+
const signOut2 = async (options3) => {
|
|
2390
|
+
try {
|
|
2391
|
+
const csrfToken = await getCSRFToken2();
|
|
2392
|
+
if (!csrfToken) {
|
|
2393
|
+
throw new AuthClientError("Failed to fetch CSRF token for sign-out.");
|
|
2394
|
+
}
|
|
2395
|
+
const response = await client.post("/signOut", {
|
|
2396
|
+
searchParams: {
|
|
2397
|
+
redirectTo: options3?.redirectTo ?? "/",
|
|
2398
|
+
token_type_hint: "session_token"
|
|
2399
|
+
},
|
|
2400
|
+
headers: {
|
|
2401
|
+
"X-CSRF-Token": csrfToken
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
const json = await response.json();
|
|
2405
|
+
if ((options3?.redirect ?? true) && typeof window !== "undefined" && json?.url) {
|
|
2406
|
+
window.location.assign(json.url);
|
|
2407
|
+
}
|
|
2408
|
+
return json;
|
|
2409
|
+
} catch (error) {
|
|
2410
|
+
console.error("Error during sign-out:", error);
|
|
2411
|
+
throw isNativeError(error) ? error : new AuthClientError("Sign-out failed.", "The sign-out request failed.", { cause: error });
|
|
2412
|
+
}
|
|
2413
|
+
};
|
|
2414
|
+
return {
|
|
2415
|
+
getSession: getSession2,
|
|
2416
|
+
signIn: signIn2,
|
|
2417
|
+
signOut: signOut2
|
|
1188
2418
|
};
|
|
1189
2419
|
};
|
|
1190
2420
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1191
2421
|
0 && (module.exports = {
|
|
2422
|
+
builtInOAuthProviders,
|
|
1192
2423
|
createAuth,
|
|
1193
|
-
|
|
2424
|
+
createAuthClient,
|
|
2425
|
+
createClient,
|
|
2426
|
+
createSyslogMessage
|
|
1194
2427
|
});
|