@boarteam/boar-pack-users-backend 4.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/package.json +60 -0
- package/src/auth/auth-manage.controller.ts +36 -0
- package/src/auth/auth-strategies.constants.ts +4 -0
- package/src/auth/auth.constants.ts +1 -0
- package/src/auth/auth.controller.ts +83 -0
- package/src/auth/auth.module.ts +98 -0
- package/src/auth/auth.service.ts +55 -0
- package/src/auth/google-auth.config.ts +38 -0
- package/src/auth/google-auth.filter.ts +15 -0
- package/src/auth/google-auth.guard.ts +6 -0
- package/src/auth/google-auth.strategy.ts +59 -0
- package/src/auth/index.ts +15 -0
- package/src/auth/local-auth.dto.ts +8 -0
- package/src/auth/local-auth.guard.ts +6 -0
- package/src/auth/local-auth.strategy.ts +21 -0
- package/src/auth/ms-auth.config.ts +45 -0
- package/src/auth/ms-auth.guard.ts +8 -0
- package/src/auth/ms-auth.strategy.ts +63 -0
- package/src/casl/action.enum.ts +7 -0
- package/src/casl/casl-ability.factory.ts +122 -0
- package/src/casl/casl.module.ts +31 -0
- package/src/casl/index.ts +5 -0
- package/src/casl/policies/manage-all.policy.ts +9 -0
- package/src/casl/policies.guard.ts +80 -0
- package/src/event-logs/dto/event-log-create.dto.ts +47 -0
- package/src/event-logs/dto/event-log-timeline-query.dto.ts +13 -0
- package/src/event-logs/dto/event-log-timeline.dto.ts +9 -0
- package/src/event-logs/dto/event-log-update.dto.ts +47 -0
- package/src/event-logs/entities/event-log.entity.ts +139 -0
- package/src/event-logs/event-logs.constants.ts +2 -0
- package/src/event-logs/event-logs.controller.ts +67 -0
- package/src/event-logs/event-logs.interceptor.ts +75 -0
- package/src/event-logs/event-logs.logger.ts +48 -0
- package/src/event-logs/event-logs.middleware.ts +60 -0
- package/src/event-logs/event-logs.module.ts +129 -0
- package/src/event-logs/event-logs.permissions.ts +4 -0
- package/src/event-logs/event-logs.service.ts +187 -0
- package/src/event-logs/event-logs.types.ts +4 -0
- package/src/event-logs/index.ts +10 -0
- package/src/event-logs/policies/manage-event-logs.policy.ts +8 -0
- package/src/event-logs/policies/view-event-logs.policy.ts +8 -0
- package/src/generateTypes.ts +72 -0
- package/src/index.ts +6 -0
- package/src/jwt-auth/index.ts +5 -0
- package/src/jwt-auth/jwt-auth.config.ts +24 -0
- package/src/jwt-auth/jwt-auth.guard.ts +26 -0
- package/src/jwt-auth/jwt-auth.module.ts +58 -0
- package/src/jwt-auth/jwt-auth.service.ts +19 -0
- package/src/jwt-auth/jwt-auth.srtategy.ts +54 -0
- package/src/users/bcrypt.service.ts +20 -0
- package/src/users/dto/permission.dto.ts +5 -0
- package/src/users/dto/user-create.dto.ts +37 -0
- package/src/users/dto/user-update.dto.ts +37 -0
- package/src/users/entities/permissions.ts +23 -0
- package/src/users/entities/user.entity.ts +53 -0
- package/src/users/hash-password.interceptor.ts +22 -0
- package/src/users/index.ts +15 -0
- package/src/users/me.controller.ts +57 -0
- package/src/users/policies/view-users.policy.ts +10 -0
- package/src/users/users-editing.guard.ts +60 -0
- package/src/users/users.config.ts +24 -0
- package/src/users/users.constants.ts +1 -0
- package/src/users/users.controller.ts +73 -0
- package/src/users/users.module.ts +75 -0
- package/src/users/users.service.ts +23 -0
- package/src/ws-auth/index.ts +3 -0
- package/src/ws-auth/ws-auth.d2 +14 -0
- package/src/ws-auth/ws-auth.gateway.ts +25 -0
- package/src/ws-auth/ws-auth.guard.ts +28 -0
- package/src/ws-auth/ws-auth.module.ts +18 -0
- package/src/ws-auth/ws-auth.service.ts +93 -0
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boarteam/boar-pack-users-backend",
|
|
3
|
+
"version": "4.1.2",
|
|
4
|
+
"description": "NestJS Users module including permissions system, authentication strategies etc",
|
|
5
|
+
"main": "src/index",
|
|
6
|
+
"files": [
|
|
7
|
+
"src"
|
|
8
|
+
],
|
|
9
|
+
"repository": "git@github.com:boarteam/boar-pack.git",
|
|
10
|
+
"author": "Andrew Balakirev <balakirev.andrey@gmail.com>",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"registry": "https://registry.npmjs.org/",
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"peerDependencies": {
|
|
17
|
+
"@nestjs/common": "^9.0.0",
|
|
18
|
+
"@nestjs/config": "^3.0.0",
|
|
19
|
+
"@nestjs/typeorm": "^10.0.0",
|
|
20
|
+
"@nestjsx/crud-typeorm": "^5.0.0-alpha.3",
|
|
21
|
+
"passport-google-oauth20": "^2.0.0",
|
|
22
|
+
"typeorm": "^0.3.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@boarteam/boar-pack-common-backend": "^2.1.3",
|
|
26
|
+
"@casl/ability": "^6.7.0",
|
|
27
|
+
"@nestjs/common": "^9.0.0",
|
|
28
|
+
"@nestjs/config": "^3.2.0",
|
|
29
|
+
"@nestjs/core": "^9.0.0",
|
|
30
|
+
"@nestjs/jwt": "^10.2.0",
|
|
31
|
+
"@nestjs/passport": "^10.0.3",
|
|
32
|
+
"@nestjs/swagger": "^7.3.0",
|
|
33
|
+
"@nestjs/typeorm": "^10.0.2",
|
|
34
|
+
"@nestjsx/crud-typeorm": "^5.0.0-alpha.3",
|
|
35
|
+
"bcrypt": "^5.1.1",
|
|
36
|
+
"joi": "^17.12.2",
|
|
37
|
+
"lodash": "^4.17.21",
|
|
38
|
+
"moment": "^2.30.1",
|
|
39
|
+
"moment-timezone": "^0.5.45",
|
|
40
|
+
"nestjs-joi": "^1.10.0",
|
|
41
|
+
"passport": "^0.7.0",
|
|
42
|
+
"passport-jwt": "^4.0.1",
|
|
43
|
+
"passport-local": "^1.0.0",
|
|
44
|
+
"typeorm": "^0.3.20",
|
|
45
|
+
"ws": "^8.16.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/passport-google-oauth20": "^2.0.14",
|
|
49
|
+
"@types/passport-jwt": "^4.0.1",
|
|
50
|
+
"@types/passport-local": "^1.0.38",
|
|
51
|
+
"@types/ws": "^8.5.10",
|
|
52
|
+
"passport-azure-ad-oauth2": "^0.0.4",
|
|
53
|
+
"passport-google-oauth20": "^2.0.0"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"yalc:push": "yalc push",
|
|
57
|
+
"gen-types": "JWT_SECRET=swagger nest start"
|
|
58
|
+
},
|
|
59
|
+
"gitHead": "392553082a2f9f0124d05eaa11d0cdb6079766e1"
|
|
60
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Controller, NotFoundException, Param, Post, Req, Res, } from '@nestjs/common';
|
|
2
|
+
import { AuthService } from './auth.service';
|
|
3
|
+
import { tokenName } from './auth.constants';
|
|
4
|
+
import { ApiTags } from '@nestjs/swagger';
|
|
5
|
+
import type { Request, Response } from 'express';
|
|
6
|
+
import { LocalAuthTokenDto } from "./local-auth.dto";
|
|
7
|
+
import { CheckPolicies, ManageAllPolicy } from "../casl";
|
|
8
|
+
import { UsersService } from "../users";
|
|
9
|
+
|
|
10
|
+
@ApiTags('Authentication')
|
|
11
|
+
@CheckPolicies(new ManageAllPolicy())
|
|
12
|
+
@Controller('auth-manage')
|
|
13
|
+
export default class AuthManageController {
|
|
14
|
+
constructor(
|
|
15
|
+
private readonly authService: AuthService,
|
|
16
|
+
private readonly usersService: UsersService,
|
|
17
|
+
) {}
|
|
18
|
+
|
|
19
|
+
@Post('login-as-user/:userId')
|
|
20
|
+
async loginAsUser(
|
|
21
|
+
@Req() req: Request,
|
|
22
|
+
@Res({ passthrough: true }) res: Response,
|
|
23
|
+
@Param('userId') userId: string,
|
|
24
|
+
): Promise<LocalAuthTokenDto> {
|
|
25
|
+
const user = await this.usersService.findOne({
|
|
26
|
+
where: { id: userId },
|
|
27
|
+
});
|
|
28
|
+
if (!user) {
|
|
29
|
+
throw new NotFoundException(`User with id ${userId} is not found`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const loginResult = await this.authService.login(user);
|
|
33
|
+
res.cookie(tokenName, loginResult.accessToken);
|
|
34
|
+
return loginResult;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const tokenName = 'auth_token';
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Body, Controller, Get, Post, Req, Res, UnauthorizedException, UseFilters, UseGuards, } from '@nestjs/common';
|
|
2
|
+
import { LocalAuthGuard } from './local-auth.guard';
|
|
3
|
+
import { AuthService } from './auth.service';
|
|
4
|
+
import { tokenName } from './auth.constants';
|
|
5
|
+
import { SkipJWTGuard } from '../jwt-auth/jwt-auth.guard';
|
|
6
|
+
import { SkipPoliciesGuard } from '../casl/policies.guard';
|
|
7
|
+
import { GoogleAuthGuard } from './google-auth.guard';
|
|
8
|
+
import { AuthExceptionFilter } from './google-auth.filter';
|
|
9
|
+
import { ApiExtraModels, ApiTags } from '@nestjs/swagger';
|
|
10
|
+
import type { Request, Response } from 'express';
|
|
11
|
+
import { LocalAuthLoginDto, LocalAuthTokenDto } from "./local-auth.dto";
|
|
12
|
+
import { MSAuthGuard } from "./ms-auth.guard";
|
|
13
|
+
|
|
14
|
+
@SkipJWTGuard()
|
|
15
|
+
@SkipPoliciesGuard()
|
|
16
|
+
@ApiTags('Authentication')
|
|
17
|
+
@ApiExtraModels(LocalAuthTokenDto)
|
|
18
|
+
@Controller('auth')
|
|
19
|
+
export default class AuthController {
|
|
20
|
+
constructor(private authService: AuthService) {}
|
|
21
|
+
|
|
22
|
+
@UseGuards(LocalAuthGuard)
|
|
23
|
+
@Post('login')
|
|
24
|
+
async login(
|
|
25
|
+
@Req() req: Request,
|
|
26
|
+
@Res({ passthrough: true }) res: Response,
|
|
27
|
+
@Body() body: LocalAuthLoginDto,
|
|
28
|
+
): Promise<LocalAuthTokenDto> {
|
|
29
|
+
if (!req.user) {
|
|
30
|
+
throw new UnauthorizedException(`User is not authorized`);
|
|
31
|
+
}
|
|
32
|
+
const loginResult = await this.authService.login(req.user);
|
|
33
|
+
res.cookie(tokenName, loginResult.accessToken);
|
|
34
|
+
return loginResult;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@UseGuards(GoogleAuthGuard)
|
|
38
|
+
@Get('google')
|
|
39
|
+
async loginGoogle() {}
|
|
40
|
+
|
|
41
|
+
@UseGuards(GoogleAuthGuard)
|
|
42
|
+
@UseFilters(AuthExceptionFilter)
|
|
43
|
+
@Get('google/callback')
|
|
44
|
+
async loginGoogleCallback(
|
|
45
|
+
@Req() req: Request,
|
|
46
|
+
@Res({ passthrough: true }) res: Response,
|
|
47
|
+
) {
|
|
48
|
+
if (!req.user) {
|
|
49
|
+
throw new UnauthorizedException(`User is not authorized`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const loginResult = await this.authService.login(req.user);
|
|
53
|
+
res.cookie(tokenName, loginResult.accessToken);
|
|
54
|
+
res.redirect('/');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@UseGuards(MSAuthGuard)
|
|
58
|
+
@Get('ms')
|
|
59
|
+
async loginMS() {}
|
|
60
|
+
|
|
61
|
+
@UseGuards(MSAuthGuard)
|
|
62
|
+
@UseFilters(AuthExceptionFilter)
|
|
63
|
+
@Get('ms/callback')
|
|
64
|
+
async loginMSCallback(
|
|
65
|
+
@Req() req: Request,
|
|
66
|
+
@Res({ passthrough: true }) res: Response,
|
|
67
|
+
) {
|
|
68
|
+
if (!req.user) {
|
|
69
|
+
throw new UnauthorizedException(`User is not authorized`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const loginResult = await this.authService.login(req.user);
|
|
73
|
+
res.cookie(tokenName, loginResult.accessToken);
|
|
74
|
+
res.redirect('/');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@Post('logout')
|
|
78
|
+
async logout(
|
|
79
|
+
@Res({ passthrough: true }) res: Response,
|
|
80
|
+
) {
|
|
81
|
+
res.cookie(tokenName, '');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { DynamicModule, Module } from '@nestjs/common';
|
|
2
|
+
import { AuthService } from './auth.service';
|
|
3
|
+
import { UsersModule } from '../users/users.module';
|
|
4
|
+
import { LocalAuthStrategy } from './local-auth.strategy';
|
|
5
|
+
import { PassportModule } from '@nestjs/passport';
|
|
6
|
+
import AuthController from './auth.controller';
|
|
7
|
+
import { ConfigModule } from '@nestjs/config';
|
|
8
|
+
import { GoogleAuthStrategy } from './google-auth.strategy';
|
|
9
|
+
import { GoogleAuthConfigService } from "./google-auth.config";
|
|
10
|
+
import { MSAuthStrategy } from './ms-auth.strategy';
|
|
11
|
+
import { MSAuthConfigService } from "./ms-auth.config";
|
|
12
|
+
import { JwtAuthModule } from "../jwt-auth/jwt-auth.module";
|
|
13
|
+
import { APP_GUARD } from "@nestjs/core";
|
|
14
|
+
import { JwtAuthGuard } from "../jwt-auth/jwt-auth.guard";
|
|
15
|
+
import AuthManageController from "./auth-manage.controller";
|
|
16
|
+
|
|
17
|
+
@Module({})
|
|
18
|
+
export class AuthModule {
|
|
19
|
+
static forRoot(config: {
|
|
20
|
+
googleAuth: boolean,
|
|
21
|
+
msAuth: boolean,
|
|
22
|
+
localAuth: boolean,
|
|
23
|
+
withControllers: boolean,
|
|
24
|
+
dataSourceName?: string;
|
|
25
|
+
}): DynamicModule {
|
|
26
|
+
const dynamicModule: DynamicModule = {
|
|
27
|
+
module: AuthModule,
|
|
28
|
+
imports: [
|
|
29
|
+
ConfigModule,
|
|
30
|
+
UsersModule.register({
|
|
31
|
+
withControllers: false,
|
|
32
|
+
dataSourceName: config.dataSourceName,
|
|
33
|
+
}),
|
|
34
|
+
PassportModule,
|
|
35
|
+
JwtAuthModule.register({
|
|
36
|
+
dataSourceName: config.dataSourceName,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
providers: [
|
|
40
|
+
AuthService,
|
|
41
|
+
{
|
|
42
|
+
provide: APP_GUARD,
|
|
43
|
+
useClass: JwtAuthGuard,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
controllers: [],
|
|
47
|
+
exports: [],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (config.googleAuth) {
|
|
51
|
+
dynamicModule.providers!.push(GoogleAuthConfigService, GoogleAuthStrategy);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (config.msAuth) {
|
|
55
|
+
dynamicModule.providers!.push(MSAuthConfigService, MSAuthStrategy);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (config.localAuth) {
|
|
59
|
+
dynamicModule.providers!.push(LocalAuthStrategy);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (config.withControllers) {
|
|
63
|
+
dynamicModule.controllers = [
|
|
64
|
+
AuthController,
|
|
65
|
+
AuthManageController,
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return dynamicModule;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static forFeature(config: {
|
|
73
|
+
dataSourceName?: string;
|
|
74
|
+
}): DynamicModule {
|
|
75
|
+
return {
|
|
76
|
+
module: AuthModule,
|
|
77
|
+
imports: [
|
|
78
|
+
ConfigModule,
|
|
79
|
+
UsersModule.register({
|
|
80
|
+
withControllers: false,
|
|
81
|
+
dataSourceName: config.dataSourceName,
|
|
82
|
+
}),
|
|
83
|
+
JwtAuthModule.register({
|
|
84
|
+
dataSourceName: config.dataSourceName,
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
providers: [
|
|
88
|
+
AuthService,
|
|
89
|
+
{
|
|
90
|
+
provide: APP_GUARD,
|
|
91
|
+
useClass: JwtAuthGuard,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
controllers: [],
|
|
95
|
+
exports: [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { TUser, UsersService } from '../users';
|
|
3
|
+
import { JWTAuthService, TJWTPayload } from '../jwt-auth';
|
|
4
|
+
import bcrypt from 'bcrypt';
|
|
5
|
+
import { LocalAuthTokenDto } from "./local-auth.dto";
|
|
6
|
+
|
|
7
|
+
declare global {
|
|
8
|
+
namespace Express {
|
|
9
|
+
interface User extends TUser {}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@Injectable()
|
|
14
|
+
export class AuthService {
|
|
15
|
+
private readonly logger = new Logger(AuthService.name);
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
private usersService: UsersService,
|
|
19
|
+
private jwtAuthService: JWTAuthService,
|
|
20
|
+
) {}
|
|
21
|
+
|
|
22
|
+
async validateUser(email: string, pass: string): Promise<TUser | null> {
|
|
23
|
+
const user = await this.usersService.findByEmail(email);
|
|
24
|
+
if (!user?.pass) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (user && (await bcrypt.compare(pass, user.pass))) {
|
|
29
|
+
const { pass, ...result } = user;
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async validateUserByEmail(email?: string): Promise<TUser | null> {
|
|
36
|
+
if (!email) {
|
|
37
|
+
this.logger.error('Email is not provided to validateUserByEmail');
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const user = await this.usersService.findByEmail(email);
|
|
42
|
+
if (user) {
|
|
43
|
+
const { pass, ...result } = user;
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async login(user: Pick<TUser, 'email' | 'id'>): Promise<LocalAuthTokenDto> {
|
|
50
|
+
const payload: TJWTPayload = { email: user.email, sub: user.id };
|
|
51
|
+
return {
|
|
52
|
+
accessToken: this.jwtAuthService.sign(payload),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
|
|
4
|
+
export type TGoogleAuthConfig = {
|
|
5
|
+
clientId: string;
|
|
6
|
+
clientSecret: string;
|
|
7
|
+
callbackURL: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
@Injectable()
|
|
11
|
+
export class GoogleAuthConfigService {
|
|
12
|
+
constructor(private configService: ConfigService) {
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get config(): TGoogleAuthConfig {
|
|
16
|
+
const clientId = this.configService.get<string>('GOOGLE_CLIENT_ID');
|
|
17
|
+
const clientSecret = this.configService.get<string>('GOOGLE_SECRET_ID');
|
|
18
|
+
const callbackURL = this.configService.get<string>('GOOGLE_CALLBACK_URL');
|
|
19
|
+
|
|
20
|
+
if (!clientId) {
|
|
21
|
+
throw new Error('GOOGLE_CLIENT_ID is not defined');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!clientSecret) {
|
|
25
|
+
throw new Error('GOOGLE_SECRET_ID is not defined');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!callbackURL) {
|
|
29
|
+
throw new Error('GOOGLE_CALLBACK_URL is not defined');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
clientId,
|
|
34
|
+
clientSecret,
|
|
35
|
+
callbackURL,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
|
|
2
|
+
import { HttpException } from '@nestjs/common/exceptions/http.exception';
|
|
3
|
+
|
|
4
|
+
@Catch(HttpException)
|
|
5
|
+
export class AuthExceptionFilter implements ExceptionFilter {
|
|
6
|
+
catch(exception: HttpException, host: ArgumentsHost) {
|
|
7
|
+
const ctx = host.switchToHttp();
|
|
8
|
+
const response = ctx.getResponse();
|
|
9
|
+
const status = exception.getStatus();
|
|
10
|
+
|
|
11
|
+
response
|
|
12
|
+
.status(status)
|
|
13
|
+
.redirect('/user/login?error=' + encodeURIComponent(exception.message));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
|
|
2
|
+
import { PassportStrategy } from '@nestjs/passport';
|
|
3
|
+
import {
|
|
4
|
+
Injectable,
|
|
5
|
+
InternalServerErrorException,
|
|
6
|
+
Logger,
|
|
7
|
+
UnauthorizedException,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
import { AuthService } from './auth.service';
|
|
10
|
+
import { GOOGLE_AUTH } from './auth-strategies.constants';
|
|
11
|
+
import { GoogleAuthConfigService } from "./google-auth.config";
|
|
12
|
+
|
|
13
|
+
@Injectable()
|
|
14
|
+
export class GoogleAuthStrategy extends PassportStrategy(
|
|
15
|
+
Strategy,
|
|
16
|
+
GOOGLE_AUTH,
|
|
17
|
+
) {
|
|
18
|
+
private readonly logger = new Logger(GoogleAuthStrategy.name);
|
|
19
|
+
|
|
20
|
+
constructor(
|
|
21
|
+
private authService: AuthService,
|
|
22
|
+
private googleAuthConfigService: GoogleAuthConfigService,
|
|
23
|
+
) {
|
|
24
|
+
const config = googleAuthConfigService.config;
|
|
25
|
+
super({
|
|
26
|
+
clientID: config.clientId,
|
|
27
|
+
clientSecret: config.clientSecret,
|
|
28
|
+
callbackURL: config.callbackURL,
|
|
29
|
+
scope: ['email', 'profile'],
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async validate(
|
|
34
|
+
accessToken: string,
|
|
35
|
+
refreshToken: string,
|
|
36
|
+
profile: { emails: { value: string; verified: boolean }[] },
|
|
37
|
+
callback: VerifyCallback,
|
|
38
|
+
): Promise<any> {
|
|
39
|
+
try {
|
|
40
|
+
const user = await this.authService.validateUserByEmail(
|
|
41
|
+
profile.emails[0].value,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
if (!user) {
|
|
45
|
+
callback(new UnauthorizedException('User is not found'));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
callback(null, user);
|
|
50
|
+
} catch (e) {
|
|
51
|
+
this.logger.error(e, e.stack);
|
|
52
|
+
callback(
|
|
53
|
+
new InternalServerErrorException(
|
|
54
|
+
'Impossible to log in user via google',
|
|
55
|
+
),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './auth.constants';
|
|
2
|
+
export * from './auth.controller';
|
|
3
|
+
export * from './auth.module';
|
|
4
|
+
export * from './auth.service';
|
|
5
|
+
export * from './auth-strategies.constants';
|
|
6
|
+
export * from './google-auth.config';
|
|
7
|
+
export * from './google-auth.filter';
|
|
8
|
+
export * from './google-auth.guard';
|
|
9
|
+
export * from './google-auth.strategy';
|
|
10
|
+
export * from './local-auth.dto';
|
|
11
|
+
export * from './local-auth.guard';
|
|
12
|
+
export * from './local-auth.strategy';
|
|
13
|
+
export * from './ms-auth.config';
|
|
14
|
+
export * from './ms-auth.guard';
|
|
15
|
+
export * from './ms-auth.strategy';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Strategy } from 'passport-local';
|
|
2
|
+
import { PassportStrategy } from '@nestjs/passport';
|
|
3
|
+
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
|
4
|
+
import { AuthService } from './auth.service';
|
|
5
|
+
import { TUser } from '../users';
|
|
6
|
+
import { LOCAL_AUTH } from './auth-strategies.constants';
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class LocalAuthStrategy extends PassportStrategy(Strategy, LOCAL_AUTH) {
|
|
10
|
+
constructor(private authService: AuthService) {
|
|
11
|
+
super({ usernameField: 'email' });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async validate(email: string, password: string): Promise<TUser> {
|
|
15
|
+
const user = await this.authService.validateUser(email.trim().toLowerCase(), password);
|
|
16
|
+
if (!user) {
|
|
17
|
+
throw new UnauthorizedException();
|
|
18
|
+
}
|
|
19
|
+
return user;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
3
|
+
|
|
4
|
+
export type TMSAuthConfig = {
|
|
5
|
+
clientId: string;
|
|
6
|
+
tenantId: string;
|
|
7
|
+
clientSecret: string;
|
|
8
|
+
callbackURL: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class MSAuthConfigService {
|
|
13
|
+
constructor(private configService: ConfigService) {
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get config(): TMSAuthConfig {
|
|
17
|
+
const clientId = this.configService.get<string>('MICROSOFT_CLIENT_ID');
|
|
18
|
+
const tenantId = this.configService.get<string>('MICROSOFT_TENANT_ID');
|
|
19
|
+
const clientSecret = this.configService.get<string>('MICROSOFT_SECRET_ID');
|
|
20
|
+
const callbackURL = this.configService.get<string>('MICROSOFT_CALLBACK_URL');
|
|
21
|
+
|
|
22
|
+
if (!clientId) {
|
|
23
|
+
throw new Error('MICROSOFT_CLIENT_ID is not defined');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!tenantId) {
|
|
27
|
+
throw new Error('MICROSOFT_TENANT_ID is not defined');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!clientSecret) {
|
|
31
|
+
throw new Error('MICROSOFT_SECRET_ID is not defined');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!callbackURL) {
|
|
35
|
+
throw new Error('MICROSOFT_CALLBACK_URL is not defined');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
clientId,
|
|
40
|
+
tenantId,
|
|
41
|
+
clientSecret,
|
|
42
|
+
callbackURL,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { PassportStrategy } from '@nestjs/passport';
|
|
2
|
+
import { Injectable, InternalServerErrorException, Logger, UnauthorizedException, } from '@nestjs/common';
|
|
3
|
+
import { AuthService } from './auth.service';
|
|
4
|
+
import { MS_AUTH } from './auth-strategies.constants';
|
|
5
|
+
import { MSAuthConfigService } from "./ms-auth.config";
|
|
6
|
+
import { JWTAuthService } from "../jwt-auth/jwt-auth.service";
|
|
7
|
+
|
|
8
|
+
// @ts-ignore-next-line There are no types for this package
|
|
9
|
+
import { Strategy, VerifyCallback } from 'passport-azure-ad-oauth2';
|
|
10
|
+
|
|
11
|
+
@Injectable()
|
|
12
|
+
export class MSAuthStrategy extends PassportStrategy(
|
|
13
|
+
Strategy,
|
|
14
|
+
MS_AUTH,
|
|
15
|
+
) {
|
|
16
|
+
private readonly logger = new Logger(MSAuthStrategy.name);
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private authService: AuthService,
|
|
20
|
+
private msAuthConfigService: MSAuthConfigService,
|
|
21
|
+
private jwtAuthService: JWTAuthService,
|
|
22
|
+
) {
|
|
23
|
+
const config = msAuthConfigService.config;
|
|
24
|
+
super({
|
|
25
|
+
clientID: config.clientId,
|
|
26
|
+
clientSecret: config.clientSecret,
|
|
27
|
+
callbackURL: config.callbackURL,
|
|
28
|
+
tenant: config.tenantId,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async validate(
|
|
33
|
+
accessToken: string,
|
|
34
|
+
refreshToken: string,
|
|
35
|
+
profile: any,
|
|
36
|
+
callback: VerifyCallback,
|
|
37
|
+
): Promise<any> {
|
|
38
|
+
this.logger.debug(`accessToken: ${accessToken}`);
|
|
39
|
+
this.logger.debug(`refreshToken: ${refreshToken}`);
|
|
40
|
+
this.logger.debug(`profile: ${JSON.stringify(profile)}`);
|
|
41
|
+
const token = this.jwtAuthService.decode<{ email?: string, upn?: string }>(accessToken);
|
|
42
|
+
this.logger.debug(`token: ${JSON.stringify(token)}`);
|
|
43
|
+
try {
|
|
44
|
+
const user = await this.authService.validateUserByEmail(
|
|
45
|
+
token.email || token.upn,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
if (!user) {
|
|
49
|
+
callback(new UnauthorizedException('User is not found'));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
callback(null, user);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
this.logger.error(e, e.stack);
|
|
56
|
+
callback(
|
|
57
|
+
new InternalServerErrorException(
|
|
58
|
+
'Impossible to log in user via ms',
|
|
59
|
+
),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|