-
Notifications
You must be signed in to change notification settings - Fork 112
/
auth.guard.ts
110 lines (100 loc) Β· 3.39 KB
/
auth.guard.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
CanActivate,
ExecutionContext,
Logger,
mixin,
Optional,
UnauthorizedException
} from '@nestjs/common';
import * as passport from 'passport';
import { Type } from './interfaces';
import {
AuthModuleOptions,
IAuthModuleOptions
} from './interfaces/auth-module.options';
import { defaultOptions } from './options';
import { memoize } from './utils/memoize.util';
export type IAuthGuard = CanActivate & {
logIn<TRequest extends { logIn: Function } = any>(
request: TRequest
): Promise<void>;
handleRequest<TUser = any>(err, user, info, context, status?): TUser;
getAuthenticateOptions(context): IAuthModuleOptions | undefined;
};
export const AuthGuard: (
type?: string | string[]
) => Type<IAuthGuard> = memoize(createAuthGuard);
const NO_STRATEGY_ERROR = `In order to use "defaultStrategy", please, ensure to import PassportModule in each place where AuthGuard() is being used. Otherwise, passport won't work correctly.`;
function createAuthGuard(type?: string | string[]): Type<CanActivate> {
class MixinAuthGuard<TUser = any> implements CanActivate {
constructor(@Optional() protected readonly options?: AuthModuleOptions) {
this.options = this.options || {};
if (!type && !this.options.defaultStrategy) {
new Logger('AuthGuard').error(NO_STRATEGY_ERROR);
}
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const options = {
...defaultOptions,
...this.options,
...await this.getAuthenticateOptions(context)
};
const [request, response] = [
this.getRequest(context),
this.getResponse(context)
];
const passportFn = createPassportContext(request, response);
const user = await passportFn(
type || this.options.defaultStrategy,
options,
(err, user, info, status) =>
this.handleRequest(err, user, info, context, status)
);
request[options.property || defaultOptions.property] = user;
return true;
}
getRequest<T = any>(context: ExecutionContext): T {
return context.switchToHttp().getRequest();
}
getResponse<T = any>(context: ExecutionContext): T {
return context.switchToHttp().getResponse();
}
async logIn<TRequest extends { logIn: Function } = any>(
request: TRequest
): Promise<void> {
const user = request[this.options.property || defaultOptions.property];
await new Promise<void>((resolve, reject) =>
request.logIn(user, (err) => (err ? reject(err) : resolve()))
);
}
handleRequest(err, user, info, context, status): TUser {
if (err || !user) {
throw err || new UnauthorizedException();
}
return user;
}
getAuthenticateOptions(
context: ExecutionContext
): Promise<IAuthModuleOptions> | IAuthModuleOptions | undefined {
return undefined;
}
}
const guard = mixin(MixinAuthGuard);
return guard;
}
const createPassportContext = (request, response) => (
type,
options,
callback: Function
) =>
new Promise<void>((resolve, reject) =>
passport.authenticate(type, options, (err, user, info, status) => {
try {
request.authInfo = info;
return resolve(callback(err, user, info, status));
} catch (err) {
reject(err);
}
})(request, response, (err) => (err ? reject(err) : resolve()))
);