@saulo.martins/backend-auth 1.0.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/dist/auth.controller.d.ts +43 -0
- package/dist/auth.controller.js +115 -0
- package/dist/auth.module.d.ts +2 -0
- package/dist/auth.module.js +45 -0
- package/dist/auth.service.d.ts +51 -0
- package/dist/auth.service.js +243 -0
- package/dist/auth.service.spec.d.ts +1 -0
- package/dist/auth.service.spec.js +105 -0
- package/dist/contracts.d.ts +59 -0
- package/dist/contracts.js +2 -0
- package/dist/dto/change-password.dto.d.ts +6 -0
- package/dist/dto/change-password.dto.js +38 -0
- package/dist/dto/forgot-password.dto.d.ts +3 -0
- package/dist/dto/forgot-password.dto.js +20 -0
- package/dist/dto/login.dto.d.ts +5 -0
- package/dist/dto/login.dto.js +31 -0
- package/dist/dto/reset-password.dto.d.ts +5 -0
- package/dist/dto/reset-password.dto.js +31 -0
- package/dist/dto/signup.dto.d.ts +11 -0
- package/dist/dto/signup.dto.js +56 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +28 -0
- package/dist/jwt-optional.guard.d.ts +7 -0
- package/dist/jwt-optional.guard.js +31 -0
- package/dist/jwt.strategy.d.ts +13 -0
- package/dist/jwt.strategy.js +32 -0
- package/dist/tokens.d.ts +2 -0
- package/dist/tokens.js +5 -0
- package/jest.config.cjs +10 -0
- package/package.json +33 -0
- package/src/auth.controller.ts +77 -0
- package/src/auth.module.ts +35 -0
- package/src/auth.service.spec.ts +129 -0
- package/src/auth.service.ts +272 -0
- package/src/contracts.ts +63 -0
- package/src/dto/change-password.dto.ts +22 -0
- package/src/dto/forgot-password.dto.ts +7 -0
- package/src/dto/login.dto.ts +16 -0
- package/src/dto/reset-password.dto.ts +16 -0
- package/src/dto/signup.dto.ts +37 -0
- package/src/index.ts +13 -0
- package/src/jwt-optional.guard.ts +28 -0
- package/src/jwt.strategy.ts +19 -0
- package/src/tokens.ts +3 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,38 @@
|
|
|
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.ChangePasswordDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class ChangePasswordDto {
|
|
15
|
+
}
|
|
16
|
+
exports.ChangePasswordDto = ChangePasswordDto;
|
|
17
|
+
__decorate([
|
|
18
|
+
(0, class_validator_1.IsOptional)(),
|
|
19
|
+
(0, class_validator_1.IsString)(),
|
|
20
|
+
(0, class_validator_1.MinLength)(6),
|
|
21
|
+
__metadata("design:type", String)
|
|
22
|
+
], ChangePasswordDto.prototype, "currentPassword", void 0);
|
|
23
|
+
__decorate([
|
|
24
|
+
(0, class_validator_1.IsOptional)(),
|
|
25
|
+
(0, class_validator_1.IsString)(),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], ChangePasswordDto.prototype, "currentClientHash", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, class_validator_1.IsOptional)(),
|
|
30
|
+
(0, class_validator_1.IsString)(),
|
|
31
|
+
(0, class_validator_1.MinLength)(6),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], ChangePasswordDto.prototype, "newPassword", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, class_validator_1.IsOptional)(),
|
|
36
|
+
(0, class_validator_1.IsString)(),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], ChangePasswordDto.prototype, "newClientHash", void 0);
|
|
@@ -0,0 +1,20 @@
|
|
|
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.ForgotPasswordDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class ForgotPasswordDto {
|
|
15
|
+
}
|
|
16
|
+
exports.ForgotPasswordDto = ForgotPasswordDto;
|
|
17
|
+
__decorate([
|
|
18
|
+
(0, class_validator_1.IsEmail)(),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], ForgotPasswordDto.prototype, "email", void 0);
|
|
@@ -0,0 +1,31 @@
|
|
|
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.LoginDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class LoginDto {
|
|
15
|
+
}
|
|
16
|
+
exports.LoginDto = LoginDto;
|
|
17
|
+
__decorate([
|
|
18
|
+
(0, class_validator_1.IsEmail)(),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], LoginDto.prototype, "email", void 0);
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_validator_1.IsOptional)(),
|
|
23
|
+
(0, class_validator_1.IsString)(),
|
|
24
|
+
(0, class_validator_1.MinLength)(6),
|
|
25
|
+
__metadata("design:type", String)
|
|
26
|
+
], LoginDto.prototype, "password", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, class_validator_1.IsOptional)(),
|
|
29
|
+
(0, class_validator_1.IsString)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], LoginDto.prototype, "clientHash", void 0);
|
|
@@ -0,0 +1,31 @@
|
|
|
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.ResetPasswordDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class ResetPasswordDto {
|
|
15
|
+
}
|
|
16
|
+
exports.ResetPasswordDto = ResetPasswordDto;
|
|
17
|
+
__decorate([
|
|
18
|
+
(0, class_validator_1.IsString)(),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], ResetPasswordDto.prototype, "token", void 0);
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_validator_1.IsOptional)(),
|
|
23
|
+
(0, class_validator_1.IsString)(),
|
|
24
|
+
(0, class_validator_1.MinLength)(6),
|
|
25
|
+
__metadata("design:type", String)
|
|
26
|
+
], ResetPasswordDto.prototype, "password", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, class_validator_1.IsOptional)(),
|
|
29
|
+
(0, class_validator_1.IsString)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], ResetPasswordDto.prototype, "clientHash", void 0);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class SignupDto {
|
|
2
|
+
email: string;
|
|
3
|
+
password?: string;
|
|
4
|
+
clientHash?: string;
|
|
5
|
+
firstName?: string;
|
|
6
|
+
lastName?: string;
|
|
7
|
+
phoneCountryCode?: string;
|
|
8
|
+
phoneNumber?: string;
|
|
9
|
+
/** When set, the host app should link customer/company profile rows via `AuthUsersServiceContract.onSignupComplete`. */
|
|
10
|
+
userType?: 'customer' | 'company';
|
|
11
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
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.SignupDto = void 0;
|
|
13
|
+
const class_validator_1 = require("class-validator");
|
|
14
|
+
class SignupDto {
|
|
15
|
+
}
|
|
16
|
+
exports.SignupDto = SignupDto;
|
|
17
|
+
__decorate([
|
|
18
|
+
(0, class_validator_1.IsEmail)(),
|
|
19
|
+
__metadata("design:type", String)
|
|
20
|
+
], SignupDto.prototype, "email", void 0);
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_validator_1.IsOptional)(),
|
|
23
|
+
(0, class_validator_1.IsString)(),
|
|
24
|
+
(0, class_validator_1.MinLength)(6),
|
|
25
|
+
__metadata("design:type", String)
|
|
26
|
+
], SignupDto.prototype, "password", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
(0, class_validator_1.IsOptional)(),
|
|
29
|
+
(0, class_validator_1.IsString)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], SignupDto.prototype, "clientHash", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, class_validator_1.IsOptional)(),
|
|
34
|
+
(0, class_validator_1.IsString)(),
|
|
35
|
+
__metadata("design:type", String)
|
|
36
|
+
], SignupDto.prototype, "firstName", void 0);
|
|
37
|
+
__decorate([
|
|
38
|
+
(0, class_validator_1.IsOptional)(),
|
|
39
|
+
(0, class_validator_1.IsString)(),
|
|
40
|
+
__metadata("design:type", String)
|
|
41
|
+
], SignupDto.prototype, "lastName", void 0);
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, class_validator_1.IsOptional)(),
|
|
44
|
+
(0, class_validator_1.IsString)(),
|
|
45
|
+
__metadata("design:type", String)
|
|
46
|
+
], SignupDto.prototype, "phoneCountryCode", void 0);
|
|
47
|
+
__decorate([
|
|
48
|
+
(0, class_validator_1.IsOptional)(),
|
|
49
|
+
(0, class_validator_1.IsString)(),
|
|
50
|
+
__metadata("design:type", String)
|
|
51
|
+
], SignupDto.prototype, "phoneNumber", void 0);
|
|
52
|
+
__decorate([
|
|
53
|
+
(0, class_validator_1.IsOptional)(),
|
|
54
|
+
(0, class_validator_1.IsIn)(['customer', 'company']),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], SignupDto.prototype, "userType", void 0);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './auth.module';
|
|
2
|
+
export * from './auth.service';
|
|
3
|
+
export * from './auth.controller';
|
|
4
|
+
export * from './jwt.strategy';
|
|
5
|
+
export * from './jwt-optional.guard';
|
|
6
|
+
export * from './contracts';
|
|
7
|
+
export * from './tokens';
|
|
8
|
+
export * from './dto/signup.dto';
|
|
9
|
+
export * from './dto/login.dto';
|
|
10
|
+
export * from './dto/forgot-password.dto';
|
|
11
|
+
export * from './dto/reset-password.dto';
|
|
12
|
+
export * from './dto/change-password.dto';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./auth.module"), exports);
|
|
18
|
+
__exportStar(require("./auth.service"), exports);
|
|
19
|
+
__exportStar(require("./auth.controller"), exports);
|
|
20
|
+
__exportStar(require("./jwt.strategy"), exports);
|
|
21
|
+
__exportStar(require("./jwt-optional.guard"), exports);
|
|
22
|
+
__exportStar(require("./contracts"), exports);
|
|
23
|
+
__exportStar(require("./tokens"), exports);
|
|
24
|
+
__exportStar(require("./dto/signup.dto"), exports);
|
|
25
|
+
__exportStar(require("./dto/login.dto"), exports);
|
|
26
|
+
__exportStar(require("./dto/forgot-password.dto"), exports);
|
|
27
|
+
__exportStar(require("./dto/reset-password.dto"), exports);
|
|
28
|
+
__exportStar(require("./dto/change-password.dto"), exports);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ExecutionContext } from '@nestjs/common';
|
|
2
|
+
declare const JwtOptionalGuard_base: import("@nestjs/passport").Type<import("@nestjs/passport").IAuthGuard>;
|
|
3
|
+
export declare class JwtOptionalGuard extends JwtOptionalGuard_base {
|
|
4
|
+
handleRequest<TUser = unknown>(err: unknown, user: unknown, _info: unknown, _context: ExecutionContext, _status?: unknown): TUser;
|
|
5
|
+
canActivate(context: ExecutionContext): boolean | Promise<boolean> | import("rxjs").Observable<boolean>;
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.JwtOptionalGuard = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const passport_1 = require("@nestjs/passport");
|
|
12
|
+
let JwtOptionalGuard = class JwtOptionalGuard extends (0, passport_1.AuthGuard)('jwt') {
|
|
13
|
+
handleRequest(err, user, _info, _context, _status) {
|
|
14
|
+
if (err) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return (user ?? null);
|
|
18
|
+
}
|
|
19
|
+
canActivate(context) {
|
|
20
|
+
const request = context.switchToHttp().getRequest();
|
|
21
|
+
const authHeader = request.headers['authorization'];
|
|
22
|
+
if (!authHeader) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return super.canActivate(context);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.JwtOptionalGuard = JwtOptionalGuard;
|
|
29
|
+
exports.JwtOptionalGuard = JwtOptionalGuard = __decorate([
|
|
30
|
+
(0, common_1.Injectable)()
|
|
31
|
+
], JwtOptionalGuard);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Strategy } from 'passport-jwt';
|
|
2
|
+
declare const JwtStrategy_base: new (...args: any[]) => Strategy;
|
|
3
|
+
export declare class JwtStrategy extends JwtStrategy_base {
|
|
4
|
+
constructor();
|
|
5
|
+
validate(payload: {
|
|
6
|
+
sub: string;
|
|
7
|
+
email: string;
|
|
8
|
+
}): Promise<{
|
|
9
|
+
userId: string;
|
|
10
|
+
email: string;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
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.JwtStrategy = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const passport_1 = require("@nestjs/passport");
|
|
15
|
+
const passport_jwt_1 = require("passport-jwt");
|
|
16
|
+
let JwtStrategy = class JwtStrategy extends (0, passport_1.PassportStrategy)(passport_jwt_1.Strategy) {
|
|
17
|
+
constructor() {
|
|
18
|
+
super({
|
|
19
|
+
jwtFromRequest: passport_jwt_1.ExtractJwt.fromAuthHeaderAsBearerToken(),
|
|
20
|
+
ignoreExpiration: false,
|
|
21
|
+
secretOrKey: process.env.JWT_SECRET || 'dev-secret'
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async validate(payload) {
|
|
25
|
+
return { userId: payload.sub, email: payload.email };
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.JwtStrategy = JwtStrategy;
|
|
29
|
+
exports.JwtStrategy = JwtStrategy = __decorate([
|
|
30
|
+
(0, common_1.Injectable)(),
|
|
31
|
+
__metadata("design:paramtypes", [])
|
|
32
|
+
], JwtStrategy);
|
package/dist/tokens.d.ts
ADDED
package/dist/tokens.js
ADDED
package/jest.config.cjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@saulo.martins/backend-auth",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"private": false,
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p tsconfig.json",
|
|
9
|
+
"test": "jest"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@nestjs/common": "^10.4.15",
|
|
13
|
+
"@nestjs/jwt": "^10.2.0",
|
|
14
|
+
"@nestjs/passport": "^10.0.3",
|
|
15
|
+
"passport": "^0.7.0",
|
|
16
|
+
"passport-jwt": "^4.0.1",
|
|
17
|
+
"reflect-metadata": "^0.2.2",
|
|
18
|
+
"rxjs": "^7.8.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@nestjs/testing": "^10.4.15",
|
|
22
|
+
"@types/jest": "^29.5.14",
|
|
23
|
+
"@types/node": "^22.10.5",
|
|
24
|
+
"@types/passport-jwt": "^4.0.1",
|
|
25
|
+
"jest": "^29.7.0",
|
|
26
|
+
"ts-jest": "^29.2.5",
|
|
27
|
+
"typescript": "^5.7.3"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@nestjs/core": "^10.4.15"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Body, Controller, Get, Patch, Post, Req, UseGuards } from '@nestjs/common';
|
|
2
|
+
import { AuthGuard } from '@nestjs/passport';
|
|
3
|
+
import { Request } from 'express';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
import { SignupDto } from './dto/signup.dto';
|
|
6
|
+
import { LoginDto } from './dto/login.dto';
|
|
7
|
+
import { ChangePasswordDto } from './dto/change-password.dto';
|
|
8
|
+
import { ForgotPasswordDto } from './dto/forgot-password.dto';
|
|
9
|
+
import { ResetPasswordDto } from './dto/reset-password.dto';
|
|
10
|
+
|
|
11
|
+
interface JwtUser {
|
|
12
|
+
userId: string;
|
|
13
|
+
email: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Controller('auth')
|
|
17
|
+
export class AuthController {
|
|
18
|
+
constructor(private readonly authService: AuthService) {}
|
|
19
|
+
|
|
20
|
+
@Post('signup')
|
|
21
|
+
signup(@Body() dto: SignupDto) {
|
|
22
|
+
return this.authService.signup(dto);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@Post('login')
|
|
26
|
+
login(@Body() dto: LoginDto) {
|
|
27
|
+
return this.authService.login(dto);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Get('me')
|
|
31
|
+
@UseGuards(AuthGuard('jwt'))
|
|
32
|
+
getMe(@Req() req: Request & { user: JwtUser }) {
|
|
33
|
+
return this.authService.getMe(req.user.userId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@Patch('me')
|
|
37
|
+
@UseGuards(AuthGuard('jwt'))
|
|
38
|
+
async patchMe(
|
|
39
|
+
@Req() req: Request & { user: JwtUser },
|
|
40
|
+
@Body() body: { preferredLanguage?: string }
|
|
41
|
+
) {
|
|
42
|
+
if (body.preferredLanguage != null) {
|
|
43
|
+
await this.authService.updatePreferredLanguage(
|
|
44
|
+
req.user.userId,
|
|
45
|
+
body.preferredLanguage
|
|
46
|
+
);
|
|
47
|
+
return { preferredLanguage: body.preferredLanguage };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Patch('change-password')
|
|
52
|
+
@UseGuards(AuthGuard('jwt'))
|
|
53
|
+
changePassword(
|
|
54
|
+
@Req() req: Request & { user: JwtUser },
|
|
55
|
+
@Body() dto: ChangePasswordDto
|
|
56
|
+
) {
|
|
57
|
+
return this.authService.changePassword(req.user.userId, dto);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Post('forgot-password')
|
|
61
|
+
forgotPassword(
|
|
62
|
+
@Body() dto: ForgotPasswordDto,
|
|
63
|
+
@Req() req: Request
|
|
64
|
+
) {
|
|
65
|
+
// The caller should set X-Frontend-Base-Url if necessary; otherwise this can be overridden at service level.
|
|
66
|
+
const headerBase =
|
|
67
|
+
(req.headers['x-frontend-base-url'] as string | undefined) ?? '';
|
|
68
|
+
const base = headerBase || '';
|
|
69
|
+
return this.authService.forgotPassword(dto.email, base);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@Post('reset-password')
|
|
73
|
+
resetPassword(@Body() dto: ResetPasswordDto) {
|
|
74
|
+
return this.authService.resetPassword(dto);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common';
|
|
2
|
+
import { JwtModule } from '@nestjs/jwt';
|
|
3
|
+
import { PassportModule } from '@nestjs/passport';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
import { AuthController } from './auth.controller';
|
|
6
|
+
import { JwtStrategy } from './jwt.strategy';
|
|
7
|
+
import { AUTH_EMAIL_SERVICE, AUTH_USERS_SERVICE } from './tokens';
|
|
8
|
+
import { AuthEmailServiceContract, AuthUsersServiceContract } from './contracts';
|
|
9
|
+
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [
|
|
12
|
+
PassportModule,
|
|
13
|
+
JwtModule.register({
|
|
14
|
+
global: true,
|
|
15
|
+
secret: process.env.JWT_SECRET || 'dev-secret',
|
|
16
|
+
signOptions: { expiresIn: '1h' }
|
|
17
|
+
})
|
|
18
|
+
],
|
|
19
|
+
providers: [
|
|
20
|
+
AuthService,
|
|
21
|
+
JwtStrategy,
|
|
22
|
+
{
|
|
23
|
+
provide: AUTH_USERS_SERVICE,
|
|
24
|
+
useValue: null as unknown as AuthUsersServiceContract
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
provide: AUTH_EMAIL_SERVICE,
|
|
28
|
+
useValue: null as unknown as AuthEmailServiceContract
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
controllers: [AuthController],
|
|
32
|
+
exports: [AuthService, AUTH_USERS_SERVICE, AUTH_EMAIL_SERVICE]
|
|
33
|
+
})
|
|
34
|
+
export class AuthModule {}
|
|
35
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { JwtService } from '@nestjs/jwt';
|
|
2
|
+
import { AuthService, ApiError } from './auth.service';
|
|
3
|
+
import { AUTH_EMAIL_SERVICE, AUTH_USERS_SERVICE } from './tokens';
|
|
4
|
+
import { AuthEmailServiceContract, AuthUsersServiceContract } from './contracts';
|
|
5
|
+
|
|
6
|
+
class FakeUsersService implements AuthUsersServiceContract {
|
|
7
|
+
private users = new Map<string, { id: string; email: string; passwordScheme: 'sha256' | 'plain'; password: string }>();
|
|
8
|
+
|
|
9
|
+
async findByEmail(email: string) {
|
|
10
|
+
for (const u of this.users.values()) {
|
|
11
|
+
if (u.email === email) {
|
|
12
|
+
return {
|
|
13
|
+
id: u.id,
|
|
14
|
+
email: u.email,
|
|
15
|
+
passwordScheme: u.passwordScheme
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async findById(id: string) {
|
|
23
|
+
const u = this.users.get(id);
|
|
24
|
+
return u ? { id: u.id, email: u.email, passwordScheme: u.passwordScheme } : null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async create(input: {
|
|
28
|
+
email: string;
|
|
29
|
+
password: string;
|
|
30
|
+
passwordScheme: 'sha256' | 'plain';
|
|
31
|
+
}) {
|
|
32
|
+
const id = (this.users.size + 1).toString();
|
|
33
|
+
this.users.set(id, { id, email: input.email, passwordScheme: input.passwordScheme, password: input.password });
|
|
34
|
+
return { id, email: input.email, passwordScheme: input.passwordScheme };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async verifyCredentials(email: string, rawPasswordOrHash: string): Promise<void> {
|
|
38
|
+
const user = await this.findByEmail(email);
|
|
39
|
+
if (!user) throw new Error('not found');
|
|
40
|
+
const stored = Array.from(this.users.values()).find((u) => u.id === user.id);
|
|
41
|
+
if (!stored || stored.password !== rawPasswordOrHash) {
|
|
42
|
+
throw new Error('invalid credentials');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async upgradeToClientHash(userId: string, clientHash: string): Promise<void> {
|
|
47
|
+
const u = this.users.get(userId);
|
|
48
|
+
if (u) {
|
|
49
|
+
u.password = clientHash;
|
|
50
|
+
u.passwordScheme = 'sha256';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async setPasswordPlain(userId: string, plainPassword: string): Promise<void> {
|
|
55
|
+
const u = this.users.get(userId);
|
|
56
|
+
if (u) {
|
|
57
|
+
u.password = plainPassword;
|
|
58
|
+
u.passwordScheme = 'plain';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async updatePreferredLanguage(): Promise<void> {
|
|
63
|
+
// no-op for tests
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async createPasswordReset() {
|
|
67
|
+
return {
|
|
68
|
+
id: '1',
|
|
69
|
+
userId: '1',
|
|
70
|
+
token: 'token',
|
|
71
|
+
expiresAt: new Date(),
|
|
72
|
+
usedAt: null
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async findValidPasswordReset() {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async markPasswordResetUsed(): Promise<void> {
|
|
81
|
+
// no-op
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class FakeEmailService implements AuthEmailServiceContract {
|
|
86
|
+
public sent: { email: string; link: string }[] = [];
|
|
87
|
+
async sendPasswordResetEmail(email: string, resetLink: string): Promise<void> {
|
|
88
|
+
this.sent.push({ email, link: resetLink });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe('AuthService', () => {
|
|
93
|
+
const users = new FakeUsersService();
|
|
94
|
+
const email = new FakeEmailService();
|
|
95
|
+
const jwt = new JwtService({ secret: 'test-secret' });
|
|
96
|
+
const service = new AuthService(
|
|
97
|
+
// Inject via tokens in real Nest app; here we pass directly
|
|
98
|
+
(users as unknown) as any,
|
|
99
|
+
(email as unknown) as any,
|
|
100
|
+
jwt
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
it('signs up new user with clientHash', async () => {
|
|
104
|
+
const token = await service.signup({
|
|
105
|
+
email: 'john@example.com',
|
|
106
|
+
clientHash: 'hash',
|
|
107
|
+
password: undefined
|
|
108
|
+
} as any);
|
|
109
|
+
|
|
110
|
+
expect(typeof token.accessToken).toBe('string');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('prevents duplicate email signup', async () => {
|
|
114
|
+
await expect(
|
|
115
|
+
service.signup({
|
|
116
|
+
email: 'john@example.com',
|
|
117
|
+
clientHash: 'hash2',
|
|
118
|
+
password: undefined
|
|
119
|
+
} as any)
|
|
120
|
+
).rejects.toBeInstanceOf(ApiError);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('requires credentials on login', async () => {
|
|
124
|
+
await expect(
|
|
125
|
+
service.login({ email: 'unknown@example.com' } as any)
|
|
126
|
+
).rejects.toBeInstanceOf(ApiError);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|