@ciscode/authentication-kit 1.1.0 → 1.1.2
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 +57 -73
- package/dist/auth-kit.module.d.ts +7 -0
- package/dist/auth-kit.module.js +50 -0
- package/dist/config/db.config.d.ts +1 -0
- package/dist/config/db.config.js +22 -0
- package/dist/config/passport.config.d.ts +3 -0
- package/dist/config/passport.config.js +272 -0
- package/dist/controllers/admin.controller.d.ts +4 -0
- package/dist/controllers/admin.controller.js +59 -0
- package/dist/controllers/auth.controller.d.ts +23 -0
- package/dist/controllers/auth.controller.js +645 -0
- package/dist/controllers/password-reset.controller.d.ts +8 -0
- package/dist/controllers/password-reset.controller.js +146 -0
- package/dist/controllers/permissions.controller.d.ts +7 -0
- package/dist/controllers/permissions.controller.js +115 -0
- package/dist/controllers/roles.controller.d.ts +7 -0
- package/dist/controllers/roles.controller.js +113 -0
- package/dist/controllers/users.controller.d.ts +8 -0
- package/dist/controllers/users.controller.js +259 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +14 -0
- package/dist/middleware/auth.guard.d.ts +4 -0
- package/dist/middleware/auth.guard.js +39 -0
- package/dist/middleware/authenticate.guard.d.ts +4 -0
- package/dist/middleware/authenticate.guard.js +44 -0
- package/dist/middleware/permission.guard.d.ts +4 -0
- package/dist/middleware/permission.guard.js +52 -0
- package/dist/middleware/tenant.guard.d.ts +4 -0
- package/dist/middleware/tenant.guard.js +39 -0
- package/dist/models/client.model.d.ts +54 -0
- package/dist/models/client.model.js +34 -0
- package/dist/models/permission.model.d.ts +19 -0
- package/dist/models/permission.model.js +17 -0
- package/dist/models/role.model.d.ts +33 -0
- package/dist/models/role.model.js +19 -0
- package/dist/models/tenant.model.d.ts +19 -0
- package/dist/models/tenant.model.js +15 -0
- package/dist/models/user.model.d.ts +63 -0
- package/dist/models/user.model.js +41 -0
- package/dist/standalone.d.ts +1 -0
- package/dist/standalone.js +12 -0
- package/dist/utils/helper.d.ts +1 -0
- package/dist/utils/helper.js +22 -0
- package/package.json +69 -43
- package/.github/workflows/ci .yml +0 -36
- package/.github/workflows/publish.yml +0 -30
- package/CODE_OF_CONDUCT +0 -38
- package/CONTRIBUTING.md +0 -40
- package/SECURITY +0 -31
- package/azure-pipelines.yml +0 -100
- package/src/config/db.config.js +0 -21
- package/src/config/passport.config.js +0 -280
- package/src/controllers/auth.controller.js +0 -566
- package/src/controllers/passwordReset.controller.js +0 -127
- package/src/controllers/permission.controller.js +0 -81
- package/src/controllers/roles.controller.js +0 -108
- package/src/controllers/user.controller.js +0 -283
- package/src/index.js +0 -32
- package/src/middleware/auth.middleware.js +0 -16
- package/src/middleware/authenticate.js +0 -25
- package/src/middleware/rbac.middleware.js +0 -24
- package/src/middleware/tenant.middleware.js +0 -16
- package/src/models/client.model.js +0 -39
- package/src/models/permission.model.js +0 -9
- package/src/models/role.model.js +0 -14
- package/src/models/tenant.model.js +0 -9
- package/src/models/user.model.js +0 -51
- package/src/routes/admin.routes.js +0 -8
- package/src/routes/auth.routes.js +0 -77
- package/src/routes/passwordReset.routes.js +0 -8
- package/src/routes/permission.routes.js +0 -17
- package/src/routes/roles.routes.js +0 -11
- package/src/routes/user.routes.js +0 -22
- package/src/utils/helper.js +0 -26
package/README.md
CHANGED
|
@@ -1,48 +1,31 @@
|
|
|
1
|
-
Auth Service (
|
|
2
|
-
Internal package
|
|
3
|
-
This package is not published on npmjs. Install it only from the company
|
|
1
|
+
Auth Service (NestJS, JWT, Multi-tenant, RBAC)
|
|
2
|
+
Internal package - private to the company.
|
|
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
|
-
Authentication
|
|
6
|
-
Provides local email/password auth with lockout, JWT access tokens
|
|
5
|
+
Authentication and authorization module for NestJS apps.
|
|
6
|
+
Provides local email/password auth with lockout, JWT access tokens and refresh, tenant scoping, 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
11
|
Multi-tenant scope on requests.
|
|
12
|
-
RBAC (roles
|
|
13
|
-
Microsoft Entra (Azure AD) OAuth (optional).
|
|
12
|
+
RBAC (roles -> permission strings).
|
|
13
|
+
Microsoft Entra (Azure AD), Google, Facebook OAuth (optional).
|
|
14
14
|
MongoDB/Mongoose models.
|
|
15
|
+
|
|
15
16
|
Routes are mounted under:
|
|
16
17
|
|
|
17
18
|
/api/auth (auth, password reset)
|
|
18
19
|
/api/users (user admin)
|
|
19
20
|
/api/auth/roles and /api/auth/permissions (RBAC)
|
|
20
|
-
|
|
21
|
-
1) Configure .npmrc
|
|
22
|
-
Do not commit real tokens. Prefer ~/.npmrc or generate .npmrc in CI using secrets.
|
|
23
|
-
|
|
24
|
-
For developers (user-level ~/.npmrc):
|
|
25
|
-
|
|
26
|
-
registry=https://registry.npmjs.org/
|
|
27
|
-
|
|
28
|
-
# Route @ciscodeapps scope to the private feed
|
|
29
|
-
@ciscodeapps:registry=https://pkgs.dev.azure.com/CISCODEAPPS/Templates/_packaging/testfeed/npm/registry/
|
|
21
|
+
/api/admin (admin actions)
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
always-auth=true
|
|
33
|
-
Set the token as an environment variable before installing:
|
|
23
|
+
Installation
|
|
34
24
|
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
1) Install the package
|
|
26
|
+
npm i @ciscode/authentication-kit
|
|
37
27
|
|
|
38
|
-
|
|
39
|
-
//pkgs.dev.azure.com/...:_password=${BASE64_PAT}
|
|
40
|
-
//pkgs.dev.azure.com/...:email=not-used@localhost
|
|
41
|
-
where BASE64_PAT=$(printf %s "$AZURE_ARTIFACTS_PAT" | base64).
|
|
42
|
-
|
|
43
|
-
2) Install the package
|
|
44
|
-
npm i @ciscodeapps/auth-service
|
|
45
|
-
3) Required environment variables (host app)
|
|
28
|
+
2) Required environment variables (host app)
|
|
46
29
|
Create a .env in the host project:
|
|
47
30
|
|
|
48
31
|
# Server
|
|
@@ -64,61 +47,59 @@ MAX_FAILED_LOGIN_ATTEMPTS=5
|
|
|
64
47
|
ACCOUNT_LOCK_TIME_MINUTES=15
|
|
65
48
|
|
|
66
49
|
# (Optional) Microsoft Entra ID (Azure AD)
|
|
67
|
-
|
|
50
|
+
MICROSOFT_TENANT_ID=common
|
|
68
51
|
MICROSOFT_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
69
52
|
MICROSOFT_CLIENT_SECRET=your-secret
|
|
70
53
|
MICROSOFT_CALLBACK_URL=${BASE_URL}/api/auth/microsoft/callback
|
|
71
|
-
Use inside an existing Express app
|
|
72
|
-
Your package exports an Express app that already parses JSON, connects to Mongo, and mounts its routes. Just mount it:
|
|
73
54
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
55
|
+
Use inside an existing Nest app
|
|
56
|
+
The module connects to Mongo on init and mounts its controllers.
|
|
57
|
+
|
|
58
|
+
// app.module.ts (host app)
|
|
59
|
+
import { Module } from '@nestjs/common';
|
|
60
|
+
import { AuthKitModule } from '@ciscode/authentication-kit';
|
|
61
|
+
|
|
62
|
+
@Module({
|
|
63
|
+
imports: [AuthKitModule]
|
|
64
|
+
})
|
|
65
|
+
export class AppModule {}
|
|
78
66
|
|
|
79
|
-
|
|
80
|
-
app.use(authApp); // exposes /api/auth, /api/users, /api/auth/roles, /api/auth/permissions
|
|
67
|
+
If you need to run it standalone, build and start the package:
|
|
81
68
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
console.log('Host app on', process.env.PORT || 3000)
|
|
85
|
-
);
|
|
86
|
-
Prefer mounting the service. If you need to run it standalone, you can also start this package directly (it calls connectDB() on import).
|
|
69
|
+
npm run build
|
|
70
|
+
npm start
|
|
87
71
|
|
|
88
|
-
What
|
|
72
|
+
What is included (routes and behavior)
|
|
89
73
|
Auth
|
|
90
|
-
POST /api/auth/
|
|
91
|
-
POST /api/auth/
|
|
92
|
-
POST /api/auth/
|
|
93
|
-
POST /api/auth/
|
|
94
|
-
|
|
95
|
-
PATCH /api/auth/reset-password – Consumes the reset token and sets a new password.
|
|
96
|
-
GET /api/auth/microsoft → GET /api/auth/microsoft/callback – Optional Microsoft Entra OAuth; issues first-party tokens.
|
|
74
|
+
POST /api/auth/login - Local login. On success, returns accessToken and may set a refreshToken httpOnly cookie.
|
|
75
|
+
POST /api/auth/refresh-token - New access token from a valid refresh token (cookie or body).
|
|
76
|
+
POST /api/auth/request-password-reset - Sends a reset token (e.g., by email).
|
|
77
|
+
POST /api/auth/reset-password - Consumes the reset token and sets a new password.
|
|
78
|
+
GET /api/auth/microsoft - GET /api/auth/microsoft/callback - Optional Microsoft Entra OAuth; issues first-party tokens.
|
|
97
79
|
Users
|
|
98
|
-
GET /api/users
|
|
99
|
-
POST /api/users
|
|
80
|
+
GET /api/users - List users (tenant-scoped, paginated).
|
|
81
|
+
POST /api/users - Create a user.
|
|
100
82
|
Additional CRUD endpoints as exposed by controllers.
|
|
101
|
-
Roles
|
|
102
|
-
GET/POST /api/auth/roles
|
|
103
|
-
GET /api/auth/permissions
|
|
83
|
+
Roles and Permissions
|
|
84
|
+
GET/POST /api/auth/roles - Manage roles (name, tenantId, permissions: string[]).
|
|
85
|
+
GET /api/auth/permissions - List permission strings and metadata.
|
|
86
|
+
|
|
104
87
|
Protecting your own routes (host app)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
88
|
+
import { UseGuards } from '@nestjs/common';
|
|
89
|
+
import { AuthenticateGuard, hasPermission } from '@ciscode/authentication-kit';
|
|
90
|
+
|
|
91
|
+
@UseGuards(AuthenticateGuard, hasPermission('reports:read'))
|
|
92
|
+
@Get('reports')
|
|
93
|
+
getReports() {
|
|
94
|
+
return { ok: true };
|
|
95
|
+
}
|
|
96
|
+
|
|
113
97
|
Tenant scope comes from the JWT payload (e.g., tenantId) and is used inside controllers/guards to filter queries.
|
|
114
98
|
|
|
115
99
|
Quick start (smoke tests)
|
|
116
|
-
Start your host app:
|
|
117
|
-
|
|
118
|
-
node server.js
|
|
119
|
-
Register & Login
|
|
100
|
+
Start your host app, then create a user and log in:
|
|
120
101
|
|
|
121
|
-
curl -X POST http://localhost:3000/api/
|
|
102
|
+
curl -X POST http://localhost:3000/api/users \
|
|
122
103
|
-H 'Content-Type: application/json' \
|
|
123
104
|
-d '{"email":"a@b.com","password":"Secret123!","tenantId":"t-001","name":"Alice"}'
|
|
124
105
|
|
|
@@ -126,17 +107,20 @@ curl -X POST http://localhost:3000/api/auth/login \
|
|
|
126
107
|
-H 'Content-Type: application/json' \
|
|
127
108
|
-d '{"email":"a@b.com","password":"Secret123!","tenantId":"t-001"}'
|
|
128
109
|
# => { "accessToken": "...", "refreshToken": "..." }
|
|
110
|
+
|
|
129
111
|
Call a protected route
|
|
130
112
|
|
|
131
113
|
ACCESS=<paste_access_token_here>
|
|
132
114
|
curl http://localhost:3000/api/users -H "Authorization: Bearer $ACCESS"
|
|
115
|
+
|
|
133
116
|
Refresh token
|
|
134
117
|
|
|
135
118
|
curl -X POST http://localhost:3000/api/auth/refresh-token \
|
|
136
119
|
-H 'Content-Type: application/json' \
|
|
137
120
|
-d '{"refreshToken":"<paste_refresh_token_here>"}'
|
|
138
121
|
# => { "accessToken": "..." }
|
|
139
|
-
|
|
122
|
+
|
|
123
|
+
Microsoft OAuth (optional) - Visit: http://localhost:3000/api/auth/microsoft to complete sign-in.
|
|
140
124
|
- Callback: ${BASE_URL}/api/auth/microsoft/callback returns tokens (and may set the refresh cookie).
|
|
141
125
|
|
|
142
126
|
CI/CD (Azure Pipelines)
|
|
@@ -151,7 +135,7 @@ CI/CD (Azure Pipelines)
|
|
|
151
135
|
|
|
152
136
|
Security notes
|
|
153
137
|
Never commit real PATs. Use env vars or CI secrets.
|
|
154
|
-
Run behind HTTPS. Rotate JWT
|
|
138
|
+
Run behind HTTPS. Rotate JWT and refresh secrets periodically.
|
|
155
139
|
Limit login attempts; log auth events for auditing.
|
|
156
140
|
License
|
|
157
|
-
Internal
|
|
141
|
+
Internal - Company proprietary.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import { MiddlewareConsumer, NestModule, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
|
3
|
+
export declare class AuthKitModule implements NestModule, OnModuleInit, OnModuleDestroy {
|
|
4
|
+
onModuleInit(): Promise<void>;
|
|
5
|
+
onModuleDestroy(): Promise<void>;
|
|
6
|
+
configure(consumer: MiddlewareConsumer): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
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.AuthKitModule = void 0;
|
|
13
|
+
require("dotenv/config");
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const passport_config_1 = __importDefault(require("./config/passport.config"));
|
|
16
|
+
const cookie_parser_1 = __importDefault(require("cookie-parser"));
|
|
17
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
18
|
+
const db_config_1 = require("./config/db.config");
|
|
19
|
+
const auth_controller_1 = require("./controllers/auth.controller");
|
|
20
|
+
const password_reset_controller_1 = require("./controllers/password-reset.controller");
|
|
21
|
+
const users_controller_1 = require("./controllers/users.controller");
|
|
22
|
+
const roles_controller_1 = require("./controllers/roles.controller");
|
|
23
|
+
const permissions_controller_1 = require("./controllers/permissions.controller");
|
|
24
|
+
const admin_controller_1 = require("./controllers/admin.controller");
|
|
25
|
+
let AuthKitModule = class AuthKitModule {
|
|
26
|
+
async onModuleInit() {
|
|
27
|
+
await (0, db_config_1.connectDB)();
|
|
28
|
+
}
|
|
29
|
+
async onModuleDestroy() {
|
|
30
|
+
await mongoose_1.default.disconnect();
|
|
31
|
+
}
|
|
32
|
+
configure(consumer) {
|
|
33
|
+
consumer
|
|
34
|
+
.apply((0, cookie_parser_1.default)(), passport_config_1.default.initialize())
|
|
35
|
+
.forRoutes({ path: '*', method: common_1.RequestMethod.ALL });
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.AuthKitModule = AuthKitModule;
|
|
39
|
+
exports.AuthKitModule = AuthKitModule = __decorate([
|
|
40
|
+
(0, common_1.Module)({
|
|
41
|
+
controllers: [
|
|
42
|
+
auth_controller_1.AuthController,
|
|
43
|
+
password_reset_controller_1.PasswordResetController,
|
|
44
|
+
users_controller_1.UsersController,
|
|
45
|
+
roles_controller_1.RolesController,
|
|
46
|
+
permissions_controller_1.PermissionsController,
|
|
47
|
+
admin_controller_1.AdminController,
|
|
48
|
+
],
|
|
49
|
+
})
|
|
50
|
+
], AuthKitModule);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function connectDB(): Promise<void>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.connectDB = connectDB;
|
|
7
|
+
const mongoose_1 = __importDefault(require("mongoose"));
|
|
8
|
+
mongoose_1.default.set('strictQuery', false);
|
|
9
|
+
async function connectDB() {
|
|
10
|
+
try {
|
|
11
|
+
const mongoURI = process.env.MONGO_URI_T;
|
|
12
|
+
if (!mongoURI) {
|
|
13
|
+
throw new Error('MONGO_URI is not defined in the environment variables.');
|
|
14
|
+
}
|
|
15
|
+
await mongoose_1.default.connect(mongoURI);
|
|
16
|
+
console.log('MongoDB Connected...');
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.error('MongoDB Connection Error:', error);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const passport_1 = __importDefault(require("passport"));
|
|
7
|
+
const passport_local_1 = require("passport-local");
|
|
8
|
+
const passport_azure_ad_oauth2_1 = require("passport-azure-ad-oauth2");
|
|
9
|
+
const passport_google_oauth20_1 = require("passport-google-oauth20");
|
|
10
|
+
const passport_facebook_1 = require("passport-facebook");
|
|
11
|
+
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
12
|
+
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
13
|
+
const user_model_1 = __importDefault(require("../models/user.model"));
|
|
14
|
+
const client_model_1 = __importDefault(require("../models/client.model"));
|
|
15
|
+
require("dotenv/config");
|
|
16
|
+
const MAX_FAILED = parseInt(process.env.MAX_FAILED_LOGIN_ATTEMPTS || '', 10) || 3;
|
|
17
|
+
const LOCK_TIME_MIN = parseInt(process.env.ACCOUNT_LOCK_TIME_MINUTES || '', 10) || 15;
|
|
18
|
+
const LOCK_TIME_MS = LOCK_TIME_MIN * 60 * 1000;
|
|
19
|
+
const DEFAULT_TENANT_ID = process.env.DEFAULT_TENANT_ID || 'social-staff';
|
|
20
|
+
passport_1.default.use(new passport_local_1.Strategy({ usernameField: 'email', passwordField: 'password', passReqToCallback: true }, async (req, email, password, done) => {
|
|
21
|
+
try {
|
|
22
|
+
const query = { email };
|
|
23
|
+
if (req.body.tenantId)
|
|
24
|
+
query.tenantId = String(req.body.tenantId).trim();
|
|
25
|
+
const user = await user_model_1.default.findOne(query);
|
|
26
|
+
if (!user)
|
|
27
|
+
return done(null, false, { message: 'Incorrect email (or tenant).' });
|
|
28
|
+
if (user.lockUntil && user.lockUntil > Date.now()) {
|
|
29
|
+
return done(null, false, { message: `Account locked until ${new Date(user.lockUntil).toLocaleString()}.` });
|
|
30
|
+
}
|
|
31
|
+
const ok = await bcryptjs_1.default.compare(password, user.password);
|
|
32
|
+
if (!ok) {
|
|
33
|
+
user.failedLoginAttempts += 1;
|
|
34
|
+
if (user.failedLoginAttempts >= MAX_FAILED)
|
|
35
|
+
user.lockUntil = Date.now() + LOCK_TIME_MS;
|
|
36
|
+
await user.save();
|
|
37
|
+
return done(null, false, { message: 'Incorrect password.' });
|
|
38
|
+
}
|
|
39
|
+
user.failedLoginAttempts = 0;
|
|
40
|
+
user.lockUntil = undefined;
|
|
41
|
+
await user.save();
|
|
42
|
+
return done(null, user);
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
return done(err);
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
passport_1.default.use(new passport_azure_ad_oauth2_1.Strategy({
|
|
49
|
+
clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
50
|
+
clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
|
|
51
|
+
callbackURL: process.env.MICROSOFT_CALLBACK_URL,
|
|
52
|
+
}, async (_at, _rt, params, _profile, done) => {
|
|
53
|
+
try {
|
|
54
|
+
const decoded = (0, jsonwebtoken_1.decode)(params.id_token);
|
|
55
|
+
const microsoftId = decoded.oid;
|
|
56
|
+
const email = decoded.preferred_username;
|
|
57
|
+
const name = decoded.name;
|
|
58
|
+
const tenantId = decoded.tid;
|
|
59
|
+
let user = await user_model_1.default.findOne({ $or: [{ microsoftId }, { email }] });
|
|
60
|
+
if (!user) {
|
|
61
|
+
user = new user_model_1.default({ email, name, tenantId, microsoftId, roles: [], status: 'active' });
|
|
62
|
+
await user.save();
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
let changed = false;
|
|
66
|
+
if (!user.microsoftId) {
|
|
67
|
+
user.microsoftId = microsoftId;
|
|
68
|
+
changed = true;
|
|
69
|
+
}
|
|
70
|
+
if (!user.tenantId) {
|
|
71
|
+
user.tenantId = tenantId;
|
|
72
|
+
changed = true;
|
|
73
|
+
}
|
|
74
|
+
if (changed)
|
|
75
|
+
await user.save();
|
|
76
|
+
}
|
|
77
|
+
return done(null, user);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return done(err);
|
|
81
|
+
}
|
|
82
|
+
}));
|
|
83
|
+
passport_1.default.use('azure_ad_oauth2_client', new passport_azure_ad_oauth2_1.Strategy({
|
|
84
|
+
clientID: process.env.MICROSOFT_CLIENT_ID_CLIENT || process.env.MICROSOFT_CLIENT_ID,
|
|
85
|
+
clientSecret: process.env.MICROSOFT_CLIENT_SECRET_CLIENT || process.env.MICROSOFT_CLIENT_SECRET,
|
|
86
|
+
callbackURL: process.env.MICROSOFT_CALLBACK_URL_CLIENT,
|
|
87
|
+
}, async (_at, _rt, params, _profile, done) => {
|
|
88
|
+
try {
|
|
89
|
+
const decoded = (0, jsonwebtoken_1.decode)(params.id_token);
|
|
90
|
+
const microsoftId = decoded.oid;
|
|
91
|
+
const email = decoded.preferred_username;
|
|
92
|
+
const name = decoded.name;
|
|
93
|
+
let client = await client_model_1.default.findOne({ $or: [{ microsoftId }, { email }] });
|
|
94
|
+
if (!client) {
|
|
95
|
+
client = new client_model_1.default({ email, name, microsoftId, roles: [] });
|
|
96
|
+
await client.save();
|
|
97
|
+
}
|
|
98
|
+
else if (!client.microsoftId) {
|
|
99
|
+
client.microsoftId = microsoftId;
|
|
100
|
+
await client.save();
|
|
101
|
+
}
|
|
102
|
+
return done(null, client);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
return done(err);
|
|
106
|
+
}
|
|
107
|
+
}));
|
|
108
|
+
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET && process.env.GOOGLE_CALLBACK_URL_USER) {
|
|
109
|
+
passport_1.default.use('google-user', new passport_google_oauth20_1.Strategy({
|
|
110
|
+
clientID: process.env.GOOGLE_CLIENT_ID,
|
|
111
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
112
|
+
callbackURL: process.env.GOOGLE_CALLBACK_URL_USER
|
|
113
|
+
}, async (_at, _rt, profile, done) => {
|
|
114
|
+
var _a;
|
|
115
|
+
try {
|
|
116
|
+
const email = profile.emails && ((_a = profile.emails[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
117
|
+
if (!email)
|
|
118
|
+
return done(null, false);
|
|
119
|
+
let user = await user_model_1.default.findOne({ email });
|
|
120
|
+
if (!user) {
|
|
121
|
+
user = new user_model_1.default({
|
|
122
|
+
email,
|
|
123
|
+
name: profile.displayName,
|
|
124
|
+
tenantId: DEFAULT_TENANT_ID,
|
|
125
|
+
googleId: profile.id,
|
|
126
|
+
roles: [],
|
|
127
|
+
status: 'active'
|
|
128
|
+
});
|
|
129
|
+
await user.save();
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
let changed = false;
|
|
133
|
+
if (!user.googleId) {
|
|
134
|
+
user.googleId = profile.id;
|
|
135
|
+
changed = true;
|
|
136
|
+
}
|
|
137
|
+
if (!user.tenantId) {
|
|
138
|
+
user.tenantId = DEFAULT_TENANT_ID;
|
|
139
|
+
changed = true;
|
|
140
|
+
}
|
|
141
|
+
if (changed)
|
|
142
|
+
await user.save();
|
|
143
|
+
}
|
|
144
|
+
return done(null, user);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
return done(err);
|
|
148
|
+
}
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET && process.env.GOOGLE_CALLBACK_URL_CLIENT) {
|
|
152
|
+
passport_1.default.use('google-client', new passport_google_oauth20_1.Strategy({
|
|
153
|
+
clientID: process.env.GOOGLE_CLIENT_ID,
|
|
154
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
155
|
+
callbackURL: process.env.GOOGLE_CALLBACK_URL_CLIENT
|
|
156
|
+
}, async (_at, _rt, profile, done) => {
|
|
157
|
+
var _a;
|
|
158
|
+
try {
|
|
159
|
+
const email = profile.emails && ((_a = profile.emails[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
160
|
+
if (!email)
|
|
161
|
+
return done(null, false);
|
|
162
|
+
let client = await client_model_1.default.findOne({ email });
|
|
163
|
+
if (!client) {
|
|
164
|
+
client = new client_model_1.default({
|
|
165
|
+
email,
|
|
166
|
+
name: profile.displayName,
|
|
167
|
+
googleId: profile.id,
|
|
168
|
+
roles: []
|
|
169
|
+
});
|
|
170
|
+
await client.save();
|
|
171
|
+
}
|
|
172
|
+
else if (!client.googleId) {
|
|
173
|
+
client.googleId = profile.id;
|
|
174
|
+
await client.save();
|
|
175
|
+
}
|
|
176
|
+
return done(null, client);
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
return done(err);
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
if (process.env.FB_CLIENT_ID && process.env.FB_CLIENT_SECRET && process.env.FB_CALLBACK_URL_USER) {
|
|
184
|
+
passport_1.default.use('facebook-user', new passport_facebook_1.Strategy({
|
|
185
|
+
clientID: process.env.FB_CLIENT_ID,
|
|
186
|
+
clientSecret: process.env.FB_CLIENT_SECRET,
|
|
187
|
+
callbackURL: process.env.FB_CALLBACK_URL_USER,
|
|
188
|
+
profileFields: ['id', 'displayName', 'emails']
|
|
189
|
+
}, async (_at, _rt, profile, done) => {
|
|
190
|
+
var _a;
|
|
191
|
+
try {
|
|
192
|
+
const email = profile.emails && ((_a = profile.emails[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
193
|
+
if (!email)
|
|
194
|
+
return done(null, false);
|
|
195
|
+
let user = await user_model_1.default.findOne({ email });
|
|
196
|
+
if (!user) {
|
|
197
|
+
user = new user_model_1.default({
|
|
198
|
+
email,
|
|
199
|
+
name: profile.displayName,
|
|
200
|
+
tenantId: DEFAULT_TENANT_ID,
|
|
201
|
+
facebookId: profile.id,
|
|
202
|
+
roles: [],
|
|
203
|
+
status: 'active'
|
|
204
|
+
});
|
|
205
|
+
await user.save();
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
let changed = false;
|
|
209
|
+
if (!user.facebookId) {
|
|
210
|
+
user.facebookId = profile.id;
|
|
211
|
+
changed = true;
|
|
212
|
+
}
|
|
213
|
+
if (!user.tenantId) {
|
|
214
|
+
user.tenantId = DEFAULT_TENANT_ID;
|
|
215
|
+
changed = true;
|
|
216
|
+
}
|
|
217
|
+
if (changed)
|
|
218
|
+
await user.save();
|
|
219
|
+
}
|
|
220
|
+
return done(null, user);
|
|
221
|
+
}
|
|
222
|
+
catch (err) {
|
|
223
|
+
return done(err);
|
|
224
|
+
}
|
|
225
|
+
}));
|
|
226
|
+
}
|
|
227
|
+
if (process.env.FB_CLIENT_ID && process.env.FB_CLIENT_SECRET && process.env.FB_CALLBACK_URL_CLIENT) {
|
|
228
|
+
passport_1.default.use('facebook-client', new passport_facebook_1.Strategy({
|
|
229
|
+
clientID: process.env.FB_CLIENT_ID,
|
|
230
|
+
clientSecret: process.env.FB_CLIENT_SECRET,
|
|
231
|
+
callbackURL: process.env.FB_CALLBACK_URL_CLIENT,
|
|
232
|
+
profileFields: ['id', 'displayName', 'emails']
|
|
233
|
+
}, async (_at, _rt, profile, done) => {
|
|
234
|
+
var _a;
|
|
235
|
+
try {
|
|
236
|
+
const email = profile.emails && ((_a = profile.emails[0]) === null || _a === void 0 ? void 0 : _a.value);
|
|
237
|
+
if (!email)
|
|
238
|
+
return done(null, false);
|
|
239
|
+
let client = await client_model_1.default.findOne({ email });
|
|
240
|
+
if (!client) {
|
|
241
|
+
client = new client_model_1.default({
|
|
242
|
+
email,
|
|
243
|
+
name: profile.displayName,
|
|
244
|
+
facebookId: profile.id,
|
|
245
|
+
roles: []
|
|
246
|
+
});
|
|
247
|
+
await client.save();
|
|
248
|
+
}
|
|
249
|
+
else if (!client.facebookId) {
|
|
250
|
+
client.facebookId = profile.id;
|
|
251
|
+
await client.save();
|
|
252
|
+
}
|
|
253
|
+
return done(null, client);
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
return done(err);
|
|
257
|
+
}
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
passport_1.default.serializeUser((principal, done) => done(null, principal.id));
|
|
261
|
+
passport_1.default.deserializeUser(async (id, done) => {
|
|
262
|
+
try {
|
|
263
|
+
let principal = await user_model_1.default.findById(id);
|
|
264
|
+
if (!principal)
|
|
265
|
+
principal = await client_model_1.default.findById(id);
|
|
266
|
+
done(null, principal);
|
|
267
|
+
}
|
|
268
|
+
catch (err) {
|
|
269
|
+
done(err);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
exports.default = passport_1.default;
|
|
@@ -0,0 +1,59 @@
|
|
|
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 __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
15
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
16
|
+
};
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.AdminController = void 0;
|
|
19
|
+
const common_1 = require("@nestjs/common");
|
|
20
|
+
const user_model_1 = __importDefault(require("../models/user.model"));
|
|
21
|
+
const authenticate_guard_1 = require("../middleware/authenticate.guard");
|
|
22
|
+
let AdminController = class AdminController {
|
|
23
|
+
async suspendUser(req, res) {
|
|
24
|
+
try {
|
|
25
|
+
if (!req.user || !req.user.roles) {
|
|
26
|
+
return res.status(403).json({ message: 'Access denied. Superadmin privileges required.' });
|
|
27
|
+
}
|
|
28
|
+
if (!req.user.roles.includes('superadmin')) {
|
|
29
|
+
return res.status(403).json({ message: 'Access denied. Superadmin privileges required.' });
|
|
30
|
+
}
|
|
31
|
+
const { id } = req.params;
|
|
32
|
+
if (!id) {
|
|
33
|
+
return res.status(400).json({ message: 'User ID is required in the URL.' });
|
|
34
|
+
}
|
|
35
|
+
const updatedUser = await user_model_1.default.findByIdAndUpdate(id, { status: 'suspended' }, { new: true });
|
|
36
|
+
if (!updatedUser) {
|
|
37
|
+
return res.status(404).json({ message: 'User not found.' });
|
|
38
|
+
}
|
|
39
|
+
return res.status(200).json({ message: 'User suspended successfully.', user: updatedUser });
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error('Error suspending user:', error);
|
|
43
|
+
return res.status(500).json({ message: 'Server error', error: error.message });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
exports.AdminController = AdminController;
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, common_1.Put)(':id/suspend'),
|
|
50
|
+
__param(0, (0, common_1.Req)()),
|
|
51
|
+
__param(1, (0, common_1.Res)()),
|
|
52
|
+
__metadata("design:type", Function),
|
|
53
|
+
__metadata("design:paramtypes", [Object, Object]),
|
|
54
|
+
__metadata("design:returntype", Promise)
|
|
55
|
+
], AdminController.prototype, "suspendUser", null);
|
|
56
|
+
exports.AdminController = AdminController = __decorate([
|
|
57
|
+
(0, common_1.UseGuards)(authenticate_guard_1.AuthenticateGuard),
|
|
58
|
+
(0, common_1.Controller)('api/admin')
|
|
59
|
+
], AdminController);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from 'express';
|
|
2
|
+
export declare class AuthController {
|
|
3
|
+
private issueTokensAndRespond;
|
|
4
|
+
private respondWebOrMobile;
|
|
5
|
+
registerClient(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
6
|
+
clientLogin(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
7
|
+
localLogin(req: Request, res: Response, next: NextFunction): any;
|
|
8
|
+
microsoftLogin(req: Request, res: Response, next: NextFunction): any;
|
|
9
|
+
microsoftCallback(req: Request, res: Response, next: NextFunction): void;
|
|
10
|
+
microsoftExchange(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
11
|
+
googleUserLogin(req: Request, res: Response, next: NextFunction): any;
|
|
12
|
+
googleUserCallback(req: Request, res: Response, next: NextFunction): void;
|
|
13
|
+
googleClientLogin(req: Request, res: Response, next: NextFunction): any;
|
|
14
|
+
googleClientCallback(req: Request, res: Response, next: NextFunction): void;
|
|
15
|
+
googleExchange(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
16
|
+
facebookUserLogin(req: Request, res: Response, next: NextFunction): any;
|
|
17
|
+
facebookUserCallback(req: Request, res: Response, next: NextFunction): void;
|
|
18
|
+
facebookClientLogin(req: Request, res: Response, next: NextFunction): any;
|
|
19
|
+
facebookClientCallback(req: Request, res: Response, next: NextFunction): void;
|
|
20
|
+
microsoftClientLogin(req: Request, res: Response, next: NextFunction): any;
|
|
21
|
+
microsoftClientCallback(req: Request, res: Response, next: NextFunction): void;
|
|
22
|
+
refreshToken(req: Request, res: Response): Promise<Response<any, Record<string, any>>>;
|
|
23
|
+
}
|