@ciscode/authentication-kit 1.1.3 → 1.1.5

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/README.md CHANGED
@@ -74,10 +74,12 @@ POST /api/auth/refresh-token - New access token from a valid refresh token (cook
74
74
  POST /api/auth/request-password-reset - Sends a reset token (e.g., by email).
75
75
  POST /api/auth/reset-password - Consumes the reset token and sets a new password.
76
76
  GET /api/auth/microsoft - GET /api/auth/microsoft/callback - Optional Microsoft Entra OAuth; issues first-party tokens.
77
+ Note: If the Microsoft ID token does not include an email, the response includes `profileIncomplete: true`.
77
78
  Users
78
79
  GET /api/users - List users (paginated).
79
80
  POST /api/users - Create a user.
80
81
  Additional CRUD endpoints as exposed by controllers.
82
+ If `profileIncomplete: true`, update the user profile (e.g., set `email`) via `PUT /api/users/:id`.
81
83
  Roles and Permissions
82
84
  GET/POST /api/auth/roles - Manage roles (name, permissions: string[]).
83
85
  GET /api/auth/permissions - List permission strings and metadata.
@@ -48,10 +48,13 @@ passport_1.default.use(new passport_azure_ad_oauth2_1.Strategy({
48
48
  }, async (_at, _rt, params, _profile, done) => {
49
49
  try {
50
50
  const decoded = (0, jsonwebtoken_1.decode)(params.id_token);
51
- const microsoftId = decoded.oid;
52
- const email = decoded.preferred_username;
51
+ const microsoftId = decoded.oid || decoded.sub;
52
+ const email = decoded.preferred_username || decoded.upn || decoded.email;
53
53
  const name = decoded.name;
54
- let user = await user_model_1.default.findOne({ $or: [{ microsoftId }, { email }] });
54
+ const match = [{ microsoftId }];
55
+ if (email)
56
+ match.push({ email });
57
+ let user = await user_model_1.default.findOne({ $or: match });
55
58
  if (!user) {
56
59
  user = new user_model_1.default({ email, name, microsoftId, roles: [], status: 'active' });
57
60
  await user.save();
@@ -78,10 +81,13 @@ passport_1.default.use('azure_ad_oauth2_client', new passport_azure_ad_oauth2_1.
78
81
  }, async (_at, _rt, params, _profile, done) => {
79
82
  try {
80
83
  const decoded = (0, jsonwebtoken_1.decode)(params.id_token);
81
- const microsoftId = decoded.oid;
82
- const email = decoded.preferred_username;
84
+ const microsoftId = decoded.oid || decoded.sub;
85
+ const email = decoded.preferred_username || decoded.upn || decoded.email;
83
86
  const name = decoded.name;
84
- let client = await client_model_1.default.findOne({ $or: [{ microsoftId }, { email }] });
87
+ const match = [{ microsoftId }];
88
+ if (email)
89
+ match.push({ email });
90
+ let client = await client_model_1.default.findOne({ $or: match });
85
91
  if (!client) {
86
92
  client = new client_model_1.default({ email, name, microsoftId, roles: [] });
87
93
  await client.save();
@@ -77,7 +77,7 @@ let AuthController = class AuthController {
77
77
  path: '/',
78
78
  maxAge: (0, helper_1.getMillisecondsFromExpiry)(refreshTTL),
79
79
  });
80
- return res.status(200).json({ accessToken, refreshToken });
80
+ return res.status(200).json({ accessToken, refreshToken, profileIncomplete: !principal.email });
81
81
  }
82
82
  async respondWebOrMobile(req, res, principal) {
83
83
  const roleDocs = await role_model_1.default.find({ _id: { $in: principal.roles } })
@@ -116,6 +116,7 @@ let AuthController = class AuthController {
116
116
  const url = new URL(mobileRedirect);
117
117
  url.searchParams.set('accessToken', accessToken);
118
118
  url.searchParams.set('refreshToken', refreshToken);
119
+ url.searchParams.set('profileIncomplete', (!principal.email).toString());
119
120
  return res.redirect(302, url.toString());
120
121
  }
121
122
  const isProd = process.env.NODE_ENV === 'production';
@@ -126,7 +127,7 @@ let AuthController = class AuthController {
126
127
  path: '/',
127
128
  maxAge: (0, helper_1.getMillisecondsFromExpiry)(refreshTTL),
128
129
  });
129
- return res.status(200).json({ accessToken, refreshToken });
130
+ return res.status(200).json({ accessToken, refreshToken, profileIncomplete: !principal.email });
130
131
  }
131
132
  async registerClient(req, res) {
132
133
  try {
@@ -201,8 +202,10 @@ let AuthController = class AuthController {
201
202
  }
202
203
  microsoftCallback(req, res, next) {
203
204
  passport_config_1.default.authenticate('azure_ad_oauth2', { session: false }, async (err, user) => {
204
- if (err)
205
- return next(err);
205
+ if (err) {
206
+ console.error('Microsoft OAuth error:', err);
207
+ return res.status(500).json({ message: 'Microsoft auth failed', error: (err === null || err === void 0 ? void 0 : err.message) || err });
208
+ }
206
209
  if (!user)
207
210
  return res.status(400).json({ message: 'Microsoft authentication failed.' });
208
211
  return this.respondWebOrMobile(req, res, user);
@@ -226,13 +229,13 @@ let AuthController = class AuthController {
226
229
  console.error('ID token verify failed:', e.message || e);
227
230
  return res.status(401).json({ message: 'Invalid Microsoft ID token.' });
228
231
  }
229
- const email = ms.preferred_username || ms.email;
230
- const name = ms.name;
231
- if (!email) {
232
- return res.status(400).json({ message: 'Email claim missing in Microsoft ID token.' });
233
- }
234
232
  const microsoftId = ms.oid || ms.sub;
235
- let user = await user_model_1.default.findOne({ email });
233
+ const email = ms.preferred_username || ms.upn || ms.email;
234
+ const name = ms.name;
235
+ const match = [{ microsoftId }];
236
+ if (email)
237
+ match.push({ email });
238
+ let user = await user_model_1.default.findOne({ $or: match });
236
239
  if (!user) {
237
240
  user = new user_model_1.default({
238
241
  email,
@@ -365,7 +368,7 @@ let AuthController = class AuthController {
365
368
  path: '/',
366
369
  maxAge: (0, helper_1.getMillisecondsFromExpiry)(refreshTTL),
367
370
  });
368
- return res.status(200).json({ accessToken, refreshToken });
371
+ return res.status(200).json({ accessToken, refreshToken, profileIncomplete: !principal.email });
369
372
  }
370
373
  catch (err) {
371
374
  console.error('googleExchange error:', ((_h = err === null || err === void 0 ? void 0 : err.response) === null || _h === void 0 ? void 0 : _h.data) || err.message || err);
@@ -407,8 +410,10 @@ let AuthController = class AuthController {
407
410
  }
408
411
  microsoftClientCallback(req, res, next) {
409
412
  passport_config_1.default.authenticate('azure_ad_oauth2_client', { session: false }, async (err, client) => {
410
- if (err)
411
- return next(err);
413
+ if (err) {
414
+ console.error('Microsoft Client OAuth error:', err);
415
+ return res.status(500).json({ message: 'Microsoft auth failed', error: (err === null || err === void 0 ? void 0 : err.message) || err });
416
+ }
412
417
  if (!client)
413
418
  return res.status(400).json({ message: 'Microsoft authentication failed.' });
414
419
  return this.respondWebOrMobile(req, res, client);
@@ -5,9 +5,9 @@ declare const ClientSchema: mongoose.Schema<any, mongoose.Model<any, any, any, a
5
5
  createdAt: NativeDate;
6
6
  updatedAt: NativeDate;
7
7
  } & {
8
- email: string;
9
8
  roles: mongoose.Types.ObjectId[];
10
9
  createdAt: Date;
10
+ email?: string;
11
11
  name?: string;
12
12
  password?: string;
13
13
  microsoftId?: string;
@@ -20,9 +20,9 @@ declare const ClientSchema: mongoose.Schema<any, mongoose.Model<any, any, any, a
20
20
  createdAt: NativeDate;
21
21
  updatedAt: NativeDate;
22
22
  } & {
23
- email: string;
24
23
  roles: mongoose.Types.ObjectId[];
25
24
  createdAt: Date;
25
+ email?: string;
26
26
  name?: string;
27
27
  password?: string;
28
28
  microsoftId?: string;
@@ -35,9 +35,9 @@ declare const ClientSchema: mongoose.Schema<any, mongoose.Model<any, any, any, a
35
35
  createdAt: NativeDate;
36
36
  updatedAt: NativeDate;
37
37
  } & {
38
- email: string;
39
38
  roles: mongoose.Types.ObjectId[];
40
39
  createdAt: Date;
40
+ email?: string;
41
41
  name?: string;
42
42
  password?: string;
43
43
  microsoftId?: string;
@@ -9,8 +9,11 @@ const mongoose_paginate_v2_1 = __importDefault(require("mongoose-paginate-v2"));
9
9
  const ClientSchema = new mongoose_1.default.Schema({
10
10
  email: {
11
11
  type: String,
12
- required: true,
13
- unique: true
12
+ required: function () {
13
+ return !this.microsoftId && !this.googleId && !this.facebookId;
14
+ },
15
+ unique: true,
16
+ sparse: true
14
17
  },
15
18
  password: {
16
19
  type: String,
@@ -5,10 +5,10 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
5
5
  createdAt: NativeDate;
6
6
  updatedAt: NativeDate;
7
7
  } & {
8
- email: string;
9
8
  roles: mongoose.Types.ObjectId[];
10
9
  status: "pending" | "active" | "suspended" | "deactivated";
11
10
  failedLoginAttempts: number;
11
+ email?: string;
12
12
  name?: string;
13
13
  password?: string;
14
14
  jobTitle?: string;
@@ -24,10 +24,10 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
24
24
  createdAt: NativeDate;
25
25
  updatedAt: NativeDate;
26
26
  } & {
27
- email: string;
28
27
  roles: mongoose.Types.ObjectId[];
29
28
  status: "pending" | "active" | "suspended" | "deactivated";
30
29
  failedLoginAttempts: number;
30
+ email?: string;
31
31
  name?: string;
32
32
  password?: string;
33
33
  jobTitle?: string;
@@ -43,10 +43,10 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
43
43
  createdAt: NativeDate;
44
44
  updatedAt: NativeDate;
45
45
  } & {
46
- email: string;
47
46
  roles: mongoose.Types.ObjectId[];
48
47
  status: "pending" | "active" | "suspended" | "deactivated";
49
48
  failedLoginAttempts: number;
49
+ email?: string;
50
50
  name?: string;
51
51
  password?: string;
52
52
  jobTitle?: string;
@@ -9,8 +9,11 @@ const mongoose_paginate_v2_1 = __importDefault(require("mongoose-paginate-v2"));
9
9
  const UserSchema = new mongoose_1.default.Schema({
10
10
  email: {
11
11
  type: String,
12
- required: true,
13
- unique: true
12
+ required: function () {
13
+ return !this.microsoftId && !this.googleId && !this.facebookId;
14
+ },
15
+ unique: true,
16
+ sparse: true
14
17
  },
15
18
  password: {
16
19
  type: String,
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/CISCODE-MA/AuthKit.git"
9
9
  },
10
- "version": "1.1.3",
10
+ "version": "1.1.5",
11
11
  "description": "A login library with local login, Microsoft Entra ID authentication, password reset, and roles/permissions.",
12
12
  "main": "dist/index.js",
13
13
  "types": "dist/index.d.ts",