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/index.cjs CHANGED
@@ -83,50 +83,6 @@ var import_crypto = require("crypto");
83
83
  var import_express = __toESM(require("express"), 1);
84
84
  var import_jsonwebtoken4 = __toESM(require("jsonwebtoken"), 1);
85
85
 
86
- // src/core/utils.ts
87
- function hasRole(session, role) {
88
- if (!session || !session.roles) return false;
89
- return session.roles.includes(role);
90
- }
91
- function baseProjectCookieOptionsFrom(cookie) {
92
- const base = {
93
- secure: cookie.secure ?? false,
94
- sameSite: cookie.sameSite ?? "lax",
95
- path: cookie.path ?? "/",
96
- maxAge: cookie.maxAgeMs
97
- };
98
- if (cookie.domain) base.domain = cookie.domain;
99
- return base;
100
- }
101
- function hasAnyRole(session, roles) {
102
- if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
103
- return false;
104
- }
105
- return roles.some((role) => session.roles.includes(role));
106
- }
107
- function hasAllRoles(session, roles) {
108
- if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
109
- return false;
110
- }
111
- return roles.every((role) => session.roles.includes(role));
112
- }
113
- function hasPermission(session, permission) {
114
- if (!session || !session.permissions) return false;
115
- return session.permissions.includes(permission);
116
- }
117
- function hasAnyPermission(session, permissions) {
118
- if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
119
- return false;
120
- }
121
- return permissions.some((perm) => session.permissions.includes(perm));
122
- }
123
- function hasAllPermissions(session, permissions) {
124
- if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
125
- return false;
126
- }
127
- return permissions.every((perm) => session.permissions.includes(perm));
128
- }
129
-
130
86
  // src/config/loadConfig.ts
131
87
  function loadConfig() {
132
88
  return {
@@ -190,6 +146,63 @@ function isPlainObject(value) {
190
146
  return typeof value === "object" && value !== null && !Array.isArray(value);
191
147
  }
192
148
 
149
+ // src/core/utils.ts
150
+ function hasRole(session, role) {
151
+ if (!session || !session.roles) return false;
152
+ return session.roles.includes(role);
153
+ }
154
+ function baseProjectCookieOptionsFrom(cookie) {
155
+ const base = {
156
+ secure: cookie.secure ?? false,
157
+ sameSite: cookie.sameSite ?? "lax",
158
+ path: cookie.path ?? "/",
159
+ maxAge: cookie.maxAgeMs
160
+ };
161
+ if (cookie.domain) base.domain = cookie.domain;
162
+ return base;
163
+ }
164
+ function buildClearCookieOptions(cookie) {
165
+ const opts = {
166
+ httpOnly: true,
167
+ // not strictly required but fine
168
+ secure: cookie.secure ?? false,
169
+ sameSite: cookie.sameSite ?? "lax",
170
+ path: cookie.path ?? "/"
171
+ };
172
+ if (cookie.domain) {
173
+ opts.domain = cookie.domain;
174
+ }
175
+ return opts;
176
+ }
177
+ function hasAnyRole(session, roles) {
178
+ if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
179
+ return false;
180
+ }
181
+ return roles.some((role) => session.roles.includes(role));
182
+ }
183
+ function hasAllRoles(session, roles) {
184
+ if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
185
+ return false;
186
+ }
187
+ return roles.every((role) => session.roles.includes(role));
188
+ }
189
+ function hasPermission(session, permission) {
190
+ if (!session || !session.permissions) return false;
191
+ return session.permissions.includes(permission);
192
+ }
193
+ function hasAnyPermission(session, permissions) {
194
+ if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
195
+ return false;
196
+ }
197
+ return permissions.some((perm) => session.permissions.includes(perm));
198
+ }
199
+ function hasAllPermissions(session, permissions) {
200
+ if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
201
+ return false;
202
+ }
203
+ return permissions.every((perm) => session.permissions.includes(perm));
204
+ }
205
+
193
206
  // src/core/roles.config.ts
194
207
  var PLATFORM_ROLES = [
195
208
  {
@@ -291,7 +304,7 @@ var MetadataSchema = new import_mongoose2.default.Schema(
291
304
  );
292
305
  var OrgUserSchema = new import_mongoose2.default.Schema(
293
306
  {
294
- id: { type: String, default: (0, import_uuid.v4)(), index: true },
307
+ id: { type: String, default: (0, import_uuid.v4)(), index: true, unique: true },
295
308
  email: { type: String, required: true, unique: true },
296
309
  firstName: { type: String, required: true },
297
310
  lastName: { type: String, required: true },
@@ -526,6 +539,7 @@ var Invite = import_mongoose3.default.model("Invite", InviteSchema);
526
539
  // src/services/auth-admin.service.ts
527
540
  var import_bcrypt = __toESM(require("bcrypt"), 1);
528
541
  var import_jsonwebtoken2 = __toESM(require("jsonwebtoken"), 1);
542
+ var import_uuid2 = require("uuid");
529
543
 
530
544
  // src/models/client.model.ts
531
545
  var import_mongoose4 = __toESM(require("mongoose"), 1);
@@ -595,14 +609,14 @@ var AuthAdminService = class {
595
609
  async createUserInRealm(payload) {
596
610
  const hashedPassword = payload.credentials?.[0]?.value ? await import_bcrypt.default.hash(payload.credentials[0].value, 10) : void 0;
597
611
  const user = await OrgUser.create({
598
- username: payload.username,
612
+ id: (0, import_uuid2.v4)(),
599
613
  email: payload.email,
600
614
  firstName: payload.firstName,
601
615
  lastName: payload.lastName,
602
616
  projectId: payload.projectId,
603
617
  emailVerified: payload.emailVerified || false,
604
618
  passwordHash: hashedPassword,
605
- enabled: true
619
+ metadata: payload.metadata || []
606
620
  });
607
621
  return user;
608
622
  }
@@ -690,29 +704,13 @@ var EmailService = class {
690
704
  }
691
705
  };
692
706
 
693
- // src/utils/cookie.ts
694
- function cookieOpts(isRefresh = false) {
695
- const maxAge = isRefresh ? config.cookies.refreshTtlMs : config.cookies.accessTtlMs;
696
- const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
697
- return {
698
- httpOnly: true,
699
- secure,
700
- sameSite: "none",
701
- domain: process.env.COOKIE_DOMAIN,
702
- maxAge
703
- };
704
- }
705
- function clearOpts() {
706
- const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
707
- return {
708
- domain: process.env.COOKIE_DOMAIN,
709
- sameSite: "none",
710
- secure
711
- };
712
- }
713
-
714
707
  // src/express/auth.routes.ts
715
708
  function createAuthRouter(options = {}) {
709
+ const googleClientId = process.env.GOOGLE_CLIENT_ID;
710
+ const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
711
+ const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;
712
+ const googleDefaultRedirect = process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
713
+ const isGoogleEnabled = !!googleClientId && !!googleClientSecret && !!googleRedirectUri;
716
714
  if (options.config) {
717
715
  configureAuthX(options.config);
718
716
  }
@@ -737,8 +735,10 @@ function createAuthRouter(options = {}) {
737
735
  );
738
736
  r.post("/login", validateLogin, async (req, res) => {
739
737
  const { email: emailAddress, password } = req.body || {};
738
+ console.log(emailAddress, password, "body");
740
739
  try {
741
740
  const user = await OrgUser.findOne({ email: emailAddress }).select("+password").lean();
741
+ console.log(user, "user");
742
742
  if (!user) {
743
743
  return res.status(400).json({
744
744
  error: "Invalid email or password",
@@ -791,13 +791,13 @@ function createAuthRouter(options = {}) {
791
791
  firstName,
792
792
  lastName,
793
793
  projectId,
794
- credentials: [{ type: "password", value: password, temporary: false }]
794
+ credentials: [{ type: "password", value: password, temporary: false }],
795
+ metadata
795
796
  });
796
797
  await authAdmin.assignRealmRole(kcUser.id, "platform_user");
797
798
  const user = await OrgUser.findOneAndUpdate(
798
799
  { email: kcUser.email },
799
800
  {
800
- id: kcUser.id,
801
801
  email: kcUser.email,
802
802
  firstName,
803
803
  lastName,
@@ -837,8 +837,9 @@ function createAuthRouter(options = {}) {
837
837
  return res.json(req.user || null);
838
838
  });
839
839
  r.post("/logout", async (_req, res) => {
840
- res.clearCookie("access_token", clearOpts());
841
- res.clearCookie("refresh_token", clearOpts());
840
+ const clearOptions = buildClearCookieOptions(cookieConfig);
841
+ res.clearCookie("access_token", clearOptions);
842
+ res.clearCookie("refresh_token", clearOptions);
842
843
  res.json({ ok: true });
843
844
  });
844
845
  r.put("/:userId/metadata", requireAuth(), async (req, res) => {
@@ -1077,16 +1078,186 @@ function createAuthRouter(options = {}) {
1077
1078
  const user = await OrgUser.findOne({ email: req.query.email }).lean();
1078
1079
  res.json(user || null);
1079
1080
  });
1080
- r.get("/google", async (_req, res) => {
1081
- res.json({ url: "/auth/google/callback?code=demo" });
1081
+ r.get("/google", (req, res) => {
1082
+ if (!isGoogleEnabled) {
1083
+ return res.status(500).json({ error: "Google login not configured" });
1084
+ }
1085
+ const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
1086
+ const params = new URLSearchParams({
1087
+ client_id: googleClientId,
1088
+ redirect_uri: googleRedirectUri,
1089
+ response_type: "code",
1090
+ scope: "openid email profile",
1091
+ access_type: "offline",
1092
+ prompt: "consent",
1093
+ state
1094
+ });
1095
+ const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
1096
+ res.redirect(url);
1082
1097
  });
1083
- r.get("/google/callback", async (_req, res) => {
1084
- res.cookie(
1085
- "access_token",
1086
- "ACCESS.TOKEN.PLACEHOLDER",
1087
- cookieOpts(false)
1088
- );
1089
- res.redirect("/");
1098
+ r.get("/google/callback", async (req, res) => {
1099
+ if (!isGoogleEnabled) {
1100
+ return res.status(500).json({ error: "Google login not configured" });
1101
+ }
1102
+ const code = String(req.query.code || "");
1103
+ const state = req.query.state ? String(req.query.state) : "";
1104
+ if (!code) {
1105
+ return res.status(400).json({ ok: false, error: "Missing authorization code" });
1106
+ }
1107
+ try {
1108
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
1109
+ method: "POST",
1110
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1111
+ body: new URLSearchParams({
1112
+ code,
1113
+ client_id: googleClientId,
1114
+ client_secret: googleClientSecret,
1115
+ redirect_uri: googleRedirectUri,
1116
+ grant_type: "authorization_code"
1117
+ })
1118
+ });
1119
+ if (!tokenRes.ok) {
1120
+ const errJson = await tokenRes.json().catch(() => ({}));
1121
+ console.error("Google token error", errJson);
1122
+ return res.status(400).json({ ok: false, error: "Failed to exchange Google code" });
1123
+ }
1124
+ const tokenJson = await tokenRes.json();
1125
+ if (!tokenJson.id_token) {
1126
+ return res.status(400).json({ ok: false, error: "Missing id_token from Google" });
1127
+ }
1128
+ const decoded = import_jsonwebtoken4.default.decode(tokenJson.id_token);
1129
+ const email2 = decoded?.email;
1130
+ if (!email2) {
1131
+ return res.status(400).json({ ok: false, error: "Google account has no email" });
1132
+ }
1133
+ const emailVerified = decoded.email_verified ?? true;
1134
+ const firstName = decoded.given_name || "";
1135
+ const lastName = decoded.family_name || "";
1136
+ let user = await OrgUser.findOne({ email: email2 }).lean();
1137
+ if (!user) {
1138
+ const created = await OrgUser.create({
1139
+ email: email2,
1140
+ firstName,
1141
+ lastName,
1142
+ emailVerified,
1143
+ roles: ["platform_user"],
1144
+ projectId: null,
1145
+ metadata: []
1146
+ // you can also store googleId: decoded.sub
1147
+ });
1148
+ user = created.toObject();
1149
+ }
1150
+ const tokens = generateTokens(user);
1151
+ setAuthCookies(res, tokens, cookieConfig);
1152
+ const redirectTo = state ? decodeURIComponent(state) : googleDefaultRedirect;
1153
+ res.redirect(redirectTo);
1154
+ } catch (err) {
1155
+ console.error("Google callback error", err);
1156
+ const redirectError = googleDefaultRedirect.includes("?") ? `${googleDefaultRedirect}&error=google_login_failed` : `${googleDefaultRedirect}?error=google_login_failed`;
1157
+ res.redirect(redirectError);
1158
+ }
1159
+ });
1160
+ r.get("/github", (req, res) => {
1161
+ const githubClientId = process.env.GITHUB_CLIENT_ID;
1162
+ const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
1163
+ if (!githubClientId || !githubRedirectUri) {
1164
+ return res.status(500).json({ error: "GitHub login not configured" });
1165
+ }
1166
+ const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
1167
+ const params = new URLSearchParams({
1168
+ client_id: githubClientId,
1169
+ redirect_uri: githubRedirectUri,
1170
+ scope: "user:email",
1171
+ state,
1172
+ allow_signup: "true"
1173
+ });
1174
+ const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
1175
+ res.redirect(url);
1176
+ });
1177
+ r.get("/github/callback", async (req, res) => {
1178
+ const githubClientId = process.env.GITHUB_CLIENT_ID;
1179
+ const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
1180
+ const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
1181
+ const githubDefaultRedirect = process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
1182
+ if (!githubClientId || !githubClientSecret || !githubRedirectUri) {
1183
+ return res.status(500).json({ error: "GitHub login not configured" });
1184
+ }
1185
+ const code = String(req.query.code || "");
1186
+ const state = req.query.state ? String(req.query.state) : "";
1187
+ if (!code) {
1188
+ return res.status(400).json({ ok: false, error: "Missing GitHub code" });
1189
+ }
1190
+ try {
1191
+ const tokenRes = await fetch(
1192
+ "https://github.com/login/oauth/access_token",
1193
+ {
1194
+ method: "POST",
1195
+ headers: {
1196
+ Accept: "application/json",
1197
+ "Content-Type": "application/x-www-form-urlencoded"
1198
+ },
1199
+ body: new URLSearchParams({
1200
+ client_id: githubClientId,
1201
+ client_secret: githubClientSecret,
1202
+ code,
1203
+ redirect_uri: githubRedirectUri
1204
+ })
1205
+ }
1206
+ );
1207
+ const tokenJson = await tokenRes.json();
1208
+ if (!tokenJson.access_token) {
1209
+ console.error("GitHub token error:", tokenJson);
1210
+ return res.status(400).json({ ok: false, error: "Failed to get GitHub access token" });
1211
+ }
1212
+ const accessToken = tokenJson.access_token;
1213
+ const userRes = await fetch("https://api.github.com/user", {
1214
+ headers: {
1215
+ Authorization: `Bearer ${accessToken}`,
1216
+ Accept: "application/vnd.github+json"
1217
+ }
1218
+ });
1219
+ const githubUser = await userRes.json();
1220
+ const emailRes = await fetch("https://api.github.com/user/emails", {
1221
+ headers: {
1222
+ Authorization: `Bearer ${accessToken}`,
1223
+ Accept: "application/vnd.github+json"
1224
+ }
1225
+ });
1226
+ const emails = await emailRes.json();
1227
+ const primaryEmail = emails?.find(
1228
+ (e) => e.primary && e.verified
1229
+ )?.email;
1230
+ if (!primaryEmail) {
1231
+ return res.status(400).json({
1232
+ ok: false,
1233
+ error: "GitHub account has no verified email"
1234
+ });
1235
+ }
1236
+ const firstName = githubUser.name?.split(" ")[0] || "";
1237
+ const lastName = githubUser.name?.split(" ").slice(1).join(" ") || "";
1238
+ let user = await OrgUser.findOne({ email: primaryEmail }).lean();
1239
+ if (!user) {
1240
+ const created = await OrgUser.create({
1241
+ email: primaryEmail,
1242
+ firstName,
1243
+ lastName,
1244
+ emailVerified: true,
1245
+ roles: ["platform_user"],
1246
+ projectId: null,
1247
+ metadata: [],
1248
+ githubId: githubUser.id
1249
+ });
1250
+ user = created.toObject();
1251
+ }
1252
+ const tokens = generateTokens(user);
1253
+ setAuthCookies(res, tokens, cookieConfig);
1254
+ const redirectTo = state ? decodeURIComponent(state) : githubDefaultRedirect;
1255
+ res.redirect(redirectTo);
1256
+ } catch (err) {
1257
+ console.error("GitHub callback error:", err);
1258
+ const redirectError = githubDefaultRedirect.includes("?") ? `${githubDefaultRedirect}&error=github_login_failed` : `${githubDefaultRedirect}?error=github_login_failed`;
1259
+ res.redirect(redirectError);
1260
+ }
1090
1261
  });
1091
1262
  r.get("/get-users", async (req, res) => {
1092
1263
  const user = await OrgUser.find({ projectId: req.query.projectId }).lean();