@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 +2 -0
- package/dist/config/passport.config.js +12 -6
- package/dist/controllers/auth.controller.js +18 -13
- package/dist/models/client.model.d.ts +3 -3
- package/dist/models/client.model.js +5 -2
- package/dist/models/user.model.d.ts +3 -3
- package/dist/models/user.model.js +5 -2
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
13
|
-
|
|
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:
|
|
13
|
-
|
|
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.
|
|
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",
|