@ciscode/authentication-kit 1.1.4 → 1.1.6
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 +10 -9
- package/dist/controllers/users.controller.d.ts +1 -0
- package/dist/controllers/users.controller.js +28 -0
- 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 {
|
|
@@ -228,13 +229,13 @@ let AuthController = class AuthController {
|
|
|
228
229
|
console.error('ID token verify failed:', e.message || e);
|
|
229
230
|
return res.status(401).json({ message: 'Invalid Microsoft ID token.' });
|
|
230
231
|
}
|
|
231
|
-
const email = ms.preferred_username || ms.email;
|
|
232
|
-
const name = ms.name;
|
|
233
|
-
if (!email) {
|
|
234
|
-
return res.status(400).json({ message: 'Email claim missing in Microsoft ID token.' });
|
|
235
|
-
}
|
|
236
232
|
const microsoftId = ms.oid || ms.sub;
|
|
237
|
-
|
|
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 });
|
|
238
239
|
if (!user) {
|
|
239
240
|
user = new user_model_1.default({
|
|
240
241
|
email,
|
|
@@ -367,7 +368,7 @@ let AuthController = class AuthController {
|
|
|
367
368
|
path: '/',
|
|
368
369
|
maxAge: (0, helper_1.getMillisecondsFromExpiry)(refreshTTL),
|
|
369
370
|
});
|
|
370
|
-
return res.status(200).json({ accessToken, refreshToken });
|
|
371
|
+
return res.status(200).json({ accessToken, refreshToken, profileIncomplete: !principal.email });
|
|
371
372
|
}
|
|
372
373
|
catch (err) {
|
|
373
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);
|
|
@@ -5,4 +5,5 @@ export declare class UsersController {
|
|
|
5
5
|
deleteUser(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
6
6
|
createUserInvitation(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
7
7
|
getAllUsers(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
8
|
+
getMe(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
8
9
|
}
|
|
@@ -21,6 +21,7 @@ const user_model_1 = __importDefault(require("../models/user.model"));
|
|
|
21
21
|
const nodemailer_1 = __importDefault(require("nodemailer"));
|
|
22
22
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
23
23
|
const node_crypto_1 = require("node:crypto");
|
|
24
|
+
const authenticate_guard_1 = require("../middleware/authenticate.guard");
|
|
24
25
|
let UsersController = class UsersController {
|
|
25
26
|
async createUser(req, res) {
|
|
26
27
|
try {
|
|
@@ -204,6 +205,24 @@ Thank you!`
|
|
|
204
205
|
return res.status(500).json({ message: 'Server error', error: error.message });
|
|
205
206
|
}
|
|
206
207
|
}
|
|
208
|
+
async getMe(req, res) {
|
|
209
|
+
var _a;
|
|
210
|
+
try {
|
|
211
|
+
const userId = (_a = req.user) === null || _a === void 0 ? void 0 : _a.id;
|
|
212
|
+
if (!userId) {
|
|
213
|
+
return res.status(401).json({ message: 'Unauthorized' });
|
|
214
|
+
}
|
|
215
|
+
const user = await user_model_1.default.findById(userId).select('-password -resetPasswordToken -resetPasswordExpires -refreshToken -__v');
|
|
216
|
+
if (!user) {
|
|
217
|
+
return res.status(404).json({ message: 'User not found.' });
|
|
218
|
+
}
|
|
219
|
+
return res.status(200).json(user);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error('Error fetching current user:', error);
|
|
223
|
+
return res.status(500).json({ message: 'Server error', error: error.message });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
207
226
|
};
|
|
208
227
|
exports.UsersController = UsersController;
|
|
209
228
|
__decorate([
|
|
@@ -246,6 +265,15 @@ __decorate([
|
|
|
246
265
|
__metadata("design:paramtypes", [Object, Object]),
|
|
247
266
|
__metadata("design:returntype", Promise)
|
|
248
267
|
], UsersController.prototype, "getAllUsers", null);
|
|
268
|
+
__decorate([
|
|
269
|
+
(0, common_1.Get)('me'),
|
|
270
|
+
(0, common_1.UseGuards)(authenticate_guard_1.AuthenticateGuard),
|
|
271
|
+
__param(0, (0, common_1.Req)()),
|
|
272
|
+
__param(1, (0, common_1.Res)()),
|
|
273
|
+
__metadata("design:type", Function),
|
|
274
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
275
|
+
__metadata("design:returntype", Promise)
|
|
276
|
+
], UsersController.prototype, "getMe", null);
|
|
249
277
|
exports.UsersController = UsersController = __decorate([
|
|
250
278
|
(0, common_1.Controller)('api/users')
|
|
251
279
|
], UsersController);
|
|
@@ -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.6",
|
|
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",
|