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.d.cts CHANGED
@@ -774,6 +774,7 @@ declare class AuthAdminService {
774
774
  lastName?: string;
775
775
  credentials?: any[];
776
776
  emailVerified?: boolean;
777
+ metadata?: any[];
777
778
  }): Promise<mongoose.Document<unknown, {}, {
778
779
  email: string;
779
780
  roles: string[];
package/dist/index.d.ts CHANGED
@@ -774,6 +774,7 @@ declare class AuthAdminService {
774
774
  lastName?: string;
775
775
  credentials?: any[];
776
776
  emailVerified?: boolean;
777
+ metadata?: any[];
777
778
  }): Promise<mongoose.Document<unknown, {}, {
778
779
  email: string;
779
780
  roles: string[];
package/dist/index.js CHANGED
@@ -31,50 +31,6 @@ import express, {
31
31
  } from "express";
32
32
  import jwt4 from "jsonwebtoken";
33
33
 
34
- // src/core/utils.ts
35
- function hasRole(session, role) {
36
- if (!session || !session.roles) return false;
37
- return session.roles.includes(role);
38
- }
39
- function baseProjectCookieOptionsFrom(cookie) {
40
- const base = {
41
- secure: cookie.secure ?? false,
42
- sameSite: cookie.sameSite ?? "lax",
43
- path: cookie.path ?? "/",
44
- maxAge: cookie.maxAgeMs
45
- };
46
- if (cookie.domain) base.domain = cookie.domain;
47
- return base;
48
- }
49
- function hasAnyRole(session, roles) {
50
- if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
51
- return false;
52
- }
53
- return roles.some((role) => session.roles.includes(role));
54
- }
55
- function hasAllRoles(session, roles) {
56
- if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
57
- return false;
58
- }
59
- return roles.every((role) => session.roles.includes(role));
60
- }
61
- function hasPermission(session, permission) {
62
- if (!session || !session.permissions) return false;
63
- return session.permissions.includes(permission);
64
- }
65
- function hasAnyPermission(session, permissions) {
66
- if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
67
- return false;
68
- }
69
- return permissions.some((perm) => session.permissions.includes(perm));
70
- }
71
- function hasAllPermissions(session, permissions) {
72
- if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
73
- return false;
74
- }
75
- return permissions.every((perm) => session.permissions.includes(perm));
76
- }
77
-
78
34
  // src/config/loadConfig.ts
79
35
  function loadConfig() {
80
36
  return {
@@ -138,6 +94,63 @@ function isPlainObject(value) {
138
94
  return typeof value === "object" && value !== null && !Array.isArray(value);
139
95
  }
140
96
 
97
+ // src/core/utils.ts
98
+ function hasRole(session, role) {
99
+ if (!session || !session.roles) return false;
100
+ return session.roles.includes(role);
101
+ }
102
+ function baseProjectCookieOptionsFrom(cookie) {
103
+ const base = {
104
+ secure: cookie.secure ?? false,
105
+ sameSite: cookie.sameSite ?? "lax",
106
+ path: cookie.path ?? "/",
107
+ maxAge: cookie.maxAgeMs
108
+ };
109
+ if (cookie.domain) base.domain = cookie.domain;
110
+ return base;
111
+ }
112
+ function buildClearCookieOptions(cookie) {
113
+ const opts = {
114
+ httpOnly: true,
115
+ // not strictly required but fine
116
+ secure: cookie.secure ?? false,
117
+ sameSite: cookie.sameSite ?? "lax",
118
+ path: cookie.path ?? "/"
119
+ };
120
+ if (cookie.domain) {
121
+ opts.domain = cookie.domain;
122
+ }
123
+ return opts;
124
+ }
125
+ function hasAnyRole(session, roles) {
126
+ if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
127
+ return false;
128
+ }
129
+ return roles.some((role) => session.roles.includes(role));
130
+ }
131
+ function hasAllRoles(session, roles) {
132
+ if (!session || !session.roles || !Array.isArray(roles) || roles.length === 0) {
133
+ return false;
134
+ }
135
+ return roles.every((role) => session.roles.includes(role));
136
+ }
137
+ function hasPermission(session, permission) {
138
+ if (!session || !session.permissions) return false;
139
+ return session.permissions.includes(permission);
140
+ }
141
+ function hasAnyPermission(session, permissions) {
142
+ if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
143
+ return false;
144
+ }
145
+ return permissions.some((perm) => session.permissions.includes(perm));
146
+ }
147
+ function hasAllPermissions(session, permissions) {
148
+ if (!session || !session.permissions || !Array.isArray(permissions) || permissions.length === 0) {
149
+ return false;
150
+ }
151
+ return permissions.every((perm) => session.permissions.includes(perm));
152
+ }
153
+
141
154
  // src/core/roles.config.ts
142
155
  var PLATFORM_ROLES = [
143
156
  {
@@ -239,7 +252,7 @@ var MetadataSchema = new mongoose2.Schema(
239
252
  );
240
253
  var OrgUserSchema = new mongoose2.Schema(
241
254
  {
242
- id: { type: String, default: uuid(), index: true },
255
+ id: { type: String, default: uuid(), index: true, unique: true },
243
256
  email: { type: String, required: true, unique: true },
244
257
  firstName: { type: String, required: true },
245
258
  lastName: { type: String, required: true },
@@ -474,6 +487,7 @@ var Invite = mongoose3.model("Invite", InviteSchema);
474
487
  // src/services/auth-admin.service.ts
475
488
  import bcrypt from "bcrypt";
476
489
  import jwt2 from "jsonwebtoken";
490
+ import { v4 as uuid2 } from "uuid";
477
491
 
478
492
  // src/models/client.model.ts
479
493
  import mongoose4, { Schema as Schema2 } from "mongoose";
@@ -543,14 +557,14 @@ var AuthAdminService = class {
543
557
  async createUserInRealm(payload) {
544
558
  const hashedPassword = payload.credentials?.[0]?.value ? await bcrypt.hash(payload.credentials[0].value, 10) : void 0;
545
559
  const user = await OrgUser.create({
546
- username: payload.username,
560
+ id: uuid2(),
547
561
  email: payload.email,
548
562
  firstName: payload.firstName,
549
563
  lastName: payload.lastName,
550
564
  projectId: payload.projectId,
551
565
  emailVerified: payload.emailVerified || false,
552
566
  passwordHash: hashedPassword,
553
- enabled: true
567
+ metadata: payload.metadata || []
554
568
  });
555
569
  return user;
556
570
  }
@@ -638,29 +652,13 @@ var EmailService = class {
638
652
  }
639
653
  };
640
654
 
641
- // src/utils/cookie.ts
642
- function cookieOpts(isRefresh = false) {
643
- const maxAge = isRefresh ? config.cookies.refreshTtlMs : config.cookies.accessTtlMs;
644
- const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
645
- return {
646
- httpOnly: true,
647
- secure,
648
- sameSite: "none",
649
- domain: process.env.COOKIE_DOMAIN,
650
- maxAge
651
- };
652
- }
653
- function clearOpts() {
654
- const secure = process.env.NODE_ENV === "production" ? process.env.COOKIE_SECURE ?? true : false;
655
- return {
656
- domain: process.env.COOKIE_DOMAIN,
657
- sameSite: "none",
658
- secure
659
- };
660
- }
661
-
662
655
  // src/express/auth.routes.ts
663
656
  function createAuthRouter(options = {}) {
657
+ const googleClientId = process.env.GOOGLE_CLIENT_ID;
658
+ const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET;
659
+ const googleRedirectUri = process.env.GOOGLE_REDIRECT_URI;
660
+ const googleDefaultRedirect = process.env.GOOGLE_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
661
+ const isGoogleEnabled = !!googleClientId && !!googleClientSecret && !!googleRedirectUri;
664
662
  if (options.config) {
665
663
  configureAuthX(options.config);
666
664
  }
@@ -685,8 +683,10 @@ function createAuthRouter(options = {}) {
685
683
  );
686
684
  r.post("/login", validateLogin, async (req, res) => {
687
685
  const { email: emailAddress, password } = req.body || {};
686
+ console.log(emailAddress, password, "body");
688
687
  try {
689
688
  const user = await OrgUser.findOne({ email: emailAddress }).select("+password").lean();
689
+ console.log(user, "user");
690
690
  if (!user) {
691
691
  return res.status(400).json({
692
692
  error: "Invalid email or password",
@@ -739,13 +739,13 @@ function createAuthRouter(options = {}) {
739
739
  firstName,
740
740
  lastName,
741
741
  projectId,
742
- credentials: [{ type: "password", value: password, temporary: false }]
742
+ credentials: [{ type: "password", value: password, temporary: false }],
743
+ metadata
743
744
  });
744
745
  await authAdmin.assignRealmRole(kcUser.id, "platform_user");
745
746
  const user = await OrgUser.findOneAndUpdate(
746
747
  { email: kcUser.email },
747
748
  {
748
- id: kcUser.id,
749
749
  email: kcUser.email,
750
750
  firstName,
751
751
  lastName,
@@ -785,8 +785,9 @@ function createAuthRouter(options = {}) {
785
785
  return res.json(req.user || null);
786
786
  });
787
787
  r.post("/logout", async (_req, res) => {
788
- res.clearCookie("access_token", clearOpts());
789
- res.clearCookie("refresh_token", clearOpts());
788
+ const clearOptions = buildClearCookieOptions(cookieConfig);
789
+ res.clearCookie("access_token", clearOptions);
790
+ res.clearCookie("refresh_token", clearOptions);
790
791
  res.json({ ok: true });
791
792
  });
792
793
  r.put("/:userId/metadata", requireAuth(), async (req, res) => {
@@ -1025,16 +1026,186 @@ function createAuthRouter(options = {}) {
1025
1026
  const user = await OrgUser.findOne({ email: req.query.email }).lean();
1026
1027
  res.json(user || null);
1027
1028
  });
1028
- r.get("/google", async (_req, res) => {
1029
- res.json({ url: "/auth/google/callback?code=demo" });
1029
+ r.get("/google", (req, res) => {
1030
+ if (!isGoogleEnabled) {
1031
+ return res.status(500).json({ error: "Google login not configured" });
1032
+ }
1033
+ const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
1034
+ const params = new URLSearchParams({
1035
+ client_id: googleClientId,
1036
+ redirect_uri: googleRedirectUri,
1037
+ response_type: "code",
1038
+ scope: "openid email profile",
1039
+ access_type: "offline",
1040
+ prompt: "consent",
1041
+ state
1042
+ });
1043
+ const url = `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
1044
+ res.redirect(url);
1030
1045
  });
1031
- r.get("/google/callback", async (_req, res) => {
1032
- res.cookie(
1033
- "access_token",
1034
- "ACCESS.TOKEN.PLACEHOLDER",
1035
- cookieOpts(false)
1036
- );
1037
- res.redirect("/");
1046
+ r.get("/google/callback", async (req, res) => {
1047
+ if (!isGoogleEnabled) {
1048
+ return res.status(500).json({ error: "Google login not configured" });
1049
+ }
1050
+ const code = String(req.query.code || "");
1051
+ const state = req.query.state ? String(req.query.state) : "";
1052
+ if (!code) {
1053
+ return res.status(400).json({ ok: false, error: "Missing authorization code" });
1054
+ }
1055
+ try {
1056
+ const tokenRes = await fetch("https://oauth2.googleapis.com/token", {
1057
+ method: "POST",
1058
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
1059
+ body: new URLSearchParams({
1060
+ code,
1061
+ client_id: googleClientId,
1062
+ client_secret: googleClientSecret,
1063
+ redirect_uri: googleRedirectUri,
1064
+ grant_type: "authorization_code"
1065
+ })
1066
+ });
1067
+ if (!tokenRes.ok) {
1068
+ const errJson = await tokenRes.json().catch(() => ({}));
1069
+ console.error("Google token error", errJson);
1070
+ return res.status(400).json({ ok: false, error: "Failed to exchange Google code" });
1071
+ }
1072
+ const tokenJson = await tokenRes.json();
1073
+ if (!tokenJson.id_token) {
1074
+ return res.status(400).json({ ok: false, error: "Missing id_token from Google" });
1075
+ }
1076
+ const decoded = jwt4.decode(tokenJson.id_token);
1077
+ const email2 = decoded?.email;
1078
+ if (!email2) {
1079
+ return res.status(400).json({ ok: false, error: "Google account has no email" });
1080
+ }
1081
+ const emailVerified = decoded.email_verified ?? true;
1082
+ const firstName = decoded.given_name || "";
1083
+ const lastName = decoded.family_name || "";
1084
+ let user = await OrgUser.findOne({ email: email2 }).lean();
1085
+ if (!user) {
1086
+ const created = await OrgUser.create({
1087
+ email: email2,
1088
+ firstName,
1089
+ lastName,
1090
+ emailVerified,
1091
+ roles: ["platform_user"],
1092
+ projectId: null,
1093
+ metadata: []
1094
+ // you can also store googleId: decoded.sub
1095
+ });
1096
+ user = created.toObject();
1097
+ }
1098
+ const tokens = generateTokens(user);
1099
+ setAuthCookies(res, tokens, cookieConfig);
1100
+ const redirectTo = state ? decodeURIComponent(state) : googleDefaultRedirect;
1101
+ res.redirect(redirectTo);
1102
+ } catch (err) {
1103
+ console.error("Google callback error", err);
1104
+ const redirectError = googleDefaultRedirect.includes("?") ? `${googleDefaultRedirect}&error=google_login_failed` : `${googleDefaultRedirect}?error=google_login_failed`;
1105
+ res.redirect(redirectError);
1106
+ }
1107
+ });
1108
+ r.get("/github", (req, res) => {
1109
+ const githubClientId = process.env.GITHUB_CLIENT_ID;
1110
+ const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
1111
+ if (!githubClientId || !githubRedirectUri) {
1112
+ return res.status(500).json({ error: "GitHub login not configured" });
1113
+ }
1114
+ const state = req.query.redirectTo ? encodeURIComponent(String(req.query.redirectTo)) : "";
1115
+ const params = new URLSearchParams({
1116
+ client_id: githubClientId,
1117
+ redirect_uri: githubRedirectUri,
1118
+ scope: "user:email",
1119
+ state,
1120
+ allow_signup: "true"
1121
+ });
1122
+ const url = `https://github.com/login/oauth/authorize?${params.toString()}`;
1123
+ res.redirect(url);
1124
+ });
1125
+ r.get("/github/callback", async (req, res) => {
1126
+ const githubClientId = process.env.GITHUB_CLIENT_ID;
1127
+ const githubClientSecret = process.env.GITHUB_CLIENT_SECRET;
1128
+ const githubRedirectUri = process.env.GITHUB_REDIRECT_URI;
1129
+ const githubDefaultRedirect = process.env.GITHUB_DEFAULT_REDIRECT || getFrontendBaseUrl(options) || "/";
1130
+ if (!githubClientId || !githubClientSecret || !githubRedirectUri) {
1131
+ return res.status(500).json({ error: "GitHub login not configured" });
1132
+ }
1133
+ const code = String(req.query.code || "");
1134
+ const state = req.query.state ? String(req.query.state) : "";
1135
+ if (!code) {
1136
+ return res.status(400).json({ ok: false, error: "Missing GitHub code" });
1137
+ }
1138
+ try {
1139
+ const tokenRes = await fetch(
1140
+ "https://github.com/login/oauth/access_token",
1141
+ {
1142
+ method: "POST",
1143
+ headers: {
1144
+ Accept: "application/json",
1145
+ "Content-Type": "application/x-www-form-urlencoded"
1146
+ },
1147
+ body: new URLSearchParams({
1148
+ client_id: githubClientId,
1149
+ client_secret: githubClientSecret,
1150
+ code,
1151
+ redirect_uri: githubRedirectUri
1152
+ })
1153
+ }
1154
+ );
1155
+ const tokenJson = await tokenRes.json();
1156
+ if (!tokenJson.access_token) {
1157
+ console.error("GitHub token error:", tokenJson);
1158
+ return res.status(400).json({ ok: false, error: "Failed to get GitHub access token" });
1159
+ }
1160
+ const accessToken = tokenJson.access_token;
1161
+ const userRes = await fetch("https://api.github.com/user", {
1162
+ headers: {
1163
+ Authorization: `Bearer ${accessToken}`,
1164
+ Accept: "application/vnd.github+json"
1165
+ }
1166
+ });
1167
+ const githubUser = await userRes.json();
1168
+ const emailRes = await fetch("https://api.github.com/user/emails", {
1169
+ headers: {
1170
+ Authorization: `Bearer ${accessToken}`,
1171
+ Accept: "application/vnd.github+json"
1172
+ }
1173
+ });
1174
+ const emails = await emailRes.json();
1175
+ const primaryEmail = emails?.find(
1176
+ (e) => e.primary && e.verified
1177
+ )?.email;
1178
+ if (!primaryEmail) {
1179
+ return res.status(400).json({
1180
+ ok: false,
1181
+ error: "GitHub account has no verified email"
1182
+ });
1183
+ }
1184
+ const firstName = githubUser.name?.split(" ")[0] || "";
1185
+ const lastName = githubUser.name?.split(" ").slice(1).join(" ") || "";
1186
+ let user = await OrgUser.findOne({ email: primaryEmail }).lean();
1187
+ if (!user) {
1188
+ const created = await OrgUser.create({
1189
+ email: primaryEmail,
1190
+ firstName,
1191
+ lastName,
1192
+ emailVerified: true,
1193
+ roles: ["platform_user"],
1194
+ projectId: null,
1195
+ metadata: [],
1196
+ githubId: githubUser.id
1197
+ });
1198
+ user = created.toObject();
1199
+ }
1200
+ const tokens = generateTokens(user);
1201
+ setAuthCookies(res, tokens, cookieConfig);
1202
+ const redirectTo = state ? decodeURIComponent(state) : githubDefaultRedirect;
1203
+ res.redirect(redirectTo);
1204
+ } catch (err) {
1205
+ console.error("GitHub callback error:", err);
1206
+ const redirectError = githubDefaultRedirect.includes("?") ? `${githubDefaultRedirect}&error=github_login_failed` : `${githubDefaultRedirect}?error=github_login_failed`;
1207
+ res.redirect(redirectError);
1208
+ }
1038
1209
  });
1039
1210
  r.get("/get-users", async (req, res) => {
1040
1211
  const user = await OrgUser.find({ projectId: req.query.projectId }).lean();