@ciscode/authentication-kit 1.1.2 → 1.1.4
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 +6 -10
- package/dist/config/passport.config.js +3 -22
- package/dist/controllers/auth.controller.js +11 -29
- package/dist/controllers/roles.controller.js +6 -10
- package/dist/controllers/users.controller.js +6 -14
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -3
- package/dist/middleware/permission.guard.js +4 -4
- package/dist/models/role.model.d.ts +0 -3
- package/dist/models/role.model.js +1 -3
- package/dist/models/user.model.d.ts +6 -3
- package/dist/models/user.model.js +4 -3
- package/package.json +3 -3
- package/dist/middleware/tenant.guard.d.ts +0 -4
- package/dist/middleware/tenant.guard.js +0 -39
package/README.md
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
Auth Service (NestJS, JWT,
|
|
1
|
+
Auth Service (NestJS, JWT, RBAC)
|
|
2
2
|
Internal package - private to the company.
|
|
3
3
|
This package is not published on npmjs. Install it only from the company Azure Artifacts feed using a project or user-level .npmrc.
|
|
4
4
|
|
|
5
5
|
Authentication and authorization module for NestJS apps.
|
|
6
|
-
Provides local email/password auth with lockout, JWT access tokens and refresh,
|
|
6
|
+
Provides local email/password auth with lockout, JWT access tokens and refresh, RBAC, and optional OAuth (Microsoft Entra, Google, Facebook).
|
|
7
7
|
|
|
8
8
|
Features
|
|
9
9
|
Local auth (email/password) with account lockout policy.
|
|
10
10
|
JWT access tokens (Bearer) and refresh endpoint (cookie or body).
|
|
11
|
-
Multi-tenant scope on requests.
|
|
12
11
|
RBAC (roles -> permission strings).
|
|
13
12
|
Microsoft Entra (Azure AD), Google, Facebook OAuth (optional).
|
|
14
13
|
MongoDB/Mongoose models.
|
|
@@ -47,7 +46,6 @@ MAX_FAILED_LOGIN_ATTEMPTS=5
|
|
|
47
46
|
ACCOUNT_LOCK_TIME_MINUTES=15
|
|
48
47
|
|
|
49
48
|
# (Optional) Microsoft Entra ID (Azure AD)
|
|
50
|
-
MICROSOFT_TENANT_ID=common
|
|
51
49
|
MICROSOFT_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
52
50
|
MICROSOFT_CLIENT_SECRET=your-secret
|
|
53
51
|
MICROSOFT_CALLBACK_URL=${BASE_URL}/api/auth/microsoft/callback
|
|
@@ -77,11 +75,11 @@ POST /api/auth/request-password-reset - Sends a reset token (e.g., by email).
|
|
|
77
75
|
POST /api/auth/reset-password - Consumes the reset token and sets a new password.
|
|
78
76
|
GET /api/auth/microsoft - GET /api/auth/microsoft/callback - Optional Microsoft Entra OAuth; issues first-party tokens.
|
|
79
77
|
Users
|
|
80
|
-
GET /api/users - List users (
|
|
78
|
+
GET /api/users - List users (paginated).
|
|
81
79
|
POST /api/users - Create a user.
|
|
82
80
|
Additional CRUD endpoints as exposed by controllers.
|
|
83
81
|
Roles and Permissions
|
|
84
|
-
GET/POST /api/auth/roles - Manage roles (name,
|
|
82
|
+
GET/POST /api/auth/roles - Manage roles (name, permissions: string[]).
|
|
85
83
|
GET /api/auth/permissions - List permission strings and metadata.
|
|
86
84
|
|
|
87
85
|
Protecting your own routes (host app)
|
|
@@ -94,18 +92,16 @@ getReports() {
|
|
|
94
92
|
return { ok: true };
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
Tenant scope comes from the JWT payload (e.g., tenantId) and is used inside controllers/guards to filter queries.
|
|
98
|
-
|
|
99
95
|
Quick start (smoke tests)
|
|
100
96
|
Start your host app, then create a user and log in:
|
|
101
97
|
|
|
102
98
|
curl -X POST http://localhost:3000/api/users \
|
|
103
99
|
-H 'Content-Type: application/json' \
|
|
104
|
-
-d '{"email":"a@b.com","password":"Secret123!","
|
|
100
|
+
-d '{"email":"a@b.com","password":"Secret123!","name":"Alice"}'
|
|
105
101
|
|
|
106
102
|
curl -X POST http://localhost:3000/api/auth/login \
|
|
107
103
|
-H 'Content-Type: application/json' \
|
|
108
|
-
-d '{"email":"a@b.com","password":"Secret123!"
|
|
104
|
+
-d '{"email":"a@b.com","password":"Secret123!"}'
|
|
109
105
|
# => { "accessToken": "...", "refreshToken": "..." }
|
|
110
106
|
|
|
111
107
|
Call a protected route
|
|
@@ -16,15 +16,11 @@ require("dotenv/config");
|
|
|
16
16
|
const MAX_FAILED = parseInt(process.env.MAX_FAILED_LOGIN_ATTEMPTS || '', 10) || 3;
|
|
17
17
|
const LOCK_TIME_MIN = parseInt(process.env.ACCOUNT_LOCK_TIME_MINUTES || '', 10) || 15;
|
|
18
18
|
const LOCK_TIME_MS = LOCK_TIME_MIN * 60 * 1000;
|
|
19
|
-
const DEFAULT_TENANT_ID = process.env.DEFAULT_TENANT_ID || 'social-staff';
|
|
20
19
|
passport_1.default.use(new passport_local_1.Strategy({ usernameField: 'email', passwordField: 'password', passReqToCallback: true }, async (req, email, password, done) => {
|
|
21
20
|
try {
|
|
22
|
-
const
|
|
23
|
-
if (req.body.tenantId)
|
|
24
|
-
query.tenantId = String(req.body.tenantId).trim();
|
|
25
|
-
const user = await user_model_1.default.findOne(query);
|
|
21
|
+
const user = await user_model_1.default.findOne({ email });
|
|
26
22
|
if (!user)
|
|
27
|
-
return done(null, false, { message: 'Incorrect email
|
|
23
|
+
return done(null, false, { message: 'Incorrect email.' });
|
|
28
24
|
if (user.lockUntil && user.lockUntil > Date.now()) {
|
|
29
25
|
return done(null, false, { message: `Account locked until ${new Date(user.lockUntil).toLocaleString()}.` });
|
|
30
26
|
}
|
|
@@ -55,10 +51,9 @@ passport_1.default.use(new passport_azure_ad_oauth2_1.Strategy({
|
|
|
55
51
|
const microsoftId = decoded.oid;
|
|
56
52
|
const email = decoded.preferred_username;
|
|
57
53
|
const name = decoded.name;
|
|
58
|
-
const tenantId = decoded.tid;
|
|
59
54
|
let user = await user_model_1.default.findOne({ $or: [{ microsoftId }, { email }] });
|
|
60
55
|
if (!user) {
|
|
61
|
-
user = new user_model_1.default({ email, name,
|
|
56
|
+
user = new user_model_1.default({ email, name, microsoftId, roles: [], status: 'active' });
|
|
62
57
|
await user.save();
|
|
63
58
|
}
|
|
64
59
|
else {
|
|
@@ -67,10 +62,6 @@ passport_1.default.use(new passport_azure_ad_oauth2_1.Strategy({
|
|
|
67
62
|
user.microsoftId = microsoftId;
|
|
68
63
|
changed = true;
|
|
69
64
|
}
|
|
70
|
-
if (!user.tenantId) {
|
|
71
|
-
user.tenantId = tenantId;
|
|
72
|
-
changed = true;
|
|
73
|
-
}
|
|
74
65
|
if (changed)
|
|
75
66
|
await user.save();
|
|
76
67
|
}
|
|
@@ -121,7 +112,6 @@ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET && process.
|
|
|
121
112
|
user = new user_model_1.default({
|
|
122
113
|
email,
|
|
123
114
|
name: profile.displayName,
|
|
124
|
-
tenantId: DEFAULT_TENANT_ID,
|
|
125
115
|
googleId: profile.id,
|
|
126
116
|
roles: [],
|
|
127
117
|
status: 'active'
|
|
@@ -134,10 +124,6 @@ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET && process.
|
|
|
134
124
|
user.googleId = profile.id;
|
|
135
125
|
changed = true;
|
|
136
126
|
}
|
|
137
|
-
if (!user.tenantId) {
|
|
138
|
-
user.tenantId = DEFAULT_TENANT_ID;
|
|
139
|
-
changed = true;
|
|
140
|
-
}
|
|
141
127
|
if (changed)
|
|
142
128
|
await user.save();
|
|
143
129
|
}
|
|
@@ -197,7 +183,6 @@ if (process.env.FB_CLIENT_ID && process.env.FB_CLIENT_SECRET && process.env.FB_C
|
|
|
197
183
|
user = new user_model_1.default({
|
|
198
184
|
email,
|
|
199
185
|
name: profile.displayName,
|
|
200
|
-
tenantId: DEFAULT_TENANT_ID,
|
|
201
186
|
facebookId: profile.id,
|
|
202
187
|
roles: [],
|
|
203
188
|
status: 'active'
|
|
@@ -210,10 +195,6 @@ if (process.env.FB_CLIENT_ID && process.env.FB_CLIENT_SECRET && process.env.FB_C
|
|
|
210
195
|
user.facebookId = profile.id;
|
|
211
196
|
changed = true;
|
|
212
197
|
}
|
|
213
|
-
if (!user.tenantId) {
|
|
214
|
-
user.tenantId = DEFAULT_TENANT_ID;
|
|
215
|
-
changed = true;
|
|
216
|
-
}
|
|
217
198
|
if (changed)
|
|
218
199
|
await user.save();
|
|
219
200
|
}
|
|
@@ -26,7 +26,6 @@ const user_model_1 = __importDefault(require("../models/user.model"));
|
|
|
26
26
|
const client_model_1 = __importDefault(require("../models/client.model"));
|
|
27
27
|
const role_model_1 = __importDefault(require("../models/role.model"));
|
|
28
28
|
const helper_1 = require("../utils/helper");
|
|
29
|
-
const TENANT_ID = process.env.MICROSOFT_TENANT_ID || 'common';
|
|
30
29
|
const MSAL_MOBILE_CLIENT_ID = process.env.MSAL_MOBILE_CLIENT_ID;
|
|
31
30
|
const resolveJwtExpiry = (value, fallback) => (value || fallback);
|
|
32
31
|
const msJwks = (0, jwks_rsa_1.default)({
|
|
@@ -58,7 +57,6 @@ let AuthController = class AuthController {
|
|
|
58
57
|
const payload = {
|
|
59
58
|
id: principal._id,
|
|
60
59
|
email: principal.email,
|
|
61
|
-
tenantId: principal.tenantId,
|
|
62
60
|
roles,
|
|
63
61
|
permissions,
|
|
64
62
|
};
|
|
@@ -92,7 +90,6 @@ let AuthController = class AuthController {
|
|
|
92
90
|
const payload = {
|
|
93
91
|
id: principal._id,
|
|
94
92
|
email: principal.email,
|
|
95
|
-
tenantId: principal.tenantId,
|
|
96
93
|
roles,
|
|
97
94
|
permissions,
|
|
98
95
|
};
|
|
@@ -204,8 +201,10 @@ let AuthController = class AuthController {
|
|
|
204
201
|
}
|
|
205
202
|
microsoftCallback(req, res, next) {
|
|
206
203
|
passport_config_1.default.authenticate('azure_ad_oauth2', { session: false }, async (err, user) => {
|
|
207
|
-
if (err)
|
|
208
|
-
|
|
204
|
+
if (err) {
|
|
205
|
+
console.error('Microsoft OAuth error:', err);
|
|
206
|
+
return res.status(500).json({ message: 'Microsoft auth failed', error: (err === null || err === void 0 ? void 0 : err.message) || err });
|
|
207
|
+
}
|
|
209
208
|
if (!user)
|
|
210
209
|
return res.status(400).json({ message: 'Microsoft authentication failed.' });
|
|
211
210
|
return this.respondWebOrMobile(req, res, user);
|
|
@@ -231,10 +230,6 @@ let AuthController = class AuthController {
|
|
|
231
230
|
}
|
|
232
231
|
const email = ms.preferred_username || ms.email;
|
|
233
232
|
const name = ms.name;
|
|
234
|
-
const tid = ms.tid;
|
|
235
|
-
if (TENANT_ID && TENANT_ID !== 'common' && tid && tid !== TENANT_ID) {
|
|
236
|
-
return res.status(401).json({ message: 'Tenant mismatch.' });
|
|
237
|
-
}
|
|
238
233
|
if (!email) {
|
|
239
234
|
return res.status(400).json({ message: 'Email claim missing in Microsoft ID token.' });
|
|
240
235
|
}
|
|
@@ -244,7 +239,6 @@ let AuthController = class AuthController {
|
|
|
244
239
|
user = new user_model_1.default({
|
|
245
240
|
email,
|
|
246
241
|
name,
|
|
247
|
-
tenantId: tid || TENANT_ID,
|
|
248
242
|
microsoftId,
|
|
249
243
|
roles: [],
|
|
250
244
|
status: 'active',
|
|
@@ -257,10 +251,6 @@ let AuthController = class AuthController {
|
|
|
257
251
|
user.microsoftId = microsoftId;
|
|
258
252
|
changed = true;
|
|
259
253
|
}
|
|
260
|
-
if (!user.tenantId) {
|
|
261
|
-
user.tenantId = tid || TENANT_ID;
|
|
262
|
-
changed = true;
|
|
263
|
-
}
|
|
264
254
|
if (changed)
|
|
265
255
|
await user.save();
|
|
266
256
|
}
|
|
@@ -302,17 +292,10 @@ let AuthController = class AuthController {
|
|
|
302
292
|
async googleExchange(req, res) {
|
|
303
293
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
304
294
|
try {
|
|
305
|
-
let { code, idToken, type = 'user'
|
|
295
|
+
let { code, idToken, type = 'user' } = req.body || {};
|
|
306
296
|
if (!['user', 'client'].includes(type)) {
|
|
307
297
|
return res.status(400).json({ message: 'invalid type; must be "user" or "client"' });
|
|
308
298
|
}
|
|
309
|
-
let resolvedTenantId;
|
|
310
|
-
if (type === 'user') {
|
|
311
|
-
resolvedTenantId = tenantId || process.env.DEFAULT_TENANT_ID;
|
|
312
|
-
if (!resolvedTenantId) {
|
|
313
|
-
return res.status(400).json({ message: 'tenantId is required for user login.' });
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
299
|
let email;
|
|
317
300
|
let name;
|
|
318
301
|
let googleId;
|
|
@@ -352,14 +335,12 @@ let AuthController = class AuthController {
|
|
|
352
335
|
let principal = await Model.findOne({ $or: [{ email }, { googleId }] });
|
|
353
336
|
if (!principal) {
|
|
354
337
|
principal = new Model(type === 'user'
|
|
355
|
-
? { email, name, googleId,
|
|
338
|
+
? { email, name, googleId, roles: [], status: 'active' }
|
|
356
339
|
: { email, name, googleId, roles: [] });
|
|
357
340
|
await principal.save();
|
|
358
341
|
}
|
|
359
342
|
else if (!principal.googleId) {
|
|
360
343
|
principal.googleId = googleId;
|
|
361
|
-
if (type === 'user' && !principal.tenantId)
|
|
362
|
-
principal.tenantId = resolvedTenantId;
|
|
363
344
|
await principal.save();
|
|
364
345
|
}
|
|
365
346
|
const roleDocs = await role_model_1.default.find({ _id: { $in: principal.roles } })
|
|
@@ -368,7 +349,7 @@ let AuthController = class AuthController {
|
|
|
368
349
|
const permissions = Array.from(new Set(roleDocs.flatMap((r) => r.permissions)));
|
|
369
350
|
const accessTTL = resolveJwtExpiry(process.env.JWT_ACCESS_TOKEN_EXPIRES_IN, '15m');
|
|
370
351
|
const refreshTTL = resolveJwtExpiry(process.env.JWT_REFRESH_TOKEN_EXPIRES_IN, '7d');
|
|
371
|
-
const payload = { id: principal._id, email: principal.email,
|
|
352
|
+
const payload = { id: principal._id, email: principal.email, roles, permissions };
|
|
372
353
|
const accessToken = jsonwebtoken_1.default.sign(payload, process.env.JWT_SECRET, { expiresIn: accessTTL });
|
|
373
354
|
const refreshToken = jsonwebtoken_1.default.sign({ id: principal._id }, process.env.JWT_REFRESH_SECRET, { expiresIn: refreshTTL });
|
|
374
355
|
principal.refreshToken = refreshToken;
|
|
@@ -428,8 +409,10 @@ let AuthController = class AuthController {
|
|
|
428
409
|
}
|
|
429
410
|
microsoftClientCallback(req, res, next) {
|
|
430
411
|
passport_config_1.default.authenticate('azure_ad_oauth2_client', { session: false }, async (err, client) => {
|
|
431
|
-
if (err)
|
|
432
|
-
|
|
412
|
+
if (err) {
|
|
413
|
+
console.error('Microsoft Client OAuth error:', err);
|
|
414
|
+
return res.status(500).json({ message: 'Microsoft auth failed', error: (err === null || err === void 0 ? void 0 : err.message) || err });
|
|
415
|
+
}
|
|
433
416
|
if (!client)
|
|
434
417
|
return res.status(400).json({ message: 'Microsoft authentication failed.' });
|
|
435
418
|
return this.respondWebOrMobile(req, res, client);
|
|
@@ -468,7 +451,6 @@ let AuthController = class AuthController {
|
|
|
468
451
|
const payload = {
|
|
469
452
|
id: principal._id,
|
|
470
453
|
email: principal.email,
|
|
471
|
-
tenantId: principal.tenantId,
|
|
472
454
|
roles,
|
|
473
455
|
permissions,
|
|
474
456
|
};
|
|
@@ -21,11 +21,11 @@ const role_model_1 = __importDefault(require("../models/role.model"));
|
|
|
21
21
|
let RolesController = class RolesController {
|
|
22
22
|
async createRole(req, res) {
|
|
23
23
|
try {
|
|
24
|
-
const {
|
|
25
|
-
if (!
|
|
26
|
-
return res.status(400).json({ message: '
|
|
24
|
+
const { name, description, permissions } = req.body;
|
|
25
|
+
if (!name) {
|
|
26
|
+
return res.status(400).json({ message: 'Role name is required.' });
|
|
27
27
|
}
|
|
28
|
-
const newRole = new role_model_1.default({
|
|
28
|
+
const newRole = new role_model_1.default({ name, description, permissions });
|
|
29
29
|
await newRole.save();
|
|
30
30
|
return res.status(201).json(newRole);
|
|
31
31
|
}
|
|
@@ -36,11 +36,7 @@ let RolesController = class RolesController {
|
|
|
36
36
|
}
|
|
37
37
|
async getRoles(req, res) {
|
|
38
38
|
try {
|
|
39
|
-
const
|
|
40
|
-
if (!tenantId) {
|
|
41
|
-
return res.status(400).json({ message: 'tenantId is required in the URL.' });
|
|
42
|
-
}
|
|
43
|
-
const roles = await role_model_1.default.paginate({ tenantId }, { page: 1, limit: 100 });
|
|
39
|
+
const roles = await role_model_1.default.paginate({}, { page: 1, limit: 100 });
|
|
44
40
|
return res.status(200).json(roles);
|
|
45
41
|
}
|
|
46
42
|
catch (error) {
|
|
@@ -85,7 +81,7 @@ __decorate([
|
|
|
85
81
|
__metadata("design:returntype", Promise)
|
|
86
82
|
], RolesController.prototype, "createRole", null);
|
|
87
83
|
__decorate([
|
|
88
|
-
(0, common_1.Get)(
|
|
84
|
+
(0, common_1.Get)(),
|
|
89
85
|
__param(0, (0, common_1.Req)()),
|
|
90
86
|
__param(1, (0, common_1.Res)()),
|
|
91
87
|
__metadata("design:type", Function),
|
|
@@ -24,17 +24,14 @@ const node_crypto_1 = require("node:crypto");
|
|
|
24
24
|
let UsersController = class UsersController {
|
|
25
25
|
async createUser(req, res) {
|
|
26
26
|
try {
|
|
27
|
-
const { email, password, name,
|
|
27
|
+
const { email, password, name, microsoftId, roles, jobTitle, company } = req.body;
|
|
28
28
|
if (!email) {
|
|
29
29
|
return res.status(400).json({ message: 'Email is required.' });
|
|
30
30
|
}
|
|
31
|
-
if (!tenantId) {
|
|
32
|
-
return res.status(400).json({ message: 'TenantId is required.' });
|
|
33
|
-
}
|
|
34
31
|
if (!microsoftId && !password) {
|
|
35
32
|
return res.status(400).json({ message: 'Password is required for local login.' });
|
|
36
33
|
}
|
|
37
|
-
const existingUser = await user_model_1.default.findOne({ email
|
|
34
|
+
const existingUser = await user_model_1.default.findOne({ email });
|
|
38
35
|
if (existingUser) {
|
|
39
36
|
return res.status(400).json({ message: 'User with this email already exists.' });
|
|
40
37
|
}
|
|
@@ -48,9 +45,10 @@ let UsersController = class UsersController {
|
|
|
48
45
|
email,
|
|
49
46
|
password: hashedPassword,
|
|
50
47
|
name,
|
|
51
|
-
tenantId,
|
|
52
48
|
microsoftId,
|
|
53
49
|
roles,
|
|
50
|
+
jobTitle,
|
|
51
|
+
company,
|
|
54
52
|
status,
|
|
55
53
|
resetPasswordToken: confirmationToken,
|
|
56
54
|
resetPasswordExpires: tokenExpiration
|
|
@@ -124,14 +122,11 @@ Thank you.`
|
|
|
124
122
|
}
|
|
125
123
|
async createUserInvitation(req, res) {
|
|
126
124
|
try {
|
|
127
|
-
const { email,
|
|
125
|
+
const { email, name } = req.body;
|
|
128
126
|
if (!email) {
|
|
129
127
|
return res.status(400).json({ message: 'Email is required.' });
|
|
130
128
|
}
|
|
131
|
-
|
|
132
|
-
return res.status(400).json({ message: 'TenantId is required.' });
|
|
133
|
-
}
|
|
134
|
-
const existingUser = await user_model_1.default.findOne({ email, tenantId });
|
|
129
|
+
const existingUser = await user_model_1.default.findOne({ email });
|
|
135
130
|
if (existingUser) {
|
|
136
131
|
return res.status(400).json({ message: 'User with this email already exists.' });
|
|
137
132
|
}
|
|
@@ -139,7 +134,6 @@ Thank you.`
|
|
|
139
134
|
const tokenExpiration = Date.now() + 24 * 60 * 60 * 1000;
|
|
140
135
|
const newUser = new user_model_1.default({
|
|
141
136
|
email,
|
|
142
|
-
tenantId,
|
|
143
137
|
name,
|
|
144
138
|
resetPasswordToken: token,
|
|
145
139
|
resetPasswordExpires: tokenExpiration
|
|
@@ -180,8 +174,6 @@ Thank you!`
|
|
|
180
174
|
async getAllUsers(req, res) {
|
|
181
175
|
try {
|
|
182
176
|
const filter = {};
|
|
183
|
-
if (req.query.tenantId)
|
|
184
|
-
filter.tenantId = req.query.tenantId;
|
|
185
177
|
if (req.query.email)
|
|
186
178
|
filter.email = req.query.email;
|
|
187
179
|
const page = Math.max(parseInt(req.query.page, 10) || 1, 1);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,4 @@ import 'reflect-metadata';
|
|
|
2
2
|
export { AuthKitModule } from './auth-kit.module';
|
|
3
3
|
export { AuthenticateGuard } from './middleware/authenticate.guard';
|
|
4
4
|
export { AuthGuard } from './middleware/auth.guard';
|
|
5
|
-
export { TenantGuard } from './middleware/tenant.guard';
|
|
6
5
|
export { hasPermission } from './middleware/permission.guard';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.hasPermission = exports.
|
|
3
|
+
exports.hasPermission = exports.AuthGuard = exports.AuthenticateGuard = exports.AuthKitModule = void 0;
|
|
4
4
|
require("reflect-metadata");
|
|
5
5
|
var auth_kit_module_1 = require("./auth-kit.module");
|
|
6
6
|
Object.defineProperty(exports, "AuthKitModule", { enumerable: true, get: function () { return auth_kit_module_1.AuthKitModule; } });
|
|
@@ -8,7 +8,5 @@ var authenticate_guard_1 = require("./middleware/authenticate.guard");
|
|
|
8
8
|
Object.defineProperty(exports, "AuthenticateGuard", { enumerable: true, get: function () { return authenticate_guard_1.AuthenticateGuard; } });
|
|
9
9
|
var auth_guard_1 = require("./middleware/auth.guard");
|
|
10
10
|
Object.defineProperty(exports, "AuthGuard", { enumerable: true, get: function () { return auth_guard_1.AuthGuard; } });
|
|
11
|
-
var tenant_guard_1 = require("./middleware/tenant.guard");
|
|
12
|
-
Object.defineProperty(exports, "TenantGuard", { enumerable: true, get: function () { return tenant_guard_1.TenantGuard; } });
|
|
13
11
|
var permission_guard_1 = require("./middleware/permission.guard");
|
|
14
12
|
Object.defineProperty(exports, "hasPermission", { enumerable: true, get: function () { return permission_guard_1.hasPermission; } });
|
|
@@ -18,18 +18,18 @@ const hasPermission = (requiredPermission) => {
|
|
|
18
18
|
const req = context.switchToHttp().getRequest();
|
|
19
19
|
const res = context.switchToHttp().getResponse();
|
|
20
20
|
try {
|
|
21
|
-
const {
|
|
21
|
+
const { roleIds, roles, permissions } = req.user || {};
|
|
22
22
|
const tokenPermissions = Array.isArray(permissions) ? permissions : [];
|
|
23
23
|
if (tokenPermissions.includes(requiredPermission)) {
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
let resolvedPermissions = [];
|
|
27
27
|
if (Array.isArray(roleIds) && roleIds.length > 0) {
|
|
28
|
-
const roleDocs = await role_model_1.default.find({ _id: { $in: roleIds }
|
|
28
|
+
const roleDocs = await role_model_1.default.find({ _id: { $in: roleIds } });
|
|
29
29
|
resolvedPermissions = roleDocs.flatMap((role) => role.permissions);
|
|
30
30
|
}
|
|
31
|
-
else if (Array.isArray(roles) && roles.length > 0
|
|
32
|
-
const roleDocs = await role_model_1.default.find({ name: { $in: roles }
|
|
31
|
+
else if (Array.isArray(roles) && roles.length > 0) {
|
|
32
|
+
const roleDocs = await role_model_1.default.find({ name: { $in: roles } });
|
|
33
33
|
resolvedPermissions = roleDocs.flatMap((role) => role.permissions);
|
|
34
34
|
}
|
|
35
35
|
if (resolvedPermissions.includes(requiredPermission)) {
|
|
@@ -6,7 +6,6 @@ declare const RoleSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
6
6
|
updatedAt: NativeDate;
|
|
7
7
|
} & {
|
|
8
8
|
name: string;
|
|
9
|
-
tenantId: string;
|
|
10
9
|
permissions: string[];
|
|
11
10
|
description?: string;
|
|
12
11
|
}, mongoose.Document<unknown, {}, mongoose.FlatRecord<{
|
|
@@ -14,7 +13,6 @@ declare const RoleSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
14
13
|
updatedAt: NativeDate;
|
|
15
14
|
} & {
|
|
16
15
|
name: string;
|
|
17
|
-
tenantId: string;
|
|
18
16
|
permissions: string[];
|
|
19
17
|
description?: string;
|
|
20
18
|
}>> & mongoose.FlatRecord<{
|
|
@@ -22,7 +20,6 @@ declare const RoleSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
22
20
|
updatedAt: NativeDate;
|
|
23
21
|
} & {
|
|
24
22
|
name: string;
|
|
25
|
-
tenantId: string;
|
|
26
23
|
permissions: string[];
|
|
27
24
|
description?: string;
|
|
28
25
|
}> & {
|
|
@@ -7,13 +7,11 @@ exports.RoleSchema = void 0;
|
|
|
7
7
|
const mongoose_1 = __importDefault(require("mongoose"));
|
|
8
8
|
const mongoose_paginate_v2_1 = __importDefault(require("mongoose-paginate-v2"));
|
|
9
9
|
const RoleSchema = new mongoose_1.default.Schema({
|
|
10
|
-
|
|
11
|
-
name: { type: String, required: true },
|
|
10
|
+
name: { type: String, required: true, unique: true },
|
|
12
11
|
description: { type: String },
|
|
13
12
|
permissions: [{ type: String }]
|
|
14
13
|
}, { timestamps: true });
|
|
15
14
|
exports.RoleSchema = RoleSchema;
|
|
16
15
|
RoleSchema.plugin(mongoose_paginate_v2_1.default);
|
|
17
|
-
RoleSchema.index({ tenantId: 1, name: 1 }, { unique: true });
|
|
18
16
|
const Role = mongoose_1.default.models.Role || mongoose_1.default.model('Role', RoleSchema);
|
|
19
17
|
exports.default = Role;
|
|
@@ -6,12 +6,13 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
6
6
|
updatedAt: NativeDate;
|
|
7
7
|
} & {
|
|
8
8
|
email: string;
|
|
9
|
-
tenantId: string;
|
|
10
9
|
roles: mongoose.Types.ObjectId[];
|
|
11
10
|
status: "pending" | "active" | "suspended" | "deactivated";
|
|
12
11
|
failedLoginAttempts: number;
|
|
13
12
|
name?: string;
|
|
14
13
|
password?: string;
|
|
14
|
+
jobTitle?: string;
|
|
15
|
+
company?: string;
|
|
15
16
|
microsoftId?: string;
|
|
16
17
|
googleId?: string;
|
|
17
18
|
facebookId?: string;
|
|
@@ -24,12 +25,13 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
24
25
|
updatedAt: NativeDate;
|
|
25
26
|
} & {
|
|
26
27
|
email: string;
|
|
27
|
-
tenantId: string;
|
|
28
28
|
roles: mongoose.Types.ObjectId[];
|
|
29
29
|
status: "pending" | "active" | "suspended" | "deactivated";
|
|
30
30
|
failedLoginAttempts: number;
|
|
31
31
|
name?: string;
|
|
32
32
|
password?: string;
|
|
33
|
+
jobTitle?: string;
|
|
34
|
+
company?: string;
|
|
33
35
|
microsoftId?: string;
|
|
34
36
|
googleId?: string;
|
|
35
37
|
facebookId?: string;
|
|
@@ -42,12 +44,13 @@ declare const UserSchema: mongoose.Schema<any, mongoose.Model<any, any, any, any
|
|
|
42
44
|
updatedAt: NativeDate;
|
|
43
45
|
} & {
|
|
44
46
|
email: string;
|
|
45
|
-
tenantId: string;
|
|
46
47
|
roles: mongoose.Types.ObjectId[];
|
|
47
48
|
status: "pending" | "active" | "suspended" | "deactivated";
|
|
48
49
|
failedLoginAttempts: number;
|
|
49
50
|
name?: string;
|
|
50
51
|
password?: string;
|
|
52
|
+
jobTitle?: string;
|
|
53
|
+
company?: string;
|
|
51
54
|
microsoftId?: string;
|
|
52
55
|
googleId?: string;
|
|
53
56
|
facebookId?: string;
|
|
@@ -9,7 +9,8 @@ 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
|
|
12
|
+
required: true,
|
|
13
|
+
unique: true
|
|
13
14
|
},
|
|
14
15
|
password: {
|
|
15
16
|
type: String,
|
|
@@ -18,7 +19,8 @@ const UserSchema = new mongoose_1.default.Schema({
|
|
|
18
19
|
}
|
|
19
20
|
},
|
|
20
21
|
name: { type: String },
|
|
21
|
-
|
|
22
|
+
jobTitle: { type: String },
|
|
23
|
+
company: { type: String },
|
|
22
24
|
microsoftId: { type: String, index: true },
|
|
23
25
|
googleId: { type: String, index: true },
|
|
24
26
|
facebookId: { type: String, index: true },
|
|
@@ -36,6 +38,5 @@ const UserSchema = new mongoose_1.default.Schema({
|
|
|
36
38
|
}, { timestamps: true });
|
|
37
39
|
exports.UserSchema = UserSchema;
|
|
38
40
|
UserSchema.plugin(mongoose_paginate_v2_1.default);
|
|
39
|
-
UserSchema.index({ tenantId: 1, email: 1 }, { unique: true });
|
|
40
41
|
const User = mongoose_1.default.models.User || mongoose_1.default.model('User', UserSchema);
|
|
41
42
|
exports.default = User;
|
package/package.json
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "git+https://github.com/CISCODE-MA/AuthKit.git"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.1.
|
|
11
|
-
"description": "A login library with local login, Microsoft Entra ID authentication, password reset, and roles/permissions
|
|
10
|
+
"version": "1.1.4",
|
|
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",
|
|
14
14
|
"files": [
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"password-reset",
|
|
31
31
|
"roles",
|
|
32
32
|
"permissions",
|
|
33
|
-
"
|
|
33
|
+
"users"
|
|
34
34
|
],
|
|
35
35
|
"author": "Ciscode",
|
|
36
36
|
"license": "MIT",
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
-
};
|
|
8
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.TenantGuard = void 0;
|
|
13
|
-
const common_1 = require("@nestjs/common");
|
|
14
|
-
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
15
|
-
let TenantGuard = class TenantGuard {
|
|
16
|
-
canActivate(context) {
|
|
17
|
-
var _a, _b;
|
|
18
|
-
const req = context.switchToHttp().getRequest();
|
|
19
|
-
const res = context.switchToHttp().getResponse();
|
|
20
|
-
const token = (_b = (_a = req.headers) === null || _a === void 0 ? void 0 : _a.authorization) === null || _b === void 0 ? void 0 : _b.split(' ')[1];
|
|
21
|
-
if (!token) {
|
|
22
|
-
res.status(401).json({ error: 'Unauthorized' });
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
const decoded = jsonwebtoken_1.default.verify(token, process.env.JWT_SECRET);
|
|
27
|
-
req.tenantId = decoded.tenantId;
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
res.status(403).json({ error: 'Invalid token' });
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
exports.TenantGuard = TenantGuard;
|
|
37
|
-
exports.TenantGuard = TenantGuard = __decorate([
|
|
38
|
-
(0, common_1.Injectable)()
|
|
39
|
-
], TenantGuard);
|