@ciscode/authentication-kit 1.1.2 → 1.1.3
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 +3 -25
- 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
|
};
|
|
@@ -231,10 +228,6 @@ let AuthController = class AuthController {
|
|
|
231
228
|
}
|
|
232
229
|
const email = ms.preferred_username || ms.email;
|
|
233
230
|
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
231
|
if (!email) {
|
|
239
232
|
return res.status(400).json({ message: 'Email claim missing in Microsoft ID token.' });
|
|
240
233
|
}
|
|
@@ -244,7 +237,6 @@ let AuthController = class AuthController {
|
|
|
244
237
|
user = new user_model_1.default({
|
|
245
238
|
email,
|
|
246
239
|
name,
|
|
247
|
-
tenantId: tid || TENANT_ID,
|
|
248
240
|
microsoftId,
|
|
249
241
|
roles: [],
|
|
250
242
|
status: 'active',
|
|
@@ -257,10 +249,6 @@ let AuthController = class AuthController {
|
|
|
257
249
|
user.microsoftId = microsoftId;
|
|
258
250
|
changed = true;
|
|
259
251
|
}
|
|
260
|
-
if (!user.tenantId) {
|
|
261
|
-
user.tenantId = tid || TENANT_ID;
|
|
262
|
-
changed = true;
|
|
263
|
-
}
|
|
264
252
|
if (changed)
|
|
265
253
|
await user.save();
|
|
266
254
|
}
|
|
@@ -302,17 +290,10 @@ let AuthController = class AuthController {
|
|
|
302
290
|
async googleExchange(req, res) {
|
|
303
291
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
304
292
|
try {
|
|
305
|
-
let { code, idToken, type = 'user'
|
|
293
|
+
let { code, idToken, type = 'user' } = req.body || {};
|
|
306
294
|
if (!['user', 'client'].includes(type)) {
|
|
307
295
|
return res.status(400).json({ message: 'invalid type; must be "user" or "client"' });
|
|
308
296
|
}
|
|
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
297
|
let email;
|
|
317
298
|
let name;
|
|
318
299
|
let googleId;
|
|
@@ -352,14 +333,12 @@ let AuthController = class AuthController {
|
|
|
352
333
|
let principal = await Model.findOne({ $or: [{ email }, { googleId }] });
|
|
353
334
|
if (!principal) {
|
|
354
335
|
principal = new Model(type === 'user'
|
|
355
|
-
? { email, name, googleId,
|
|
336
|
+
? { email, name, googleId, roles: [], status: 'active' }
|
|
356
337
|
: { email, name, googleId, roles: [] });
|
|
357
338
|
await principal.save();
|
|
358
339
|
}
|
|
359
340
|
else if (!principal.googleId) {
|
|
360
341
|
principal.googleId = googleId;
|
|
361
|
-
if (type === 'user' && !principal.tenantId)
|
|
362
|
-
principal.tenantId = resolvedTenantId;
|
|
363
342
|
await principal.save();
|
|
364
343
|
}
|
|
365
344
|
const roleDocs = await role_model_1.default.find({ _id: { $in: principal.roles } })
|
|
@@ -368,7 +347,7 @@ let AuthController = class AuthController {
|
|
|
368
347
|
const permissions = Array.from(new Set(roleDocs.flatMap((r) => r.permissions)));
|
|
369
348
|
const accessTTL = resolveJwtExpiry(process.env.JWT_ACCESS_TOKEN_EXPIRES_IN, '15m');
|
|
370
349
|
const refreshTTL = resolveJwtExpiry(process.env.JWT_REFRESH_TOKEN_EXPIRES_IN, '7d');
|
|
371
|
-
const payload = { id: principal._id, email: principal.email,
|
|
350
|
+
const payload = { id: principal._id, email: principal.email, roles, permissions };
|
|
372
351
|
const accessToken = jsonwebtoken_1.default.sign(payload, process.env.JWT_SECRET, { expiresIn: accessTTL });
|
|
373
352
|
const refreshToken = jsonwebtoken_1.default.sign({ id: principal._id }, process.env.JWT_REFRESH_SECRET, { expiresIn: refreshTTL });
|
|
374
353
|
principal.refreshToken = refreshToken;
|
|
@@ -468,7 +447,6 @@ let AuthController = class AuthController {
|
|
|
468
447
|
const payload = {
|
|
469
448
|
id: principal._id,
|
|
470
449
|
email: principal.email,
|
|
471
|
-
tenantId: principal.tenantId,
|
|
472
450
|
roles,
|
|
473
451
|
permissions,
|
|
474
452
|
};
|
|
@@ -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.3",
|
|
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);
|