@scryan7371/sdr-security 0.1.2 → 0.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 +48 -7
- package/dist/api/contracts.d.ts +0 -2
- package/dist/api/migrations/1739500000000-create-security-identity.d.ts +1 -1
- package/dist/api/migrations/1739500000000-create-security-identity.js +9 -35
- package/dist/api/migrations/1739510000000-create-security-roles.d.ts +1 -1
- package/dist/api/migrations/1739510000000-create-security-roles.js +1 -67
- package/dist/api/migrations/1739515000000-create-security-user-roles.d.ts +9 -0
- package/dist/api/migrations/1739515000000-create-security-user-roles.js +39 -0
- package/dist/api/migrations/1739520000000-create-password-reset-tokens.js +1 -1
- package/dist/api/migrations/1739530000000-create-security-user.d.ts +9 -0
- package/dist/api/migrations/1739530000000-create-security-user.js +41 -0
- package/dist/api/migrations/index.d.ts +3 -2
- package/dist/api/migrations/index.js +7 -4
- package/dist/api/migrations/migrations.test.js +37 -83
- package/dist/api/notification-workflows.d.ts +0 -4
- package/dist/api/notification-workflows.js +0 -1
- package/dist/api/notification-workflows.test.js +1 -4
- package/dist/app/client.d.ts +0 -2
- package/dist/app/client.test.js +0 -2
- package/dist/nest/contracts.d.ts +0 -3
- package/dist/nest/dto/auth.dto.d.ts +0 -2
- package/dist/nest/dto/auth.dto.js +0 -10
- package/dist/nest/entities/app-user.entity.d.ts +0 -7
- package/dist/nest/entities/app-user.entity.js +0 -35
- package/dist/nest/entities/security-user.entity.d.ts +9 -0
- package/dist/nest/entities/security-user.entity.js +54 -0
- package/dist/nest/index.d.ts +1 -0
- package/dist/nest/index.js +1 -0
- package/dist/nest/security-auth.controller.d.ts +0 -2
- package/dist/nest/security-auth.controller.js +0 -2
- package/dist/nest/security-auth.controller.test.js +0 -4
- package/dist/nest/security-auth.module.js +2 -0
- package/dist/nest/security-auth.service.d.ts +5 -4
- package/dist/nest/security-auth.service.js +81 -51
- package/dist/nest/security-auth.service.test.js +45 -41
- package/dist/nest/security-workflows.module.js +2 -0
- package/dist/nest/security-workflows.service.d.ts +4 -2
- package/dist/nest/security-workflows.service.js +19 -16
- package/dist/nest/security-workflows.service.test.js +29 -24
- package/package.json +3 -3
- package/src/api/contracts.ts +0 -2
- package/src/api/migrations/1739500000000-create-security-identity.ts +11 -50
- package/src/api/migrations/1739510000000-create-security-roles.ts +2 -89
- package/src/api/migrations/1739515000000-create-security-user-roles.ts +49 -0
- package/src/api/migrations/1739520000000-create-password-reset-tokens.ts +1 -1
- package/src/api/migrations/1739530000000-create-security-user.ts +51 -0
- package/src/api/migrations/index.ts +6 -3
- package/src/api/migrations/migrations.test.ts +48 -111
- package/src/api/notification-workflows.test.ts +1 -4
- package/src/api/notification-workflows.ts +1 -8
- package/src/app/client.test.ts +0 -2
- package/src/app/client.ts +1 -6
- package/src/nest/contracts.ts +1 -6
- package/src/nest/dto/auth.dto.ts +0 -6
- package/src/nest/entities/app-user.entity.ts +0 -21
- package/src/nest/entities/security-user.entity.ts +25 -0
- package/src/nest/index.ts +1 -0
- package/src/nest/security-auth.controller.test.ts +0 -4
- package/src/nest/security-auth.controller.ts +0 -4
- package/src/nest/security-auth.module.ts +2 -0
- package/src/nest/security-auth.service.test.ts +74 -43
- package/src/nest/security-auth.service.ts +88 -51
- package/src/nest/security-workflows.module.ts +2 -0
- package/src/nest/security-workflows.service.test.ts +31 -25
- package/src/nest/security-workflows.service.ts +18 -13
- package/dist/api/migrations/1739490000000-add-google-subject-to-user.d.ts +0 -5
- package/dist/api/migrations/1739490000000-add-google-subject-to-user.js +0 -14
- package/src/api/migrations/1739490000000-add-google-subject-to-user.ts +0 -12
package/dist/app/client.d.ts
CHANGED
|
@@ -9,8 +9,6 @@ export declare const createSecurityClient: (options: SecurityClientOptions) => {
|
|
|
9
9
|
register: (payload: {
|
|
10
10
|
email: string;
|
|
11
11
|
password: string;
|
|
12
|
-
firstName?: string;
|
|
13
|
-
lastName?: string;
|
|
14
12
|
}) => Promise<RegisterResponse>;
|
|
15
13
|
login: (payload: {
|
|
16
14
|
email: string;
|
package/dist/app/client.test.js
CHANGED
|
@@ -26,8 +26,6 @@ const makeFetch = (responses) => {
|
|
|
26
26
|
await client.register({
|
|
27
27
|
email: "user@example.com",
|
|
28
28
|
password: "Secret123",
|
|
29
|
-
firstName: "A",
|
|
30
|
-
lastName: "B",
|
|
31
29
|
});
|
|
32
30
|
(0, vitest_1.expect)(fetchImpl).toHaveBeenCalledWith("https://api.example.com/security/auth/register", vitest_1.expect.objectContaining({
|
|
33
31
|
method: "POST",
|
package/dist/nest/contracts.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export type SecurityWorkflowUser = {
|
|
2
2
|
id: string;
|
|
3
3
|
email: string;
|
|
4
|
-
firstName: string | null;
|
|
5
|
-
lastName: string | null;
|
|
6
4
|
};
|
|
7
5
|
export type SecurityWorkflowNotifier = {
|
|
8
6
|
sendEmailVerification?: (params: {
|
|
@@ -19,6 +17,5 @@ export type SecurityWorkflowNotifier = {
|
|
|
19
17
|
}) => Promise<void>;
|
|
20
18
|
sendUserAccountApproved: (params: {
|
|
21
19
|
email: string;
|
|
22
|
-
firstName: string | null;
|
|
23
20
|
}) => Promise<void>;
|
|
24
21
|
};
|
|
@@ -14,8 +14,6 @@ const swagger_1 = require("@nestjs/swagger");
|
|
|
14
14
|
class RegisterDto {
|
|
15
15
|
email;
|
|
16
16
|
password;
|
|
17
|
-
firstName;
|
|
18
|
-
lastName;
|
|
19
17
|
}
|
|
20
18
|
exports.RegisterDto = RegisterDto;
|
|
21
19
|
__decorate([
|
|
@@ -26,14 +24,6 @@ __decorate([
|
|
|
26
24
|
(0, swagger_1.ApiProperty)({ example: "StrongPass1" }),
|
|
27
25
|
__metadata("design:type", String)
|
|
28
26
|
], RegisterDto.prototype, "password", void 0);
|
|
29
|
-
__decorate([
|
|
30
|
-
(0, swagger_1.ApiProperty)({ required: false, nullable: true, example: "John" }),
|
|
31
|
-
__metadata("design:type", Object)
|
|
32
|
-
], RegisterDto.prototype, "firstName", void 0);
|
|
33
|
-
__decorate([
|
|
34
|
-
(0, swagger_1.ApiProperty)({ required: false, nullable: true, example: "Doe" }),
|
|
35
|
-
__metadata("design:type", Object)
|
|
36
|
-
], RegisterDto.prototype, "lastName", void 0);
|
|
37
27
|
class LoginDto {
|
|
38
28
|
email;
|
|
39
29
|
password;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
export declare class AppUserEntity {
|
|
2
2
|
id: string;
|
|
3
3
|
email: string;
|
|
4
|
-
passwordHash: string;
|
|
5
|
-
firstName: string | null;
|
|
6
|
-
lastName: string | null;
|
|
7
|
-
emailVerifiedAt: Date | null;
|
|
8
|
-
emailVerificationToken: string | null;
|
|
9
|
-
adminApprovedAt: Date | null;
|
|
10
|
-
isActive: boolean;
|
|
11
4
|
}
|
|
@@ -14,13 +14,6 @@ const typeorm_1 = require("typeorm");
|
|
|
14
14
|
let AppUserEntity = class AppUserEntity {
|
|
15
15
|
id;
|
|
16
16
|
email;
|
|
17
|
-
passwordHash;
|
|
18
|
-
firstName;
|
|
19
|
-
lastName;
|
|
20
|
-
emailVerifiedAt;
|
|
21
|
-
emailVerificationToken;
|
|
22
|
-
adminApprovedAt;
|
|
23
|
-
isActive;
|
|
24
17
|
};
|
|
25
18
|
exports.AppUserEntity = AppUserEntity;
|
|
26
19
|
__decorate([
|
|
@@ -31,34 +24,6 @@ __decorate([
|
|
|
31
24
|
(0, typeorm_1.Column)({ type: "varchar" }),
|
|
32
25
|
__metadata("design:type", String)
|
|
33
26
|
], AppUserEntity.prototype, "email", void 0);
|
|
34
|
-
__decorate([
|
|
35
|
-
(0, typeorm_1.Column)({ type: "varchar", name: "password_hash" }),
|
|
36
|
-
__metadata("design:type", String)
|
|
37
|
-
], AppUserEntity.prototype, "passwordHash", void 0);
|
|
38
|
-
__decorate([
|
|
39
|
-
(0, typeorm_1.Column)({ type: "varchar", name: "first_name", nullable: true }),
|
|
40
|
-
__metadata("design:type", Object)
|
|
41
|
-
], AppUserEntity.prototype, "firstName", void 0);
|
|
42
|
-
__decorate([
|
|
43
|
-
(0, typeorm_1.Column)({ type: "varchar", name: "last_name", nullable: true }),
|
|
44
|
-
__metadata("design:type", Object)
|
|
45
|
-
], AppUserEntity.prototype, "lastName", void 0);
|
|
46
|
-
__decorate([
|
|
47
|
-
(0, typeorm_1.Column)({ type: "timestamptz", name: "email_verified_at", nullable: true }),
|
|
48
|
-
__metadata("design:type", Object)
|
|
49
|
-
], AppUserEntity.prototype, "emailVerifiedAt", void 0);
|
|
50
|
-
__decorate([
|
|
51
|
-
(0, typeorm_1.Column)({ type: "varchar", name: "email_verification_token", nullable: true }),
|
|
52
|
-
__metadata("design:type", Object)
|
|
53
|
-
], AppUserEntity.prototype, "emailVerificationToken", void 0);
|
|
54
|
-
__decorate([
|
|
55
|
-
(0, typeorm_1.Column)({ type: "timestamptz", name: "admin_approved_at", nullable: true }),
|
|
56
|
-
__metadata("design:type", Object)
|
|
57
|
-
], AppUserEntity.prototype, "adminApprovedAt", void 0);
|
|
58
|
-
__decorate([
|
|
59
|
-
(0, typeorm_1.Column)({ type: "boolean", name: "is_active", default: true }),
|
|
60
|
-
__metadata("design:type", Boolean)
|
|
61
|
-
], AppUserEntity.prototype, "isActive", void 0);
|
|
62
27
|
exports.AppUserEntity = AppUserEntity = __decorate([
|
|
63
28
|
(0, typeorm_1.Entity)({ name: "app_user" })
|
|
64
29
|
], AppUserEntity);
|
|
@@ -0,0 +1,54 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SecurityUserEntity = void 0;
|
|
13
|
+
const typeorm_1 = require("typeorm");
|
|
14
|
+
let SecurityUserEntity = class SecurityUserEntity {
|
|
15
|
+
userId;
|
|
16
|
+
passwordHash;
|
|
17
|
+
emailVerifiedAt;
|
|
18
|
+
emailVerificationToken;
|
|
19
|
+
adminApprovedAt;
|
|
20
|
+
isActive;
|
|
21
|
+
createdAt;
|
|
22
|
+
};
|
|
23
|
+
exports.SecurityUserEntity = SecurityUserEntity;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, typeorm_1.PrimaryColumn)({ type: "varchar", name: "user_id" }),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], SecurityUserEntity.prototype, "userId", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, typeorm_1.Column)({ type: "varchar", name: "password_hash" }),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], SecurityUserEntity.prototype, "passwordHash", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, typeorm_1.Column)({ type: "timestamptz", name: "email_verified_at", nullable: true }),
|
|
34
|
+
__metadata("design:type", Object)
|
|
35
|
+
], SecurityUserEntity.prototype, "emailVerifiedAt", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, typeorm_1.Column)({ type: "varchar", name: "email_verification_token", nullable: true }),
|
|
38
|
+
__metadata("design:type", Object)
|
|
39
|
+
], SecurityUserEntity.prototype, "emailVerificationToken", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, typeorm_1.Column)({ type: "timestamptz", name: "admin_approved_at", nullable: true }),
|
|
42
|
+
__metadata("design:type", Object)
|
|
43
|
+
], SecurityUserEntity.prototype, "adminApprovedAt", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, typeorm_1.Column)({ type: "boolean", name: "is_active", default: true }),
|
|
46
|
+
__metadata("design:type", Boolean)
|
|
47
|
+
], SecurityUserEntity.prototype, "isActive", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, typeorm_1.CreateDateColumn)({ name: "created_at" }),
|
|
50
|
+
__metadata("design:type", Date)
|
|
51
|
+
], SecurityUserEntity.prototype, "createdAt", void 0);
|
|
52
|
+
exports.SecurityUserEntity = SecurityUserEntity = __decorate([
|
|
53
|
+
(0, typeorm_1.Entity)({ name: "security_user" })
|
|
54
|
+
], SecurityUserEntity);
|
package/dist/nest/index.d.ts
CHANGED
|
@@ -15,4 +15,5 @@ export * from "./entities/app-user.entity";
|
|
|
15
15
|
export * from "./entities/refresh-token.entity";
|
|
16
16
|
export * from "./entities/password-reset-token.entity";
|
|
17
17
|
export * from "./entities/security-role.entity";
|
|
18
|
+
export * from "./entities/security-user.entity";
|
|
18
19
|
export * from "./entities/security-user-role.entity";
|
package/dist/nest/index.js
CHANGED
|
@@ -31,4 +31,5 @@ __exportStar(require("./entities/app-user.entity"), exports);
|
|
|
31
31
|
__exportStar(require("./entities/refresh-token.entity"), exports);
|
|
32
32
|
__exportStar(require("./entities/password-reset-token.entity"), exports);
|
|
33
33
|
__exportStar(require("./entities/security-role.entity"), exports);
|
|
34
|
+
__exportStar(require("./entities/security-user.entity"), exports);
|
|
34
35
|
__exportStar(require("./entities/security-user-role.entity"), exports);
|
|
@@ -29,15 +29,11 @@ const makeAuthService = () => ({
|
|
|
29
29
|
const result = await controller.register({
|
|
30
30
|
email: "user@example.com",
|
|
31
31
|
password: "Secret123",
|
|
32
|
-
firstName: "A",
|
|
33
|
-
lastName: "B",
|
|
34
32
|
});
|
|
35
33
|
(0, vitest_1.expect)(result).toEqual({ success: true });
|
|
36
34
|
(0, vitest_1.expect)(service.register).toHaveBeenCalledWith({
|
|
37
35
|
email: "user@example.com",
|
|
38
36
|
password: "Secret123",
|
|
39
|
-
firstName: "A",
|
|
40
|
-
lastName: "B",
|
|
41
37
|
});
|
|
42
38
|
});
|
|
43
39
|
(0, vitest_1.it)("validates login and refresh payloads", async () => {
|
|
@@ -14,6 +14,7 @@ const app_user_entity_1 = require("./entities/app-user.entity");
|
|
|
14
14
|
const password_reset_token_entity_1 = require("./entities/password-reset-token.entity");
|
|
15
15
|
const refresh_token_entity_1 = require("./entities/refresh-token.entity");
|
|
16
16
|
const security_role_entity_1 = require("./entities/security-role.entity");
|
|
17
|
+
const security_user_entity_1 = require("./entities/security-user.entity");
|
|
17
18
|
const security_user_role_entity_1 = require("./entities/security-user-role.entity");
|
|
18
19
|
const security_admin_guard_1 = require("./security-admin.guard");
|
|
19
20
|
const security_auth_controller_1 = require("./security-auth.controller");
|
|
@@ -40,6 +41,7 @@ let SecurityAuthModule = SecurityAuthModule_1 = class SecurityAuthModule {
|
|
|
40
41
|
imports: [
|
|
41
42
|
typeorm_1.TypeOrmModule.forFeature([
|
|
42
43
|
app_user_entity_1.AppUserEntity,
|
|
44
|
+
security_user_entity_1.SecurityUserEntity,
|
|
43
45
|
refresh_token_entity_1.RefreshTokenEntity,
|
|
44
46
|
password_reset_token_entity_1.PasswordResetTokenEntity,
|
|
45
47
|
security_role_entity_1.SecurityRoleEntity,
|
|
@@ -5,22 +5,22 @@ import { AppUserEntity } from "./entities/app-user.entity";
|
|
|
5
5
|
import { PasswordResetTokenEntity } from "./entities/password-reset-token.entity";
|
|
6
6
|
import { RefreshTokenEntity } from "./entities/refresh-token.entity";
|
|
7
7
|
import { SecurityRoleEntity } from "./entities/security-role.entity";
|
|
8
|
+
import { SecurityUserEntity } from "./entities/security-user.entity";
|
|
8
9
|
import { SecurityUserRoleEntity } from "./entities/security-user-role.entity";
|
|
9
10
|
import { SecurityWorkflowNotifier } from "./contracts";
|
|
10
11
|
export declare class SecurityAuthService {
|
|
11
|
-
private readonly
|
|
12
|
+
private readonly appUsersRepo;
|
|
13
|
+
private readonly securityUsersRepo;
|
|
12
14
|
private readonly refreshTokenRepo;
|
|
13
15
|
private readonly passwordResetRepo;
|
|
14
16
|
private readonly rolesRepo;
|
|
15
17
|
private readonly userRolesRepo;
|
|
16
18
|
private readonly options;
|
|
17
19
|
private readonly notifier;
|
|
18
|
-
constructor(
|
|
20
|
+
constructor(appUsersRepo: Repository<AppUserEntity>, securityUsersRepo: Repository<SecurityUserEntity>, refreshTokenRepo: Repository<RefreshTokenEntity>, passwordResetRepo: Repository<PasswordResetTokenEntity>, rolesRepo: Repository<SecurityRoleEntity>, userRolesRepo: Repository<SecurityUserRoleEntity>, options: SecurityAuthModuleOptions, notifier: SecurityWorkflowNotifier);
|
|
19
21
|
register(params: {
|
|
20
22
|
email: string;
|
|
21
23
|
password: string;
|
|
22
|
-
firstName?: string | null;
|
|
23
|
-
lastName?: string | null;
|
|
24
24
|
}): Promise<RegisterResponse>;
|
|
25
25
|
login(params: {
|
|
26
26
|
email: string;
|
|
@@ -50,6 +50,7 @@ export declare class SecurityAuthService {
|
|
|
50
50
|
userId: string;
|
|
51
51
|
roles: string[];
|
|
52
52
|
}>;
|
|
53
|
+
getUserIdByVerificationToken(token: string): Promise<string | null>;
|
|
53
54
|
private assertCanAuthenticate;
|
|
54
55
|
private issueTokens;
|
|
55
56
|
private createEmailVerificationToken;
|
|
@@ -26,21 +26,24 @@ const app_user_entity_1 = require("./entities/app-user.entity");
|
|
|
26
26
|
const password_reset_token_entity_1 = require("./entities/password-reset-token.entity");
|
|
27
27
|
const refresh_token_entity_1 = require("./entities/refresh-token.entity");
|
|
28
28
|
const security_role_entity_1 = require("./entities/security-role.entity");
|
|
29
|
+
const security_user_entity_1 = require("./entities/security-user.entity");
|
|
29
30
|
const security_user_role_entity_1 = require("./entities/security-user-role.entity");
|
|
30
31
|
const tokens_1 = require("./tokens");
|
|
31
32
|
const EMAIL_TOKEN_BYTES = 24;
|
|
32
33
|
const REFRESH_TOKEN_BYTES = 32;
|
|
33
34
|
const PASSWORD_ROUNDS = 12;
|
|
34
35
|
let SecurityAuthService = class SecurityAuthService {
|
|
35
|
-
|
|
36
|
+
appUsersRepo;
|
|
37
|
+
securityUsersRepo;
|
|
36
38
|
refreshTokenRepo;
|
|
37
39
|
passwordResetRepo;
|
|
38
40
|
rolesRepo;
|
|
39
41
|
userRolesRepo;
|
|
40
42
|
options;
|
|
41
43
|
notifier;
|
|
42
|
-
constructor(
|
|
43
|
-
this.
|
|
44
|
+
constructor(appUsersRepo, securityUsersRepo, refreshTokenRepo, passwordResetRepo, rolesRepo, userRolesRepo, options, notifier) {
|
|
45
|
+
this.appUsersRepo = appUsersRepo;
|
|
46
|
+
this.securityUsersRepo = securityUsersRepo;
|
|
44
47
|
this.refreshTokenRepo = refreshTokenRepo;
|
|
45
48
|
this.passwordResetRepo = passwordResetRepo;
|
|
46
49
|
this.rolesRepo = rolesRepo;
|
|
@@ -50,45 +53,52 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
50
53
|
}
|
|
51
54
|
async register(params) {
|
|
52
55
|
const email = (0, validation_1.sanitizeEmail)(params.email);
|
|
53
|
-
const existing = await this.
|
|
56
|
+
const existing = await this.appUsersRepo.findOne({ where: { email } });
|
|
54
57
|
if (existing) {
|
|
55
58
|
throw new common_1.BadRequestException("Email already in use");
|
|
56
59
|
}
|
|
57
|
-
const
|
|
60
|
+
const appUser = await this.appUsersRepo.save(this.appUsersRepo.create({
|
|
58
61
|
email,
|
|
62
|
+
}));
|
|
63
|
+
const securityUser = await this.securityUsersRepo.save(this.securityUsersRepo.create({
|
|
64
|
+
userId: appUser.id,
|
|
59
65
|
passwordHash: await (0, bcryptjs_1.hash)(params.password, PASSWORD_ROUNDS),
|
|
60
|
-
firstName: params.firstName ?? null,
|
|
61
|
-
lastName: params.lastName ?? null,
|
|
62
66
|
emailVerifiedAt: null,
|
|
63
67
|
emailVerificationToken: null,
|
|
64
68
|
adminApprovedAt: null,
|
|
65
69
|
isActive: true,
|
|
66
70
|
}));
|
|
67
|
-
const verificationToken = await this.createEmailVerificationToken(
|
|
71
|
+
const verificationToken = await this.createEmailVerificationToken(appUser.id);
|
|
68
72
|
if (this.notifier.sendEmailVerification) {
|
|
69
73
|
await this.notifier.sendEmailVerification({
|
|
70
|
-
email:
|
|
74
|
+
email: appUser.email,
|
|
71
75
|
token: verificationToken,
|
|
72
76
|
});
|
|
73
77
|
}
|
|
74
78
|
return {
|
|
75
79
|
success: true,
|
|
76
|
-
user: await this.toSafeUser(
|
|
80
|
+
user: await this.toSafeUser(appUser, securityUser),
|
|
77
81
|
debugToken: verificationToken,
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
84
|
async login(params) {
|
|
81
85
|
const email = (0, validation_1.sanitizeEmail)(params.email);
|
|
82
|
-
const
|
|
83
|
-
if (!
|
|
86
|
+
const appUser = await this.appUsersRepo.findOne({ where: { email } });
|
|
87
|
+
if (!appUser) {
|
|
88
|
+
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
89
|
+
}
|
|
90
|
+
const securityUser = await this.securityUsersRepo.findOne({
|
|
91
|
+
where: { userId: appUser.id },
|
|
92
|
+
});
|
|
93
|
+
if (!securityUser) {
|
|
84
94
|
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
85
95
|
}
|
|
86
|
-
const ok = await (0, bcryptjs_1.compare)(params.password,
|
|
96
|
+
const ok = await (0, bcryptjs_1.compare)(params.password, securityUser.passwordHash);
|
|
87
97
|
if (!ok) {
|
|
88
98
|
throw new common_1.UnauthorizedException("Invalid credentials");
|
|
89
99
|
}
|
|
90
|
-
this.assertCanAuthenticate(
|
|
91
|
-
return this.issueTokens(
|
|
100
|
+
this.assertCanAuthenticate(securityUser);
|
|
101
|
+
return this.issueTokens(appUser, securityUser);
|
|
92
102
|
}
|
|
93
103
|
async refresh(refreshToken) {
|
|
94
104
|
const record = await this.findValidRefreshToken(refreshToken);
|
|
@@ -96,14 +106,20 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
96
106
|
throw new common_1.UnauthorizedException("Invalid refresh token");
|
|
97
107
|
}
|
|
98
108
|
await this.refreshTokenRepo.update({ id: record.id }, { revokedAt: new Date() });
|
|
99
|
-
const
|
|
109
|
+
const appUser = await this.appUsersRepo.findOne({
|
|
100
110
|
where: { id: record.userId ?? "" },
|
|
101
111
|
});
|
|
102
|
-
if (!
|
|
112
|
+
if (!appUser) {
|
|
103
113
|
throw new common_1.UnauthorizedException("User not found");
|
|
104
114
|
}
|
|
105
|
-
this.
|
|
106
|
-
|
|
115
|
+
const securityUser = await this.securityUsersRepo.findOne({
|
|
116
|
+
where: { userId: appUser.id },
|
|
117
|
+
});
|
|
118
|
+
if (!securityUser) {
|
|
119
|
+
throw new common_1.UnauthorizedException("User not found");
|
|
120
|
+
}
|
|
121
|
+
this.assertCanAuthenticate(securityUser);
|
|
122
|
+
return this.issueTokens(appUser, securityUser);
|
|
107
123
|
}
|
|
108
124
|
async logout(refreshToken) {
|
|
109
125
|
if (!refreshToken) {
|
|
@@ -116,34 +132,42 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
116
132
|
return { success: true };
|
|
117
133
|
}
|
|
118
134
|
async changePassword(params) {
|
|
119
|
-
const
|
|
120
|
-
|
|
135
|
+
const securityUser = await this.securityUsersRepo.findOne({
|
|
136
|
+
where: { userId: params.userId },
|
|
137
|
+
});
|
|
138
|
+
if (!securityUser) {
|
|
121
139
|
throw new common_1.BadRequestException("User not found");
|
|
122
140
|
}
|
|
123
|
-
const ok = await (0, bcryptjs_1.compare)(params.currentPassword,
|
|
141
|
+
const ok = await (0, bcryptjs_1.compare)(params.currentPassword, securityUser.passwordHash);
|
|
124
142
|
if (!ok) {
|
|
125
143
|
throw new common_1.UnauthorizedException("Current password is incorrect");
|
|
126
144
|
}
|
|
127
|
-
await this.
|
|
145
|
+
await this.securityUsersRepo.update({ userId: securityUser.userId }, { passwordHash: await (0, bcryptjs_1.hash)(params.newPassword, PASSWORD_ROUNDS) });
|
|
128
146
|
return { success: true };
|
|
129
147
|
}
|
|
130
148
|
async requestForgotPassword(emailInput) {
|
|
131
149
|
const email = (0, validation_1.sanitizeEmail)(emailInput);
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
150
|
+
const appUser = await this.appUsersRepo.findOne({ where: { email } });
|
|
151
|
+
if (!appUser) {
|
|
152
|
+
return { success: true };
|
|
153
|
+
}
|
|
154
|
+
const securityUser = await this.securityUsersRepo.findOne({
|
|
155
|
+
where: { userId: appUser.id },
|
|
156
|
+
});
|
|
157
|
+
if (!securityUser) {
|
|
134
158
|
return { success: true };
|
|
135
159
|
}
|
|
136
160
|
const token = (0, crypto_1.randomBytes)(EMAIL_TOKEN_BYTES).toString("hex");
|
|
137
161
|
const expiresAt = new Date(Date.now() +
|
|
138
162
|
(this.options.passwordResetTokenExpiresInMinutes ?? 30) * 60_000);
|
|
139
163
|
await this.passwordResetRepo.save(this.passwordResetRepo.create({
|
|
140
|
-
userId:
|
|
164
|
+
userId: appUser.id,
|
|
141
165
|
token,
|
|
142
166
|
expiresAt,
|
|
143
167
|
usedAt: null,
|
|
144
168
|
}));
|
|
145
169
|
if (this.notifier.sendPasswordReset) {
|
|
146
|
-
await this.notifier.sendPasswordReset({ email:
|
|
170
|
+
await this.notifier.sendPasswordReset({ email: appUser.email, token });
|
|
147
171
|
}
|
|
148
172
|
return { success: true };
|
|
149
173
|
}
|
|
@@ -152,23 +176,29 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
152
176
|
if (!reset || reset.usedAt || reset.expiresAt.getTime() <= Date.now()) {
|
|
153
177
|
throw new common_1.BadRequestException("Invalid password reset token");
|
|
154
178
|
}
|
|
155
|
-
await this.
|
|
179
|
+
await this.securityUsersRepo.update({ userId: reset.userId }, { passwordHash: await (0, bcryptjs_1.hash)(newPassword, PASSWORD_ROUNDS) });
|
|
156
180
|
await this.passwordResetRepo.update({ id: reset.id }, { usedAt: new Date() });
|
|
157
181
|
return { success: true };
|
|
158
182
|
}
|
|
159
183
|
async verifyEmailByToken(token) {
|
|
160
|
-
const user = await this.
|
|
184
|
+
const user = await this.securityUsersRepo.findOne({
|
|
161
185
|
where: { emailVerificationToken: token },
|
|
162
186
|
});
|
|
163
187
|
if (!user) {
|
|
164
188
|
throw new common_1.BadRequestException("Invalid verification token");
|
|
165
189
|
}
|
|
166
|
-
await this.
|
|
190
|
+
await this.securityUsersRepo.update({ userId: user.userId }, { emailVerifiedAt: new Date(), emailVerificationToken: null });
|
|
167
191
|
return { success: true };
|
|
168
192
|
}
|
|
169
193
|
async getMyRoles(userId) {
|
|
170
194
|
return { userId, roles: await this.getUserRoleKeys(userId) };
|
|
171
195
|
}
|
|
196
|
+
async getUserIdByVerificationToken(token) {
|
|
197
|
+
const user = await this.securityUsersRepo.findOne({
|
|
198
|
+
where: { emailVerificationToken: token },
|
|
199
|
+
});
|
|
200
|
+
return user?.userId ?? null;
|
|
201
|
+
}
|
|
172
202
|
assertCanAuthenticate(user) {
|
|
173
203
|
if (!user.isActive) {
|
|
174
204
|
throw new common_1.UnauthorizedException("Account is inactive");
|
|
@@ -181,17 +211,17 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
181
211
|
throw new common_1.UnauthorizedException("Admin approval required");
|
|
182
212
|
}
|
|
183
213
|
}
|
|
184
|
-
async issueTokens(
|
|
185
|
-
const roles = await this.getUserRoleKeys(
|
|
214
|
+
async issueTokens(appUser, securityUser) {
|
|
215
|
+
const roles = await this.getUserRoleKeys(appUser.id);
|
|
186
216
|
const accessTokenExpiresIn = this.options.accessTokenExpiresIn ?? "15m";
|
|
187
|
-
const accessToken = (0, jsonwebtoken_1.sign)({ sub:
|
|
217
|
+
const accessToken = (0, jsonwebtoken_1.sign)({ sub: appUser.id, email: appUser.email, roles }, this.options.jwtSecret, { expiresIn: accessTokenExpiresIn });
|
|
188
218
|
const refreshToken = (0, crypto_1.randomBytes)(REFRESH_TOKEN_BYTES).toString("hex");
|
|
189
219
|
const refreshTokenHash = await (0, bcryptjs_1.hash)(refreshToken, PASSWORD_ROUNDS);
|
|
190
220
|
const refreshTokenExpiresAt = new Date(Date.now() +
|
|
191
221
|
(this.options.refreshTokenExpiresInDays ?? 30) * 24 * 60 * 60 * 1000);
|
|
192
222
|
await this.refreshTokenRepo.save(this.refreshTokenRepo.create({
|
|
193
223
|
id: (0, crypto_1.randomUUID)(),
|
|
194
|
-
userId:
|
|
224
|
+
userId: appUser.id,
|
|
195
225
|
tokenHash: refreshTokenHash,
|
|
196
226
|
expiresAt: refreshTokenExpiresAt,
|
|
197
227
|
revokedAt: null,
|
|
@@ -201,12 +231,12 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
201
231
|
accessTokenExpiresIn,
|
|
202
232
|
refreshToken,
|
|
203
233
|
refreshTokenExpiresAt,
|
|
204
|
-
user: await this.toSafeUser(
|
|
234
|
+
user: await this.toSafeUser(appUser, securityUser),
|
|
205
235
|
};
|
|
206
236
|
}
|
|
207
237
|
async createEmailVerificationToken(userId) {
|
|
208
238
|
const token = (0, crypto_1.randomBytes)(EMAIL_TOKEN_BYTES).toString("hex");
|
|
209
|
-
await this.
|
|
239
|
+
await this.securityUsersRepo.update({ userId }, { emailVerificationToken: token });
|
|
210
240
|
return token;
|
|
211
241
|
}
|
|
212
242
|
async findValidRefreshToken(token, onlyUnexpired = true) {
|
|
@@ -236,18 +266,16 @@ let SecurityAuthService = class SecurityAuthService {
|
|
|
236
266
|
const roles = await this.rolesRepo.find({ where: { id: (0, typeorm_2.In)(roleIds) } });
|
|
237
267
|
return roles.map((role) => (0, roles_1.normalizeRoleName)(role.roleKey)).sort();
|
|
238
268
|
}
|
|
239
|
-
async toSafeUser(
|
|
269
|
+
async toSafeUser(appUser, securityUser) {
|
|
240
270
|
return {
|
|
241
|
-
id:
|
|
242
|
-
email:
|
|
243
|
-
firstName: user.firstName,
|
|
244
|
-
lastName: user.lastName,
|
|
271
|
+
id: appUser.id,
|
|
272
|
+
email: appUser.email,
|
|
245
273
|
phone: null,
|
|
246
|
-
roles: await this.getUserRoleKeys(
|
|
247
|
-
emailVerifiedAt:
|
|
274
|
+
roles: await this.getUserRoleKeys(appUser.id),
|
|
275
|
+
emailVerifiedAt: securityUser.emailVerifiedAt,
|
|
248
276
|
phoneVerifiedAt: null,
|
|
249
|
-
adminApprovedAt:
|
|
250
|
-
isActive:
|
|
277
|
+
adminApprovedAt: securityUser.adminApprovedAt,
|
|
278
|
+
isActive: securityUser.isActive,
|
|
251
279
|
};
|
|
252
280
|
}
|
|
253
281
|
};
|
|
@@ -255,15 +283,17 @@ exports.SecurityAuthService = SecurityAuthService;
|
|
|
255
283
|
exports.SecurityAuthService = SecurityAuthService = __decorate([
|
|
256
284
|
(0, common_1.Injectable)(),
|
|
257
285
|
__param(0, (0, typeorm_1.InjectRepository)(app_user_entity_1.AppUserEntity)),
|
|
258
|
-
__param(1, (0, typeorm_1.InjectRepository)(
|
|
259
|
-
__param(2, (0, typeorm_1.InjectRepository)(
|
|
260
|
-
__param(3, (0, typeorm_1.InjectRepository)(
|
|
261
|
-
__param(4, (0, typeorm_1.InjectRepository)(
|
|
262
|
-
__param(5, (0,
|
|
263
|
-
__param(6, (0, common_1.Inject)(
|
|
286
|
+
__param(1, (0, typeorm_1.InjectRepository)(security_user_entity_1.SecurityUserEntity)),
|
|
287
|
+
__param(2, (0, typeorm_1.InjectRepository)(refresh_token_entity_1.RefreshTokenEntity)),
|
|
288
|
+
__param(3, (0, typeorm_1.InjectRepository)(password_reset_token_entity_1.PasswordResetTokenEntity)),
|
|
289
|
+
__param(4, (0, typeorm_1.InjectRepository)(security_role_entity_1.SecurityRoleEntity)),
|
|
290
|
+
__param(5, (0, typeorm_1.InjectRepository)(security_user_role_entity_1.SecurityUserRoleEntity)),
|
|
291
|
+
__param(6, (0, common_1.Inject)(security_auth_constants_1.SECURITY_AUTH_OPTIONS)),
|
|
292
|
+
__param(7, (0, common_1.Inject)(tokens_1.SECURITY_WORKFLOW_NOTIFIER)),
|
|
264
293
|
__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
265
294
|
typeorm_2.Repository,
|
|
266
295
|
typeorm_2.Repository,
|
|
267
296
|
typeorm_2.Repository,
|
|
297
|
+
typeorm_2.Repository,
|
|
268
298
|
typeorm_2.Repository, Object, Object])
|
|
269
299
|
], SecurityAuthService);
|