-
-
Notifications
You must be signed in to change notification settings - Fork 795
Expand file tree
/
Copy pathstrategy.ts
More file actions
134 lines (104 loc) · 3.74 KB
/
strategy.ts
File metadata and controls
134 lines (104 loc) · 3.74 KB
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* eslint-disable @typescript-eslint/no-unused-vars */
import bcrypt from 'bcryptjs';
import get from 'lodash/get';
import omit from 'lodash/omit';
import Debug from 'debug';
import { NotAuthenticated } from '@feathersjs/errors';
import { Query, Params } from '@feathersjs/feathers';
import {
AuthenticationRequest, AuthenticationBaseStrategy
} from '@feathersjs/authentication';
const debug = Debug('@feathersjs/authentication-local/strategy');
export class LocalStrategy extends AuthenticationBaseStrategy {
verifyConfiguration () {
const config = this.configuration;
[ 'usernameField', 'passwordField' ].forEach(prop => {
if (typeof config[prop] !== 'string') {
throw new Error(`'${this.name}' authentication strategy requires a '${prop}' setting`);
}
});
}
get configuration () {
const authConfig = this.authentication.configuration;
const config = super.configuration || {};
return {
hashSize: 10,
service: authConfig.service,
entity: authConfig.entity,
entityId: authConfig.entityId,
errorMessage: 'Invalid login',
entityPasswordField: config.passwordField,
entityUsernameField: config.usernameField,
...config
};
}
async getEntityQuery (query: Query, _params: Params) {
return {
$limit: 1,
...query
};
}
async findEntity (username: string, params: Params) {
const { entityUsernameField, errorMessage } = this.configuration;
if (!username) { // don't query for users without any condition set.
throw new NotAuthenticated(errorMessage);
}
const query = await this.getEntityQuery({
[entityUsernameField]: username
}, params);
const findParams = Object.assign({}, params, { query });
const entityService = this.entityService;
debug('Finding entity with query', params.query);
const result = await entityService.find(findParams);
const list = Array.isArray(result) ? result : result.data;
if (!Array.isArray(list) || list.length === 0) {
debug('No entity found');
throw new NotAuthenticated(errorMessage);
}
const [ entity ] = list;
return entity;
}
async getEntity (result: any, params: Params) {
const entityService = this.entityService;
const { entityId = (entityService as any).id, entity } = this.configuration;
if (!entityId || result[entityId] === undefined) {
throw new NotAuthenticated('Could not get local entity');
}
if (!params.provider) {
return result;
}
return entityService.get(result[entityId], {
...params,
[entity]: result
});
}
async comparePassword (entity: any, password: string) {
const { entityPasswordField, errorMessage } = this.configuration;
// find password in entity, this allows for dot notation
const hash = get(entity, entityPasswordField);
if (!hash) {
debug(`Record is missing the '${entityPasswordField}' password field`);
throw new NotAuthenticated(errorMessage);
}
debug('Verifying password');
const result = await bcrypt.compare(password, hash);
if (result) {
return entity;
}
throw new NotAuthenticated(errorMessage);
}
async hashPassword (password: string, _params: Params) {
return bcrypt.hash(password, this.configuration.hashSize);
}
async authenticate (data: AuthenticationRequest, params: Params) {
const { passwordField, usernameField, entity } = this.configuration;
const username = data[usernameField];
const password = data[passwordField];
const result = await this.findEntity(username, omit(params, 'provider'));
await this.comparePassword(result, password);
return {
authentication: { strategy: this.name },
[entity]: await this.getEntity(result, params)
};
}
}