aaspai-authx 0.1.0 → 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.js
CHANGED
|
@@ -6,24 +6,6 @@ import express, {
|
|
|
6
6
|
} from "express";
|
|
7
7
|
import jwt4 from "jsonwebtoken";
|
|
8
8
|
|
|
9
|
-
// src/core/utils.ts
|
|
10
|
-
function baseProjectCookieOptionsFrom(cookie) {
|
|
11
|
-
const base = {
|
|
12
|
-
secure: cookie.secure ?? false,
|
|
13
|
-
sameSite: cookie.sameSite ?? "lax",
|
|
14
|
-
path: cookie.path ?? "/",
|
|
15
|
-
maxAge: cookie.maxAgeMs
|
|
16
|
-
};
|
|
17
|
-
if (cookie.domain) base.domain = cookie.domain;
|
|
18
|
-
return base;
|
|
19
|
-
}
|
|
20
|
-
function hasAnyRole(session, roles) {
|
|
21
|
-
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
return roles.some((role) => session.roles.includes(role));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
9
|
// src/config/loadConfig.ts
|
|
28
10
|
function loadConfig() {
|
|
29
11
|
return {
|
|
@@ -87,6 +69,37 @@ function isPlainObject(value) {
|
|
|
87
69
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
88
70
|
}
|
|
89
71
|
|
|
72
|
+
// src/core/utils.ts
|
|
73
|
+
function baseProjectCookieOptionsFrom(cookie) {
|
|
74
|
+
const base = {
|
|
75
|
+
secure: cookie.secure ?? false,
|
|
76
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
77
|
+
path: cookie.path ?? "/",
|
|
78
|
+
maxAge: cookie.maxAgeMs
|
|
79
|
+
};
|
|
80
|
+
if (cookie.domain) base.domain = cookie.domain;
|
|
81
|
+
return base;
|
|
82
|
+
}
|
|
83
|
+
function buildClearCookieOptions(cookie) {
|
|
84
|
+
const opts = {
|
|
85
|
+
httpOnly: true,
|
|
86
|
+
// not strictly required but fine
|
|
87
|
+
secure: cookie.secure ?? false,
|
|
88
|
+
sameSite: cookie.sameSite ?? "lax",
|
|
89
|
+
path: cookie.path ?? "/"
|
|
90
|
+
};
|
|
91
|
+
if (cookie.domain) {
|
|
92
|
+
opts.domain = cookie.domain;
|
|
93
|
+
}
|
|
94
|
+
return opts;
|
|
95
|
+
}
|
|
96
|
+
function hasAnyRole(session, roles) {
|
|
97
|
+
if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return roles.some((role) => session.roles.includes(role));
|
|
101
|
+
}
|
|
102
|
+
|
|
90
103
|
// src/core/roles.config.ts
|
|
91
104
|
var PLATFORM_ROLES = [
|
|
92
105
|
{
|
|
@@ -188,7 +201,7 @@ var MetadataSchema = new mongoose2.Schema(
|
|
|
188
201
|
);
|
|
189
202
|
var OrgUserSchema = new mongoose2.Schema(
|
|
190
203
|
{
|
|
191
|
-
id: { type: String, default: uuid(), index: true },
|
|
204
|
+
id: { type: String, default: uuid(), index: true, unique: true },
|
|
192
205
|
email: { type: String, required: true, unique: true },
|
|
193
206
|
firstName: { type: String, required: true },
|
|
194
207
|
lastName: { type: String, required: true },
|
|
@@ -408,6 +421,7 @@ var Invite = mongoose3.model("Invite", InviteSchema);
|
|
|
408
421
|
// src/services/auth-admin.service.ts
|
|
409
422
|
import bcrypt from "bcrypt";
|
|
410
423
|
import jwt2 from "jsonwebtoken";
|
|
424
|
+
import { v4 as uuid2 } from "uuid";
|
|
411
425
|
|
|
412
426
|
// src/models/client.model.ts
|
|
413
427
|
import mongoose4, { Schema as Schema2 } from "mongoose";
|
|
@@ -477,14 +491,14 @@ var AuthAdminService = class {
|
|
|
477
491
|
async createUserInRealm(payload) {
|
|
478
492
|
const hashedPassword = payload.credentials?.[0]?.value ? await bcrypt.hash(payload.credentials[0].value, 10) : void 0;
|
|
479
493
|
const user = await OrgUser.create({
|
|
480
|
-
|
|
494
|
+
id: uuid2(),
|
|
481
495
|
email: payload.email,
|
|
482
496
|
firstName: payload.firstName,
|
|
483
497
|
lastName: payload.lastName,
|
|
484
498
|
projectId: payload.projectId,
|
|
485
499
|
emailVerified: payload.emailVerified || false,
|
|
486
500
|
passwordHash: hashedPassword,
|
|
487
|
-
|
|
501
|
+
metadata: payload.metadata || []
|
|
488
502
|
});
|
|
489
503
|
return user;
|
|
490
504
|
}
|
|
@@ -572,29 +586,13 @@ var EmailService = class {
|
|
|
572
586
|
}
|
|
573
587
|
};
|
|
574
588
|
|
|
575
|
-
// src/utils/cookie.ts
|
|
576
|
-
function cookieOpts(isRefresh = false) {
|
|
577
|
-
const maxAge = isRefresh ? config.cookies.refreshTtlMs : config.cookies.accessTtlMs;
|
|
578
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
579
|
-
return {
|
|
580
|
-
httpOnly: true,
|
|
581
|
-
secure,
|
|
582
|
-
sameSite: "none",
|
|
583
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
584
|
-
maxAge
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
function clearOpts() {
|
|
588
|
-
const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
|
|
589
|
-
return {
|
|
590
|
-
domain: process.env.COOKIE_DOMAIN,
|
|
591
|
-
sameSite: "none",
|
|
592
|
-
secure
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
|
|
596
589
|
// src/express/auth.routes.ts
|
|
597
590
|
function createAuthRouter(options = {}) {
|
|
591
|
+
const googleClientId = process.env.GOOGLE_CLIENT_ID;
|
|
592
|
+
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
|
|
593
|
+
const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;
|
|
594
|
+
const googleDefaultRedirect = process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
595
|
+
const isGoogleEnabled = !!googleClientId && !!googleClientSecret && !!googleRedirectUri;
|
|
598
596
|
if (options.config) {
|
|
599
597
|
configureAuthX(options.config);
|
|
600
598
|
}
|
|
@@ -619,8 +617,10 @@ function createAuthRouter(options = {}) {
|
|
|
619
617
|
);
|
|
620
618
|
r.post("/login", validateLogin, async (req, res) => {
|
|
621
619
|
const { email: emailAddress, password } = req.body || {};
|
|
620
|
+
console.log(emailAddress, password, "body");
|
|
622
621
|
try {
|
|
623
622
|
const user = await OrgUser.findOne({ email: emailAddress }).select("+password").lean();
|
|
623
|
+
console.log(user, "user");
|
|
624
624
|
if (!user) {
|
|
625
625
|
return res.status(400).json({
|
|
626
626
|
error: "Invalid email or password",
|
|
@@ -673,13 +673,13 @@ function createAuthRouter(options = {}) {
|
|
|
673
673
|
firstName,
|
|
674
674
|
lastName,
|
|
675
675
|
projectId,
|
|
676
|
-
credentials: [{ type: "password", value: password, temporary: false }]
|
|
676
|
+
credentials: [{ type: "password", value: password, temporary: false }],
|
|
677
|
+
metadata
|
|
677
678
|
});
|
|
678
679
|
await authAdmin.assignRealmRole(kcUser.id, "platform_user");
|
|
679
680
|
const user = await OrgUser.findOneAndUpdate(
|
|
680
681
|
{ email: kcUser.email },
|
|
681
682
|
{
|
|
682
|
-
id: kcUser.id,
|
|
683
683
|
email: kcUser.email,
|
|
684
684
|
firstName,
|
|
685
685
|
lastName,
|
|
@@ -719,8 +719,9 @@ function createAuthRouter(options = {}) {
|
|
|
719
719
|
return res.json(req.user || null);
|
|
720
720
|
});
|
|
721
721
|
r.post("/logout", async (_req, res) => {
|
|
722
|
-
|
|
723
|
-
res.clearCookie("
|
|
722
|
+
const clearOptions = buildClearCookieOptions(cookieConfig);
|
|
723
|
+
res.clearCookie("access_token", clearOptions);
|
|
724
|
+
res.clearCookie("refresh_token", clearOptions);
|
|
724
725
|
res.json({ ok: true });
|
|
725
726
|
});
|
|
726
727
|
r.put("/:userId/metadata", requireAuth(), async (req, res) => {
|
|
@@ -959,16 +960,186 @@ function createAuthRouter(options = {}) {
|
|
|
959
960
|
const user = await OrgUser.findOne({ email: req.query.email }).lean();
|
|
960
961
|
res.json(user || null);
|
|
961
962
|
});
|
|
962
|
-
r.get("/google",
|
|
963
|
-
|
|
963
|
+
r.get("/google", (req, res) => {
|
|
964
|
+
if (!isGoogleEnabled) {
|
|
965
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
966
|
+
}
|
|
967
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
968
|
+
const params = new URLSearchParams({
|
|
969
|
+
client_id: googleClientId,
|
|
970
|
+
redirect_uri: googleRedirectUri,
|
|
971
|
+
response_type: "code",
|
|
972
|
+
scope: "openid email profile",
|
|
973
|
+
access_type: "offline",
|
|
974
|
+
prompt: "consent",
|
|
975
|
+
state
|
|
976
|
+
});
|
|
977
|
+
const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
|
|
978
|
+
res.redirect(url);
|
|
964
979
|
});
|
|
965
|
-
r.get("/google/callback", async (
|
|
966
|
-
|
|
967
|
-
"
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
);
|
|
971
|
-
|
|
980
|
+
r.get("/google/callback", async (req, res) => {
|
|
981
|
+
if (!isGoogleEnabled) {
|
|
982
|
+
return res.status(500).json({ error: "Google login not configured" });
|
|
983
|
+
}
|
|
984
|
+
const code = String(req.query.code || "");
|
|
985
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
986
|
+
if (!code) {
|
|
987
|
+
return res.status(400).json({ ok: false, error: "Missing authorization code" });
|
|
988
|
+
}
|
|
989
|
+
try {
|
|
990
|
+
const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
|
|
991
|
+
method: "POST",
|
|
992
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
993
|
+
body: new URLSearchParams({
|
|
994
|
+
code,
|
|
995
|
+
client_id: googleClientId,
|
|
996
|
+
client_secret: googleClientSecret,
|
|
997
|
+
redirect_uri: googleRedirectUri,
|
|
998
|
+
grant_type: "authorization_code"
|
|
999
|
+
})
|
|
1000
|
+
});
|
|
1001
|
+
if (!tokenRes.ok) {
|
|
1002
|
+
const errJson = await tokenRes.json().catch(() => ({}));
|
|
1003
|
+
console.error("Google token error", errJson);
|
|
1004
|
+
return res.status(400).json({ ok: false, error: "Failed to exchange Google code" });
|
|
1005
|
+
}
|
|
1006
|
+
const tokenJson = await tokenRes.json();
|
|
1007
|
+
if (!tokenJson.id_token) {
|
|
1008
|
+
return res.status(400).json({ ok: false, error: "Missing id_token from Google" });
|
|
1009
|
+
}
|
|
1010
|
+
const decoded = jwt4.decode(tokenJson.id_token);
|
|
1011
|
+
const email2 = decoded?.email;
|
|
1012
|
+
if (!email2) {
|
|
1013
|
+
return res.status(400).json({ ok: false, error: "Google account has no email" });
|
|
1014
|
+
}
|
|
1015
|
+
const emailVerified = decoded.email_verified ?? true;
|
|
1016
|
+
const firstName = decoded.given_name || "";
|
|
1017
|
+
const lastName = decoded.family_name || "";
|
|
1018
|
+
let user = await OrgUser.findOne({ email: email2 }).lean();
|
|
1019
|
+
if (!user) {
|
|
1020
|
+
const created = await OrgUser.create({
|
|
1021
|
+
email: email2,
|
|
1022
|
+
firstName,
|
|
1023
|
+
lastName,
|
|
1024
|
+
emailVerified,
|
|
1025
|
+
roles: ["platform_user"],
|
|
1026
|
+
projectId: null,
|
|
1027
|
+
metadata: []
|
|
1028
|
+
// you can also store googleId: decoded.sub
|
|
1029
|
+
});
|
|
1030
|
+
user = created.toObject();
|
|
1031
|
+
}
|
|
1032
|
+
const tokens = generateTokens(user);
|
|
1033
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1034
|
+
const redirectTo = state ? decodeURIComponent(state) : googleDefaultRedirect;
|
|
1035
|
+
res.redirect(redirectTo);
|
|
1036
|
+
} catch (err) {
|
|
1037
|
+
console.error("Google callback error", err);
|
|
1038
|
+
const redirectError = googleDefaultRedirect.includes("?") ? `${googleDefaultRedirect}&error=google_login_failed` : `${googleDefaultRedirect}?error=google_login_failed`;
|
|
1039
|
+
res.redirect(redirectError);
|
|
1040
|
+
}
|
|
1041
|
+
});
|
|
1042
|
+
r.get("/github", (req, res) => {
|
|
1043
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1044
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1045
|
+
if (!githubClientId || !githubRedirectUri) {
|
|
1046
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1047
|
+
}
|
|
1048
|
+
const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
|
|
1049
|
+
const params = new URLSearchParams({
|
|
1050
|
+
client_id: githubClientId,
|
|
1051
|
+
redirect_uri: githubRedirectUri,
|
|
1052
|
+
scope: "user:email",
|
|
1053
|
+
state,
|
|
1054
|
+
allow_signup: "true"
|
|
1055
|
+
});
|
|
1056
|
+
const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
|
|
1057
|
+
res.redirect(url);
|
|
1058
|
+
});
|
|
1059
|
+
r.get("/github/callback", async (req, res) => {
|
|
1060
|
+
const githubClientId = process.env.GITHUB_CLIENT_ID;
|
|
1061
|
+
const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
|
|
1062
|
+
const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
|
|
1063
|
+
const githubDefaultRedirect = process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
|
|
1064
|
+
if (!githubClientId || !githubClientSecret || !githubRedirectUri) {
|
|
1065
|
+
return res.status(500).json({ error: "GitHub login not configured" });
|
|
1066
|
+
}
|
|
1067
|
+
const code = String(req.query.code || "");
|
|
1068
|
+
const state = req.query.state ? String(req.query.state) : "";
|
|
1069
|
+
if (!code) {
|
|
1070
|
+
return res.status(400).json({ ok: false, error: "Missing GitHub code" });
|
|
1071
|
+
}
|
|
1072
|
+
try {
|
|
1073
|
+
const tokenRes = await fetch(
|
|
1074
|
+
"https://github.com/login/oauth/access_token",
|
|
1075
|
+
{
|
|
1076
|
+
method: "POST",
|
|
1077
|
+
headers: {
|
|
1078
|
+
Accept: "application/json",
|
|
1079
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
1080
|
+
},
|
|
1081
|
+
body: new URLSearchParams({
|
|
1082
|
+
client_id: githubClientId,
|
|
1083
|
+
client_secret: githubClientSecret,
|
|
1084
|
+
code,
|
|
1085
|
+
redirect_uri: githubRedirectUri
|
|
1086
|
+
})
|
|
1087
|
+
}
|
|
1088
|
+
);
|
|
1089
|
+
const tokenJson = await tokenRes.json();
|
|
1090
|
+
if (!tokenJson.access_token) {
|
|
1091
|
+
console.error("GitHub token error:", tokenJson);
|
|
1092
|
+
return res.status(400).json({ ok: false, error: "Failed to get GitHub access token" });
|
|
1093
|
+
}
|
|
1094
|
+
const accessToken = tokenJson.access_token;
|
|
1095
|
+
const userRes = await fetch("https://api.github.com/user", {
|
|
1096
|
+
headers: {
|
|
1097
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1098
|
+
Accept: "application/vnd.github+json"
|
|
1099
|
+
}
|
|
1100
|
+
});
|
|
1101
|
+
const githubUser = await userRes.json();
|
|
1102
|
+
const emailRes = await fetch("https://api.github.com/user/emails", {
|
|
1103
|
+
headers: {
|
|
1104
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1105
|
+
Accept: "application/vnd.github+json"
|
|
1106
|
+
}
|
|
1107
|
+
});
|
|
1108
|
+
const emails = await emailRes.json();
|
|
1109
|
+
const primaryEmail = emails?.find(
|
|
1110
|
+
(e) => e.primary && e.verified
|
|
1111
|
+
)?.email;
|
|
1112
|
+
if (!primaryEmail) {
|
|
1113
|
+
return res.status(400).json({
|
|
1114
|
+
ok: false,
|
|
1115
|
+
error: "GitHub account has no verified email"
|
|
1116
|
+
});
|
|
1117
|
+
}
|
|
1118
|
+
const firstName = githubUser.name?.split(" ")[0] || "";
|
|
1119
|
+
const lastName = githubUser.name?.split(" ").slice(1).join(" ") || "";
|
|
1120
|
+
let user = await OrgUser.findOne({ email: primaryEmail }).lean();
|
|
1121
|
+
if (!user) {
|
|
1122
|
+
const created = await OrgUser.create({
|
|
1123
|
+
email: primaryEmail,
|
|
1124
|
+
firstName,
|
|
1125
|
+
lastName,
|
|
1126
|
+
emailVerified: true,
|
|
1127
|
+
roles: ["platform_user"],
|
|
1128
|
+
projectId: null,
|
|
1129
|
+
metadata: [],
|
|
1130
|
+
githubId: githubUser.id
|
|
1131
|
+
});
|
|
1132
|
+
user = created.toObject();
|
|
1133
|
+
}
|
|
1134
|
+
const tokens = generateTokens(user);
|
|
1135
|
+
setAuthCookies(res, tokens, cookieConfig);
|
|
1136
|
+
const redirectTo = state ? decodeURIComponent(state) : githubDefaultRedirect;
|
|
1137
|
+
res.redirect(redirectTo);
|
|
1138
|
+
} catch (err) {
|
|
1139
|
+
console.error("GitHub callback error:", err);
|
|
1140
|
+
const redirectError = githubDefaultRedirect.includes("?") ? `${githubDefaultRedirect}&error=github_login_failed` : `${githubDefaultRedirect}?error=github_login_failed`;
|
|
1141
|
+
res.redirect(redirectError);
|
|
1142
|
+
}
|
|
972
1143
|
});
|
|
973
1144
|
r.get("/get-users", async (req, res) => {
|
|
974
1145
|
const user = await OrgUser.find({ projectId: req.query.projectId }).lean();
|