aaspai-authx 0.0.9 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/express/index.cjs +226 -55
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.js +226 -55
- package/dist/express/index.js.map +1 -1
- package/dist/index.cjs +252 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +252 -81
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +226 -55
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.js +226 -55
- package/dist/nest/index.js.map +1 -1
- package/package.json +1 -1
package/dist/express/index.cjs
CHANGED
|
@@ -44,24 +44,6 @@ var import_crypto = require("crypto");
|
|
|
44
44
|
var import_express = __toESM(require("express"), 1);
|
|
45
45
|
var import_jsonwebtoken4 = __toESM(require("jsonwebtoken"), 1);
|
|
46
46
|
|
|
47
|
-
// src/core/utils.ts
|
|
48
|
-
function baseProjectCookieOptionsFrom(cookie) {
|
|
49
|
-
const base = {
|
|
50
|
-
secure: cookie.secure ?? false,
|
|
51
|
-
sameSite: cookie.sameSite ?? "lax",
|
|
52
|
-
path: cookie.path ?? "/",
|
|
53
|
-
maxAge: cookie.maxAgeMs
|
|
54
|
-
};
|
|
55
|
-
if (cookie.domain) base.domain = cookie.domain;
|
|
56
|
-
return base;
|
|
57
|
-
}
|
|
58
|
-
function hasAnyRole(session, roles) {
|
|
59
|
-
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
return roles.some((role) => session.roles.includes(role));
|
|
63
|
-
}
|
|
64
|
-
|
|
65
47
|
// src/config/loadConfig.ts
|
|
66
48
|
function loadConfig() {
|
|
67
49
|
return {
|
|
@@ -125,6 +107,37 @@ function isPlainObject(value) {
|
|
|
125
107
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
126
108
|
}
|
|
127
109
|
|
|
110
|
+
// src/core/utils.ts
|
|
111
|
+
function baseProjectCookieOptionsFrom(cookie) {
|
|
112
|
+
const base = {
|
|
113
|
+
secure: cookie.secure ?? false,
|
|
114
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
115
|
+
path: cookie.path ?? "/",
|
|
116
|
+
maxAge: cookie.maxAgeMs
|
|
117
|
+
};
|
|
118
|
+
if (cookie.domain) base.domain = cookie.domain;
|
|
119
|
+
return base;
|
|
120
|
+
}
|
|
121
|
+
function buildClearCookieOptions(cookie) {
|
|
122
|
+
const opts = {
|
|
123
|
+
httpOnly: true,
|
|
124
|
+
// not strictly required but fine
|
|
125
|
+
secure: cookie.secure ?? false,
|
|
126
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
127
|
+
path: cookie.path ?? "/"
|
|
128
|
+
};
|
|
129
|
+
if (cookie.domain) {
|
|
130
|
+
opts.domain = cookie.domain;
|
|
131
|
+
}
|
|
132
|
+
return opts;
|
|
133
|
+
}
|
|
134
|
+
function hasAnyRole(session, roles) {
|
|
135
|
+
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return roles.some((role) => session.roles.includes(role));
|
|
139
|
+
}
|
|
140
|
+
|
|
128
141
|
// src/core/roles.config.ts
|
|
129
142
|
var PLATFORM_ROLES = [
|
|
130
143
|
{
|
|
@@ -226,7 +239,7 @@ var MetadataSchema = new import_mongoose2.default.Schema(
|
|
|
226
239
|
);
|
|
227
240
|
var OrgUserSchema = new import_mongoose2.default.Schema(
|
|
228
241
|
{
|
|
229
|
-
id: { type: String, default: (0, import_uuid.v4)(), index: true },
|
|
242
|
+
id: { type: String, default: (0, import_uuid.v4)(), index: true, unique: true },
|
|
230
243
|
email: { type: String, required: true, unique: true },
|
|
231
244
|
firstName: { type: String, required: true },
|
|
232
245
|
lastName: { type: String, required: true },
|
|
@@ -446,6 +459,7 @@ var Invite = import_mongoose3.default.model("Invite", InviteSchema);
|
|
|
446
459
|
// src/services/auth-admin.service.ts
|
|
447
460
|
var import_bcrypt = __toESM(require("bcrypt"), 1);
|
|
448
461
|
var import_jsonwebtoken2 = __toESM(require("jsonwebtoken"), 1);
|
|
462
|
+
var import_uuid2 = require("uuid");
|
|
449
463
|
|
|
450
464
|
// src/models/client.model.ts
|
|
451
465
|
var import_mongoose4 = __toESM(require("mongoose"), 1);
|
|
@@ -515,14 +529,14 @@ var AuthAdminService = class {
|
|
|
515
529
|
async createUserInRealm(payload) {
|
|
516
530
|
const hashedPassword = payload.credentials?.[0]?.value ? await import_bcrypt.default.hash(payload.credentials[0].value, 10) : void 0;
|
|
517
531
|
const user = await OrgUser.create({
|
|
518
|
-
|
|
532
|
+
id: (0, import_uuid2.v4)(),
|
|
519
533
|
email: payload.email,
|
|
520
534
|
firstName: payload.firstName,
|
|
521
535
|
lastName: payload.lastName,
|
|
522
536
|
projectId: payload.projectId,
|
|
523
537
|
emailVerified: payload.emailVerified || false,
|
|
524
538
|
passwordHash: hashedPassword,
|
|
525
|
-
|
|
539
|
+
metadata: payload.metadata || []
|
|
526
540
|
});
|
|
527
541
|
return user;
|
|
528
542
|
}
|
|
@@ -610,29 +624,13 @@ var EmailService = class {
|
|
|
610
624
|
}
|
|
611
625
|
};
|
|
612
626
|
|
|
613
|
-
// src/utils/cookie.ts
|
|
614
|
-
function cookieOpts(isRefresh = false) {
|
|
615
|
-
const maxAge = isRefresh ? config.cookies.refreshTtlMs : config.cookies.accessTtlMs;
|
|
616
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
617
|
-
return {
|
|
618
|
-
httpOnly: true,
|
|
619
|
-
secure,
|
|
620
|
-
sameSite: "none",
|
|
621
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
622
|
-
maxAge
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
function clearOpts() {
|
|
626
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
627
|
-
return {
|
|
628
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
629
|
-
sameSite: "none",
|
|
630
|
-
secure
|
|
631
|
-
};
|
|
632
|
-
}
|
|
633
|
-
|
|
634
627
|
// src/express/auth.routes.ts
|
|
635
628
|
function createAuthRouter(options = {}) {
|
|
629
|
+
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
|
630
|
+
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
|
631
|
+
const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;
|
|
632
|
+
const googleDefaultRedirect = process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
633
|
+
const isGoogleEnabled = !!googleClientId && !!googleClientSecret && !!googleRedirectUri;
|
|
636
634
|
if (options.config) {
|
|
637
635
|
configureAuthX(options.config);
|
|
638
636
|
}
|
|
@@ -657,8 +655,10 @@ function createAuthRouter(options = {}) {
|
|
|
657
655
|
);
|
|
658
656
|
r.post("/login", validateLogin, async (req, res) => {
|
|
659
657
|
const { email: emailAddress, password } = req.body || {};
|
|
658
|
+
console.log(emailAddress, password, "body");
|
|
660
659
|
try {
|
|
661
660
|
const user = await OrgUser.findOne({ email: emailAddress }).select("+password").lean();
|
|
661
|
+
console.log(user, "user");
|
|
662
662
|
if (!user) {
|
|
663
663
|
return res.status(400).json({
|
|
664
664
|
error: "Invalid email or password",
|
|
@@ -711,13 +711,13 @@ function createAuthRouter(options = {}) {
|
|
|
711
711
|
firstName,
|
|
712
712
|
lastName,
|
|
713
713
|
projectId,
|
|
714
|
-
credentials: [{ type: "password", value: password, temporary: false }]
|
|
714
|
+
credentials: [{ type: "password", value: password, temporary: false }],
|
|
715
|
+
metadata
|
|
715
716
|
});
|
|
716
717
|
await authAdmin.assignRealmRole(kcUser.id, "platform_user");
|
|
717
718
|
const user = await OrgUser.findOneAndUpdate(
|
|
718
719
|
{ email: kcUser.email },
|
|
719
720
|
{
|
|
720
|
-
id: kcUser.id,
|
|
721
721
|
email: kcUser.email,
|
|
722
722
|
firstName,
|
|
723
723
|
lastName,
|
|
@@ -757,8 +757,9 @@ function createAuthRouter(options = {}) {
|
|
|
757
757
|
return res.json(req.user || null);
|
|
758
758
|
});
|
|
759
759
|
r.post("/logout", async (_req, res) => {
|
|
760
|
-
|
|
761
|
-
res.clearCookie("
|
|
760
|
+
const clearOptions = buildClearCookieOptions(cookieConfig);
|
|
761
|
+
res.clearCookie("access_token", clearOptions);
|
|
762
|
+
res.clearCookie("refresh_token", clearOptions);
|
|
762
763
|
res.json({ ok: true });
|
|
763
764
|
});
|
|
764
765
|
r.put("/:userId/metadata", requireAuth(), async (req, res) => {
|
|
@@ -997,16 +998,186 @@ function createAuthRouter(options = {}) {
|
|
|
997
998
|
const user = await OrgUser.findOne({ email: req.query.email }).lean();
|
|
998
999
|
res.json(user || null);
|
|
999
1000
|
});
|
|
1000
|
-
r.get("/google",
|
|
1001
|
-
|
|
1001
|
+
r.get("/google", (req, res) => {
|
|
1002
|
+
if (!isGoogleEnabled) {
|
|
1003
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
1004
|
+
}
|
|
1005
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
1006
|
+
const params = new URLSearchParams({
|
|
1007
|
+
client_id: googleClientId,
|
|
1008
|
+
redirect_uri: googleRedirectUri,
|
|
1009
|
+
response_type: "code",
|
|
1010
|
+
scope: "openid email profile",
|
|
1011
|
+
access_type: "offline",
|
|
1012
|
+
prompt: "consent",
|
|
1013
|
+
state
|
|
1014
|
+
});
|
|
1015
|
+
const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
1016
|
+
res.redirect(url);
|
|
1002
1017
|
});
|
|
1003
|
-
r.get("/google/callback", async (
|
|
1004
|
-
|
|
1005
|
-
"
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
);
|
|
1009
|
-
|
|
1018
|
+
r.get("/google/callback", async (req, res) => {
|
|
1019
|
+
if (!isGoogleEnabled) {
|
|
1020
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
1021
|
+
}
|
|
1022
|
+
const code = String(req.query.code || "");
|
|
1023
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
1024
|
+
if (!code) {
|
|
1025
|
+
return res.status(400).json({ ok: false, error: "Missing authorization code" });
|
|
1026
|
+
}
|
|
1027
|
+
try {
|
|
1028
|
+
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
|
1029
|
+
method: "POST",
|
|
1030
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1031
|
+
body: new URLSearchParams({
|
|
1032
|
+
code,
|
|
1033
|
+
client_id: googleClientId,
|
|
1034
|
+
client_secret: googleClientSecret,
|
|
1035
|
+
redirect_uri: googleRedirectUri,
|
|
1036
|
+
grant_type: "authorization_code"
|
|
1037
|
+
})
|
|
1038
|
+
});
|
|
1039
|
+
if (!tokenRes.ok) {
|
|
1040
|
+
const errJson = await tokenRes.json().catch(() => ({}));
|
|
1041
|
+
console.error("Google token error", errJson);
|
|
1042
|
+
return res.status(400).json({ ok: false, error: "Failed to exchange Google code" });
|
|
1043
|
+
}
|
|
1044
|
+
const tokenJson = await tokenRes.json();
|
|
1045
|
+
if (!tokenJson.id_token) {
|
|
1046
|
+
return res.status(400).json({ ok: false, error: "Missing id_token from Google" });
|
|
1047
|
+
}
|
|
1048
|
+
const decoded = import_jsonwebtoken4.default.decode(tokenJson.id_token);
|
|
1049
|
+
const email2 = decoded?.email;
|
|
1050
|
+
if (!email2) {
|
|
1051
|
+
return res.status(400).json({ ok: false, error: "Google account has no email" });
|
|
1052
|
+
}
|
|
1053
|
+
const emailVerified = decoded.email_verified ?? true;
|
|
1054
|
+
const firstName = decoded.given_name || "";
|
|
1055
|
+
const lastName = decoded.family_name || "";
|
|
1056
|
+
let user = await OrgUser.findOne({ email: email2 }).lean();
|
|
1057
|
+
if (!user) {
|
|
1058
|
+
const created = await OrgUser.create({
|
|
1059
|
+
email: email2,
|
|
1060
|
+
firstName,
|
|
1061
|
+
lastName,
|
|
1062
|
+
emailVerified,
|
|
1063
|
+
roles: ["platform_user"],
|
|
1064
|
+
projectId: null,
|
|
1065
|
+
metadata: []
|
|
1066
|
+
// you can also store googleId: decoded.sub
|
|
1067
|
+
});
|
|
1068
|
+
user = created.toObject();
|
|
1069
|
+
}
|
|
1070
|
+
const tokens = generateTokens(user);
|
|
1071
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1072
|
+
const redirectTo = state ? decodeURIComponent(state) : googleDefaultRedirect;
|
|
1073
|
+
res.redirect(redirectTo);
|
|
1074
|
+
} catch (err) {
|
|
1075
|
+
console.error("Google callback error", err);
|
|
1076
|
+
const redirectError = googleDefaultRedirect.includes("?") ? `${googleDefaultRedirect}&error=google_login_failed` : `${googleDefaultRedirect}?error=google_login_failed`;
|
|
1077
|
+
res.redirect(redirectError);
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
r.get("/github", (req, res) => {
|
|
1081
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1082
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1083
|
+
if (!githubClientId || !githubRedirectUri) {
|
|
1084
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1085
|
+
}
|
|
1086
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
1087
|
+
const params = new URLSearchParams({
|
|
1088
|
+
client_id: githubClientId,
|
|
1089
|
+
redirect_uri: githubRedirectUri,
|
|
1090
|
+
scope: "user:email",
|
|
1091
|
+
state,
|
|
1092
|
+
allow_signup: "true"
|
|
1093
|
+
});
|
|
1094
|
+
const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
1095
|
+
res.redirect(url);
|
|
1096
|
+
});
|
|
1097
|
+
r.get("/github/callback", async (req, res) => {
|
|
1098
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1099
|
+
const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
1100
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1101
|
+
const githubDefaultRedirect = process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
1102
|
+
if (!githubClientId || !githubClientSecret || !githubRedirectUri) {
|
|
1103
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1104
|
+
}
|
|
1105
|
+
const code = String(req.query.code || "");
|
|
1106
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
1107
|
+
if (!code) {
|
|
1108
|
+
return res.status(400).json({ ok: false, error: "Missing GitHub code" });
|
|
1109
|
+
}
|
|
1110
|
+
try {
|
|
1111
|
+
const tokenRes = await fetch(
|
|
1112
|
+
"https://github.com/login/oauth/access_token",
|
|
1113
|
+
{
|
|
1114
|
+
method: "POST",
|
|
1115
|
+
headers: {
|
|
1116
|
+
Accept: "application/json",
|
|
1117
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1118
|
+
},
|
|
1119
|
+
body: new URLSearchParams({
|
|
1120
|
+
client_id: githubClientId,
|
|
1121
|
+
client_secret: githubClientSecret,
|
|
1122
|
+
code,
|
|
1123
|
+
redirect_uri: githubRedirectUri
|
|
1124
|
+
})
|
|
1125
|
+
}
|
|
1126
|
+
);
|
|
1127
|
+
const tokenJson = await tokenRes.json();
|
|
1128
|
+
if (!tokenJson.access_token) {
|
|
1129
|
+
console.error("GitHub token error:", tokenJson);
|
|
1130
|
+
return res.status(400).json({ ok: false, error: "Failed to get GitHub access token" });
|
|
1131
|
+
}
|
|
1132
|
+
const accessToken = tokenJson.access_token;
|
|
1133
|
+
const userRes = await fetch("https://api.github.com/user", {
|
|
1134
|
+
headers: {
|
|
1135
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1136
|
+
Accept: "application/vnd.github+json"
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
const githubUser = await userRes.json();
|
|
1140
|
+
const emailRes = await fetch("https://api.github.com/user/emails", {
|
|
1141
|
+
headers: {
|
|
1142
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1143
|
+
Accept: "application/vnd.github+json"
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
const emails = await emailRes.json();
|
|
1147
|
+
const primaryEmail = emails?.find(
|
|
1148
|
+
(e) => e.primary && e.verified
|
|
1149
|
+
)?.email;
|
|
1150
|
+
if (!primaryEmail) {
|
|
1151
|
+
return res.status(400).json({
|
|
1152
|
+
ok: false,
|
|
1153
|
+
error: "GitHub account has no verified email"
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
const firstName = githubUser.name?.split(" ")[0] || "";
|
|
1157
|
+
const lastName = githubUser.name?.split(" ").slice(1).join(" ") || "";
|
|
1158
|
+
let user = await OrgUser.findOne({ email: primaryEmail }).lean();
|
|
1159
|
+
if (!user) {
|
|
1160
|
+
const created = await OrgUser.create({
|
|
1161
|
+
email: primaryEmail,
|
|
1162
|
+
firstName,
|
|
1163
|
+
lastName,
|
|
1164
|
+
emailVerified: true,
|
|
1165
|
+
roles: ["platform_user"],
|
|
1166
|
+
projectId: null,
|
|
1167
|
+
metadata: [],
|
|
1168
|
+
githubId: githubUser.id
|
|
1169
|
+
});
|
|
1170
|
+
user = created.toObject();
|
|
1171
|
+
}
|
|
1172
|
+
const tokens = generateTokens(user);
|
|
1173
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1174
|
+
const redirectTo = state ? decodeURIComponent(state) : githubDefaultRedirect;
|
|
1175
|
+
res.redirect(redirectTo);
|
|
1176
|
+
} catch (err) {
|
|
1177
|
+
console.error("GitHub callback error:", err);
|
|
1178
|
+
const redirectError = githubDefaultRedirect.includes("?") ? `${githubDefaultRedirect}&error=github_login_failed` : `${githubDefaultRedirect}?error=github_login_failed`;
|
|
1179
|
+
res.redirect(redirectError);
|
|
1180
|
+
}
|
|
1010
1181
|
});
|
|
1011
1182
|
r.get("/get-users", async (req, res) => {
|
|
1012
1183
|
const user = await OrgUser.find({ projectId: req.query.projectId }).lean();
|