aaspai-authx 0.1.0 → 0.1.2
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/express/index.cjs +239 -56
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js +239 -56
- package/dist/express/index.js.map +1 -1
- package/dist/index.cjs +265 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -69
- package/dist/index.d.ts +70 -69
- package/dist/index.js +265 -82
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +239 -56
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +239 -56
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/nest/index.cjs
CHANGED
|
@@ -40,24 +40,6 @@ var import_crypto = require("crypto");
|
|
|
40
40
|
var import_express = __toESM(require("express"), 1);
|
|
41
41
|
var import_jsonwebtoken4 = __toESM(require("jsonwebtoken"), 1);
|
|
42
42
|
|
|
43
|
-
// src/core/utils.ts
|
|
44
|
-
function baseProjectCookieOptionsFrom(cookie) {
|
|
45
|
-
const base = {
|
|
46
|
-
secure: cookie.secure ?? false,
|
|
47
|
-
sameSite: cookie.sameSite ?? "lax",
|
|
48
|
-
path: cookie.path ?? "/",
|
|
49
|
-
maxAge: cookie.maxAgeMs
|
|
50
|
-
};
|
|
51
|
-
if (cookie.domain) base.domain = cookie.domain;
|
|
52
|
-
return base;
|
|
53
|
-
}
|
|
54
|
-
function hasAnyRole(session, roles) {
|
|
55
|
-
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
return roles.some((role) => session.roles.includes(role));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
43
|
// src/config/loadConfig.ts
|
|
62
44
|
function loadConfig() {
|
|
63
45
|
return {
|
|
@@ -121,6 +103,37 @@ function isPlainObject(value) {
|
|
|
121
103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
122
104
|
}
|
|
123
105
|
|
|
106
|
+
// src/core/utils.ts
|
|
107
|
+
function baseProjectCookieOptionsFrom(cookie) {
|
|
108
|
+
const base = {
|
|
109
|
+
secure: cookie.secure ?? false,
|
|
110
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
111
|
+
path: cookie.path ?? "/",
|
|
112
|
+
maxAge: cookie.maxAgeMs
|
|
113
|
+
};
|
|
114
|
+
if (cookie.domain) base.domain = cookie.domain;
|
|
115
|
+
return base;
|
|
116
|
+
}
|
|
117
|
+
function buildClearCookieOptions(cookie) {
|
|
118
|
+
const opts = {
|
|
119
|
+
httpOnly: true,
|
|
120
|
+
// not strictly required but fine
|
|
121
|
+
secure: cookie.secure ?? false,
|
|
122
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
123
|
+
path: cookie.path ?? "/"
|
|
124
|
+
};
|
|
125
|
+
if (cookie.domain) {
|
|
126
|
+
opts.domain = cookie.domain;
|
|
127
|
+
}
|
|
128
|
+
return opts;
|
|
129
|
+
}
|
|
130
|
+
function hasAnyRole(session, roles) {
|
|
131
|
+
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return roles.some((role) => session.roles.includes(role));
|
|
135
|
+
}
|
|
136
|
+
|
|
124
137
|
// src/core/roles.config.ts
|
|
125
138
|
var PLATFORM_ROLES = [
|
|
126
139
|
{
|
|
@@ -165,10 +178,14 @@ function buildSession(payload) {
|
|
|
165
178
|
roles: normalizedRoles,
|
|
166
179
|
permissions
|
|
167
180
|
};
|
|
181
|
+
if (payload?.firstName) session.firstName = payload.firstName;
|
|
182
|
+
if (payload?.lastName) session.lastName = payload.lastName;
|
|
168
183
|
if (payload?.projectId) session.projectId = payload.projectId;
|
|
169
184
|
if (payload?.orgId) session.orgId = payload.orgId;
|
|
170
185
|
if (payload?.org_id) session.org_id = payload.org_id;
|
|
171
186
|
if (payload?.authType) session.authType = payload.authType;
|
|
187
|
+
if (payload?.createdAt) session.createdAt = payload.createdAt;
|
|
188
|
+
if (payload?.metadata) session.metadata = payload.metadata;
|
|
172
189
|
Object.keys(payload || {}).forEach((key) => {
|
|
173
190
|
if (![
|
|
174
191
|
"sub",
|
|
@@ -222,7 +239,7 @@ var MetadataSchema = new import_mongoose2.default.Schema(
|
|
|
222
239
|
);
|
|
223
240
|
var OrgUserSchema = new import_mongoose2.default.Schema(
|
|
224
241
|
{
|
|
225
|
-
id: { type: String, default: (0, import_uuid.v4)(), index: true },
|
|
242
|
+
id: { type: String, default: (0, import_uuid.v4)(), index: true, unique: true },
|
|
226
243
|
email: { type: String, required: true, unique: true },
|
|
227
244
|
firstName: { type: String, required: true },
|
|
228
245
|
lastName: { type: String, required: true },
|
|
@@ -342,10 +359,14 @@ function requireAuth() {
|
|
|
342
359
|
const session = buildSession({
|
|
343
360
|
sub: user.id.toString(),
|
|
344
361
|
email: user.email,
|
|
362
|
+
firstName: user.firstName,
|
|
363
|
+
lastName: user.lastName,
|
|
364
|
+
metadata: user.metadata || [],
|
|
345
365
|
roles: user.roles || [],
|
|
346
366
|
orgId: user.orgId,
|
|
347
367
|
org_id: user.orgId,
|
|
348
|
-
projectId: user.projectId
|
|
368
|
+
projectId: user.projectId,
|
|
369
|
+
createdAt: user.createdAt
|
|
349
370
|
});
|
|
350
371
|
session.authType = "api-key";
|
|
351
372
|
session.projectId = readProjectId(req) || user.projectId || void 0;
|
|
@@ -511,14 +532,14 @@ var AuthAdminService = class {
|
|
|
511
532
|
async createUserInRealm(payload) {
|
|
512
533
|
const hashedPassword = payload.credentials?.[0]?.value ? await import_bcrypt.default.hash(payload.credentials[0].value, 10) : void 0;
|
|
513
534
|
const user = await OrgUser.create({
|
|
514
|
-
|
|
535
|
+
id: crypto.randomUUID(),
|
|
515
536
|
email: payload.email,
|
|
516
537
|
firstName: payload.firstName,
|
|
517
538
|
lastName: payload.lastName,
|
|
518
539
|
projectId: payload.projectId,
|
|
519
540
|
emailVerified: payload.emailVerified || false,
|
|
520
541
|
passwordHash: hashedPassword,
|
|
521
|
-
|
|
542
|
+
metadata: payload.metadata || []
|
|
522
543
|
});
|
|
523
544
|
return user;
|
|
524
545
|
}
|
|
@@ -606,29 +627,13 @@ var EmailService = class {
|
|
|
606
627
|
}
|
|
607
628
|
};
|
|
608
629
|
|
|
609
|
-
// src/utils/cookie.ts
|
|
610
|
-
function cookieOpts(isRefresh = false) {
|
|
611
|
-
const maxAge = isRefresh ? config.cookies.refreshTtlMs : config.cookies.accessTtlMs;
|
|
612
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
613
|
-
return {
|
|
614
|
-
httpOnly: true,
|
|
615
|
-
secure,
|
|
616
|
-
sameSite: "none",
|
|
617
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
618
|
-
maxAge
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
function clearOpts() {
|
|
622
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
623
|
-
return {
|
|
624
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
625
|
-
sameSite: "none",
|
|
626
|
-
secure
|
|
627
|
-
};
|
|
628
|
-
}
|
|
629
|
-
|
|
630
630
|
// src/express/auth.routes.ts
|
|
631
631
|
function createAuthRouter(options = {}) {
|
|
632
|
+
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
|
633
|
+
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
|
634
|
+
const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;
|
|
635
|
+
const googleDefaultRedirect = process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
636
|
+
const isGoogleEnabled = !!googleClientId && !!googleClientSecret && !!googleRedirectUri;
|
|
632
637
|
if (options.config) {
|
|
633
638
|
configureAuthX(options.config);
|
|
634
639
|
}
|
|
@@ -653,8 +658,10 @@ function createAuthRouter(options = {}) {
|
|
|
653
658
|
);
|
|
654
659
|
r.post("/login", validateLogin, async (req, res) => {
|
|
655
660
|
const { email: emailAddress, password } = req.body || {};
|
|
661
|
+
console.log(emailAddress, password, "body");
|
|
656
662
|
try {
|
|
657
663
|
const user = await OrgUser.findOne({ email: emailAddress }).select("+password").lean();
|
|
664
|
+
console.log(user, "user");
|
|
658
665
|
if (!user) {
|
|
659
666
|
return res.status(400).json({
|
|
660
667
|
error: "Invalid email or password",
|
|
@@ -707,13 +714,13 @@ function createAuthRouter(options = {}) {
|
|
|
707
714
|
firstName,
|
|
708
715
|
lastName,
|
|
709
716
|
projectId,
|
|
710
|
-
credentials: [{ type: "password", value: password, temporary: false }]
|
|
717
|
+
credentials: [{ type: "password", value: password, temporary: false }],
|
|
718
|
+
metadata
|
|
711
719
|
});
|
|
712
720
|
await authAdmin.assignRealmRole(kcUser.id, "platform_user");
|
|
713
721
|
const user = await OrgUser.findOneAndUpdate(
|
|
714
722
|
{ email: kcUser.email },
|
|
715
723
|
{
|
|
716
|
-
id: kcUser.id,
|
|
717
724
|
email: kcUser.email,
|
|
718
725
|
firstName,
|
|
719
726
|
lastName,
|
|
@@ -753,8 +760,9 @@ function createAuthRouter(options = {}) {
|
|
|
753
760
|
return res.json(req.user || null);
|
|
754
761
|
});
|
|
755
762
|
r.post("/logout", async (_req, res) => {
|
|
756
|
-
|
|
757
|
-
res.clearCookie("
|
|
763
|
+
const clearOptions = buildClearCookieOptions(cookieConfig);
|
|
764
|
+
res.clearCookie("access_token", clearOptions);
|
|
765
|
+
res.clearCookie("refresh_token", clearOptions);
|
|
758
766
|
res.json({ ok: true });
|
|
759
767
|
});
|
|
760
768
|
r.put("/:userId/metadata", requireAuth(), async (req, res) => {
|
|
@@ -993,16 +1001,186 @@ function createAuthRouter(options = {}) {
|
|
|
993
1001
|
const user = await OrgUser.findOne({ email: req.query.email }).lean();
|
|
994
1002
|
res.json(user || null);
|
|
995
1003
|
});
|
|
996
|
-
r.get("/google",
|
|
997
|
-
|
|
1004
|
+
r.get("/google", (req, res) => {
|
|
1005
|
+
if (!isGoogleEnabled) {
|
|
1006
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
1007
|
+
}
|
|
1008
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
1009
|
+
const params = new URLSearchParams({
|
|
1010
|
+
client_id: googleClientId,
|
|
1011
|
+
redirect_uri: googleRedirectUri,
|
|
1012
|
+
response_type: "code",
|
|
1013
|
+
scope: "openid email profile",
|
|
1014
|
+
access_type: "offline",
|
|
1015
|
+
prompt: "consent",
|
|
1016
|
+
state
|
|
1017
|
+
});
|
|
1018
|
+
const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
1019
|
+
res.redirect(url);
|
|
998
1020
|
});
|
|
999
|
-
r.get("/google/callback", async (
|
|
1000
|
-
|
|
1001
|
-
"
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
);
|
|
1005
|
-
|
|
1021
|
+
r.get("/google/callback", async (req, res) => {
|
|
1022
|
+
if (!isGoogleEnabled) {
|
|
1023
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
1024
|
+
}
|
|
1025
|
+
const code = String(req.query.code || "");
|
|
1026
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
1027
|
+
if (!code) {
|
|
1028
|
+
return res.status(400).json({ ok: false, error: "Missing authorization code" });
|
|
1029
|
+
}
|
|
1030
|
+
try {
|
|
1031
|
+
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1034
|
+
body: new URLSearchParams({
|
|
1035
|
+
code,
|
|
1036
|
+
client_id: googleClientId,
|
|
1037
|
+
client_secret: googleClientSecret,
|
|
1038
|
+
redirect_uri: googleRedirectUri,
|
|
1039
|
+
grant_type: "authorization_code"
|
|
1040
|
+
})
|
|
1041
|
+
});
|
|
1042
|
+
if (!tokenRes.ok) {
|
|
1043
|
+
const errJson = await tokenRes.json().catch(() => ({}));
|
|
1044
|
+
console.error("Google token error", errJson);
|
|
1045
|
+
return res.status(400).json({ ok: false, error: "Failed to exchange Google code" });
|
|
1046
|
+
}
|
|
1047
|
+
const tokenJson = await tokenRes.json();
|
|
1048
|
+
if (!tokenJson.id_token) {
|
|
1049
|
+
return res.status(400).json({ ok: false, error: "Missing id_token from Google" });
|
|
1050
|
+
}
|
|
1051
|
+
const decoded = import_jsonwebtoken4.default.decode(tokenJson.id_token);
|
|
1052
|
+
const email2 = decoded?.email;
|
|
1053
|
+
if (!email2) {
|
|
1054
|
+
return res.status(400).json({ ok: false, error: "Google account has no email" });
|
|
1055
|
+
}
|
|
1056
|
+
const emailVerified = decoded.email_verified ?? true;
|
|
1057
|
+
const firstName = decoded.given_name || "";
|
|
1058
|
+
const lastName = decoded.family_name || "";
|
|
1059
|
+
let user = await OrgUser.findOne({ email: email2 }).lean();
|
|
1060
|
+
if (!user) {
|
|
1061
|
+
const created = await OrgUser.create({
|
|
1062
|
+
email: email2,
|
|
1063
|
+
firstName,
|
|
1064
|
+
lastName,
|
|
1065
|
+
emailVerified,
|
|
1066
|
+
roles: ["platform_user"],
|
|
1067
|
+
projectId: null,
|
|
1068
|
+
metadata: []
|
|
1069
|
+
// you can also store googleId: decoded.sub
|
|
1070
|
+
});
|
|
1071
|
+
user = created.toObject();
|
|
1072
|
+
}
|
|
1073
|
+
const tokens = generateTokens(user);
|
|
1074
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1075
|
+
const redirectTo = state ? decodeURIComponent(state) : googleDefaultRedirect;
|
|
1076
|
+
res.redirect(redirectTo);
|
|
1077
|
+
} catch (err) {
|
|
1078
|
+
console.error("Google callback error", err);
|
|
1079
|
+
const redirectError = googleDefaultRedirect.includes("?") ? `${googleDefaultRedirect}&error=google_login_failed` : `${googleDefaultRedirect}?error=google_login_failed`;
|
|
1080
|
+
res.redirect(redirectError);
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
r.get("/github", (req, res) => {
|
|
1084
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1085
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1086
|
+
if (!githubClientId || !githubRedirectUri) {
|
|
1087
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1088
|
+
}
|
|
1089
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
1090
|
+
const params = new URLSearchParams({
|
|
1091
|
+
client_id: githubClientId,
|
|
1092
|
+
redirect_uri: githubRedirectUri,
|
|
1093
|
+
scope: "user:email",
|
|
1094
|
+
state,
|
|
1095
|
+
allow_signup: "true"
|
|
1096
|
+
});
|
|
1097
|
+
const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
1098
|
+
res.redirect(url);
|
|
1099
|
+
});
|
|
1100
|
+
r.get("/github/callback", async (req, res) => {
|
|
1101
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1102
|
+
const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
1103
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1104
|
+
const githubDefaultRedirect = process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
1105
|
+
if (!githubClientId || !githubClientSecret || !githubRedirectUri) {
|
|
1106
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1107
|
+
}
|
|
1108
|
+
const code = String(req.query.code || "");
|
|
1109
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
1110
|
+
if (!code) {
|
|
1111
|
+
return res.status(400).json({ ok: false, error: "Missing GitHub code" });
|
|
1112
|
+
}
|
|
1113
|
+
try {
|
|
1114
|
+
const tokenRes = await fetch(
|
|
1115
|
+
"https://github.com/login/oauth/access_token",
|
|
1116
|
+
{
|
|
1117
|
+
method: "POST",
|
|
1118
|
+
headers: {
|
|
1119
|
+
Accept: "application/json",
|
|
1120
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1121
|
+
},
|
|
1122
|
+
body: new URLSearchParams({
|
|
1123
|
+
client_id: githubClientId,
|
|
1124
|
+
client_secret: githubClientSecret,
|
|
1125
|
+
code,
|
|
1126
|
+
redirect_uri: githubRedirectUri
|
|
1127
|
+
})
|
|
1128
|
+
}
|
|
1129
|
+
);
|
|
1130
|
+
const tokenJson = await tokenRes.json();
|
|
1131
|
+
if (!tokenJson.access_token) {
|
|
1132
|
+
console.error("GitHub token error:", tokenJson);
|
|
1133
|
+
return res.status(400).json({ ok: false, error: "Failed to get GitHub access token" });
|
|
1134
|
+
}
|
|
1135
|
+
const accessToken = tokenJson.access_token;
|
|
1136
|
+
const userRes = await fetch("https://api.github.com/user", {
|
|
1137
|
+
headers: {
|
|
1138
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1139
|
+
Accept: "application/vnd.github+json"
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
const githubUser = await userRes.json();
|
|
1143
|
+
const emailRes = await fetch("https://api.github.com/user/emails", {
|
|
1144
|
+
headers: {
|
|
1145
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1146
|
+
Accept: "application/vnd.github+json"
|
|
1147
|
+
}
|
|
1148
|
+
});
|
|
1149
|
+
const emails = await emailRes.json();
|
|
1150
|
+
const primaryEmail = emails?.find(
|
|
1151
|
+
(e) => e.primary && e.verified
|
|
1152
|
+
)?.email;
|
|
1153
|
+
if (!primaryEmail) {
|
|
1154
|
+
return res.status(400).json({
|
|
1155
|
+
ok: false,
|
|
1156
|
+
error: "GitHub account has no verified email"
|
|
1157
|
+
});
|
|
1158
|
+
}
|
|
1159
|
+
const firstName = githubUser.name?.split(" ")[0] || "";
|
|
1160
|
+
const lastName = githubUser.name?.split(" ").slice(1).join(" ") || "";
|
|
1161
|
+
let user = await OrgUser.findOne({ email: primaryEmail }).lean();
|
|
1162
|
+
if (!user) {
|
|
1163
|
+
const created = await OrgUser.create({
|
|
1164
|
+
email: primaryEmail,
|
|
1165
|
+
firstName,
|
|
1166
|
+
lastName,
|
|
1167
|
+
emailVerified: true,
|
|
1168
|
+
roles: ["platform_user"],
|
|
1169
|
+
projectId: null,
|
|
1170
|
+
metadata: [],
|
|
1171
|
+
githubId: githubUser.id
|
|
1172
|
+
});
|
|
1173
|
+
user = created.toObject();
|
|
1174
|
+
}
|
|
1175
|
+
const tokens = generateTokens(user);
|
|
1176
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1177
|
+
const redirectTo = state ? decodeURIComponent(state) : githubDefaultRedirect;
|
|
1178
|
+
res.redirect(redirectTo);
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
console.error("GitHub callback error:", err);
|
|
1181
|
+
const redirectError = githubDefaultRedirect.includes("?") ? `${githubDefaultRedirect}&error=github_login_failed` : `${githubDefaultRedirect}?error=github_login_failed`;
|
|
1182
|
+
res.redirect(redirectError);
|
|
1183
|
+
}
|
|
1006
1184
|
});
|
|
1007
1185
|
r.get("/get-users", async (req, res) => {
|
|
1008
1186
|
const user = await OrgUser.find({ projectId: req.query.projectId }).lean();
|
|
@@ -1080,6 +1258,11 @@ function generateTokens(user) {
|
|
|
1080
1258
|
orgId: user.orgId || null,
|
|
1081
1259
|
org_id: user.orgId || null,
|
|
1082
1260
|
projectId: user.projectId || null,
|
|
1261
|
+
firstName: user.firstName,
|
|
1262
|
+
lastName: user.lastName,
|
|
1263
|
+
emailVerified: user.emailVerified,
|
|
1264
|
+
createdAt: user.createdAt,
|
|
1265
|
+
metadata: user.metadata,
|
|
1083
1266
|
type: "user"
|
|
1084
1267
|
};
|
|
1085
1268
|
const accessToken = import_jsonwebtoken4.default.sign(accessPayload, process.env.JWT_SECRET, {
|