@feathersjs/authentication 5.0.0-pre.20 → 5.0.0-pre.23
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/CHANGELOG.md +24 -0
- package/lib/core.d.ts +1 -0
- package/lib/core.js +5 -7
- package/lib/core.js.map +1 -1
- package/lib/hooks/authenticate.js +3 -2
- package/lib/hooks/authenticate.js.map +1 -1
- package/lib/hooks/connection.js.map +1 -1
- package/lib/hooks/event.js.map +1 -1
- package/lib/hooks/index.js.map +1 -1
- package/lib/index.js.map +1 -1
- package/lib/jwt.js +6 -4
- package/lib/jwt.js.map +1 -1
- package/lib/options.js +6 -3
- package/lib/options.js.map +1 -1
- package/lib/service.js +4 -3
- package/lib/service.js.map +1 -1
- package/lib/strategy.js.map +1 -1
- package/package.json +11 -11
- package/src/core.ts +107 -95
- package/src/hooks/authenticate.ts +35 -31
- package/src/hooks/connection.ts +12 -9
- package/src/hooks/event.ts +9 -9
- package/src/hooks/index.ts +3 -3
- package/src/index.ts +7 -7
- package/src/jwt.ts +84 -77
- package/src/options.ts +16 -10
- package/src/service.ts +75 -67
- package/src/strategy.ts +17 -17
package/src/jwt.ts
CHANGED
|
@@ -1,88 +1,97 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
-
import omit from 'lodash/omit'
|
|
3
|
-
import { IncomingMessage } from 'http'
|
|
4
|
-
import { NotAuthenticated } from '@feathersjs/errors'
|
|
5
|
-
import { Params } from '@feathersjs/feathers'
|
|
6
|
-
import { createDebug } from '@feathersjs/commons'
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/ban-ts-comment */
|
|
2
|
+
import omit from 'lodash/omit'
|
|
3
|
+
import { IncomingMessage } from 'http'
|
|
4
|
+
import { NotAuthenticated } from '@feathersjs/errors'
|
|
5
|
+
import { Params } from '@feathersjs/feathers'
|
|
6
|
+
import { createDebug } from '@feathersjs/commons'
|
|
7
7
|
// @ts-ignore
|
|
8
|
-
import lt from 'long-timeout'
|
|
8
|
+
import lt from 'long-timeout'
|
|
9
9
|
|
|
10
|
-
import { AuthenticationBaseStrategy } from './strategy'
|
|
11
|
-
import { AuthenticationParams, AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core'
|
|
10
|
+
import { AuthenticationBaseStrategy } from './strategy'
|
|
11
|
+
import { AuthenticationParams, AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core'
|
|
12
12
|
|
|
13
|
-
const debug = createDebug('@feathersjs/authentication/jwt')
|
|
14
|
-
const SPLIT_HEADER = /(\S+)\s+(\S+)
|
|
13
|
+
const debug = createDebug('@feathersjs/authentication/jwt')
|
|
14
|
+
const SPLIT_HEADER = /(\S+)\s+(\S+)/
|
|
15
15
|
|
|
16
16
|
export class JWTStrategy extends AuthenticationBaseStrategy {
|
|
17
|
-
expirationTimers = new WeakMap()
|
|
17
|
+
expirationTimers = new WeakMap()
|
|
18
18
|
|
|
19
|
-
get configuration
|
|
20
|
-
const authConfig = this.authentication.configuration
|
|
21
|
-
const config = super.configuration
|
|
19
|
+
get configuration() {
|
|
20
|
+
const authConfig = this.authentication.configuration
|
|
21
|
+
const config = super.configuration
|
|
22
22
|
|
|
23
23
|
return {
|
|
24
24
|
service: authConfig.service,
|
|
25
25
|
entity: authConfig.entity,
|
|
26
26
|
entityId: authConfig.entityId,
|
|
27
27
|
header: 'Authorization',
|
|
28
|
-
schemes: [
|
|
28
|
+
schemes: ['Bearer', 'JWT'],
|
|
29
29
|
...config
|
|
30
|
-
}
|
|
30
|
+
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
async handleConnection
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
async handleConnection(
|
|
34
|
+
event: ConnectionEvent,
|
|
35
|
+
connection: any,
|
|
36
|
+
authResult?: AuthenticationResult
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
const isValidLogout =
|
|
39
|
+
event === 'logout' &&
|
|
40
|
+
connection.authentication &&
|
|
41
|
+
authResult &&
|
|
42
|
+
connection.authentication.accessToken === authResult.accessToken
|
|
36
43
|
|
|
37
|
-
const { accessToken } = authResult || {}
|
|
44
|
+
const { accessToken } = authResult || {}
|
|
38
45
|
|
|
39
46
|
if (accessToken && event === 'login') {
|
|
40
|
-
debug('Adding authentication information to connection')
|
|
41
|
-
const { exp } = await this.authentication.verifyAccessToken(accessToken)
|
|
47
|
+
debug('Adding authentication information to connection')
|
|
48
|
+
const { exp } = await this.authentication.verifyAccessToken(accessToken)
|
|
42
49
|
// The time (in ms) until the token expires
|
|
43
|
-
const duration =
|
|
50
|
+
const duration = exp * 1000 - Date.now()
|
|
44
51
|
// This may have to be a `logout` event but right now we don't want
|
|
45
52
|
// the whole context object lingering around until the timer is gone
|
|
46
|
-
const timer = lt.setTimeout(() => this.app.emit('disconnect', connection), duration)
|
|
53
|
+
const timer = lt.setTimeout(() => this.app.emit('disconnect', connection), duration)
|
|
47
54
|
|
|
48
|
-
debug(`Registering connection expiration timer for ${duration}ms`)
|
|
49
|
-
lt.clearTimeout(this.expirationTimers.get(connection))
|
|
50
|
-
this.expirationTimers.set(connection, timer)
|
|
55
|
+
debug(`Registering connection expiration timer for ${duration}ms`)
|
|
56
|
+
lt.clearTimeout(this.expirationTimers.get(connection))
|
|
57
|
+
this.expirationTimers.set(connection, timer)
|
|
51
58
|
|
|
52
|
-
debug('Adding authentication information to connection')
|
|
59
|
+
debug('Adding authentication information to connection')
|
|
53
60
|
connection.authentication = {
|
|
54
61
|
strategy: this.name,
|
|
55
62
|
accessToken
|
|
56
|
-
}
|
|
63
|
+
}
|
|
57
64
|
} else if (event === 'disconnect' || isValidLogout) {
|
|
58
|
-
debug('Removing authentication information and expiration timer from connection')
|
|
65
|
+
debug('Removing authentication information and expiration timer from connection')
|
|
59
66
|
|
|
60
|
-
const { entity } = this.configuration
|
|
67
|
+
const { entity } = this.configuration
|
|
61
68
|
|
|
62
|
-
delete connection[entity]
|
|
63
|
-
delete connection.authentication
|
|
69
|
+
delete connection[entity]
|
|
70
|
+
delete connection.authentication
|
|
64
71
|
|
|
65
|
-
lt.clearTimeout(this.expirationTimers.get(connection))
|
|
66
|
-
this.expirationTimers.delete(connection)
|
|
72
|
+
lt.clearTimeout(this.expirationTimers.get(connection))
|
|
73
|
+
this.expirationTimers.delete(connection)
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
|
|
70
|
-
verifyConfiguration
|
|
71
|
-
const allowedKeys = [
|
|
77
|
+
verifyConfiguration() {
|
|
78
|
+
const allowedKeys = ['entity', 'entityId', 'service', 'header', 'schemes']
|
|
72
79
|
|
|
73
80
|
for (const key of Object.keys(this.configuration)) {
|
|
74
81
|
if (!allowedKeys.includes(key)) {
|
|
75
|
-
throw new Error(
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?`
|
|
84
|
+
)
|
|
76
85
|
}
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
if (typeof this.configuration.header !== 'string') {
|
|
80
|
-
throw new Error(`The 'header' option for the ${this.name} strategy must be a string`)
|
|
89
|
+
throw new Error(`The 'header' option for the ${this.name} strategy must be a string`)
|
|
81
90
|
}
|
|
82
91
|
}
|
|
83
92
|
|
|
84
|
-
async getEntityQuery
|
|
85
|
-
return {}
|
|
93
|
+
async getEntityQuery(_params: Params) {
|
|
94
|
+
return {}
|
|
86
95
|
}
|
|
87
96
|
|
|
88
97
|
/**
|
|
@@ -91,40 +100,40 @@ export class JWTStrategy extends AuthenticationBaseStrategy {
|
|
|
91
100
|
* @param id The id to use
|
|
92
101
|
* @param params Service call parameters
|
|
93
102
|
*/
|
|
94
|
-
async getEntity
|
|
95
|
-
const entityService = this.entityService
|
|
96
|
-
const { entity } = this.configuration
|
|
103
|
+
async getEntity(id: string, params: Params) {
|
|
104
|
+
const entityService = this.entityService
|
|
105
|
+
const { entity } = this.configuration
|
|
97
106
|
|
|
98
|
-
debug('Getting entity', id)
|
|
107
|
+
debug('Getting entity', id)
|
|
99
108
|
|
|
100
109
|
if (entityService === null) {
|
|
101
|
-
throw new NotAuthenticated('Could not find entity service')
|
|
110
|
+
throw new NotAuthenticated('Could not find entity service')
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
const query = await this.getEntityQuery(params)
|
|
105
|
-
const getParams = Object.assign({}, omit(params, 'provider'), { query })
|
|
106
|
-
const result = await entityService.get(id, getParams)
|
|
113
|
+
const query = await this.getEntityQuery(params)
|
|
114
|
+
const getParams = Object.assign({}, omit(params, 'provider'), { query })
|
|
115
|
+
const result = await entityService.get(id, getParams)
|
|
107
116
|
|
|
108
117
|
if (!params.provider) {
|
|
109
|
-
return result
|
|
118
|
+
return result
|
|
110
119
|
}
|
|
111
120
|
|
|
112
|
-
return entityService.get(id, { ...params, [entity]: result })
|
|
121
|
+
return entityService.get(id, { ...params, [entity]: result })
|
|
113
122
|
}
|
|
114
123
|
|
|
115
|
-
async getEntityId
|
|
116
|
-
return authResult.authentication.payload.sub
|
|
124
|
+
async getEntityId(authResult: AuthenticationResult, _params: Params) {
|
|
125
|
+
return authResult.authentication.payload.sub
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
async authenticate
|
|
120
|
-
const { accessToken } = authentication
|
|
121
|
-
const { entity } = this.configuration
|
|
128
|
+
async authenticate(authentication: AuthenticationRequest, params: AuthenticationParams) {
|
|
129
|
+
const { accessToken } = authentication
|
|
130
|
+
const { entity } = this.configuration
|
|
122
131
|
|
|
123
132
|
if (!accessToken) {
|
|
124
|
-
throw new NotAuthenticated('No access token')
|
|
133
|
+
throw new NotAuthenticated('No access token')
|
|
125
134
|
}
|
|
126
135
|
|
|
127
|
-
const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt)
|
|
136
|
+
const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt)
|
|
128
137
|
const result = {
|
|
129
138
|
accessToken,
|
|
130
139
|
authentication: {
|
|
@@ -132,46 +141,44 @@ export class JWTStrategy extends AuthenticationBaseStrategy {
|
|
|
132
141
|
accessToken,
|
|
133
142
|
payload
|
|
134
143
|
}
|
|
135
|
-
}
|
|
144
|
+
}
|
|
136
145
|
|
|
137
146
|
if (entity === null) {
|
|
138
|
-
return result
|
|
147
|
+
return result
|
|
139
148
|
}
|
|
140
149
|
|
|
141
|
-
const entityId = await this.getEntityId(result, params)
|
|
142
|
-
const value = await this.getEntity(entityId, params)
|
|
150
|
+
const entityId = await this.getEntityId(result, params)
|
|
151
|
+
const value = await this.getEntity(entityId, params)
|
|
143
152
|
|
|
144
153
|
return {
|
|
145
154
|
...result,
|
|
146
155
|
[entity]: value
|
|
147
|
-
}
|
|
156
|
+
}
|
|
148
157
|
}
|
|
149
158
|
|
|
150
|
-
async parse
|
|
151
|
-
strategy: string
|
|
152
|
-
accessToken: string
|
|
159
|
+
async parse(req: IncomingMessage): Promise<{
|
|
160
|
+
strategy: string
|
|
161
|
+
accessToken: string
|
|
153
162
|
} | null> {
|
|
154
|
-
const { header, schemes }: { header: string
|
|
155
|
-
const headerValue = req.headers && req.headers[header.toLowerCase()]
|
|
163
|
+
const { header, schemes }: { header: string; schemes: string[] } = this.configuration
|
|
164
|
+
const headerValue = req.headers && req.headers[header.toLowerCase()]
|
|
156
165
|
|
|
157
166
|
if (!headerValue || typeof headerValue !== 'string') {
|
|
158
|
-
return null
|
|
167
|
+
return null
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
debug('Found parsed header value')
|
|
170
|
+
debug('Found parsed header value')
|
|
162
171
|
|
|
163
|
-
const [
|
|
164
|
-
const hasScheme = scheme && schemes.some(
|
|
165
|
-
current => new RegExp(current, 'i').test(scheme)
|
|
166
|
-
);
|
|
172
|
+
const [, scheme, schemeValue] = headerValue.match(SPLIT_HEADER) || []
|
|
173
|
+
const hasScheme = scheme && schemes.some((current) => new RegExp(current, 'i').test(scheme))
|
|
167
174
|
|
|
168
175
|
if (scheme && !hasScheme) {
|
|
169
|
-
return null
|
|
176
|
+
return null
|
|
170
177
|
}
|
|
171
178
|
|
|
172
179
|
return {
|
|
173
180
|
strategy: this.name,
|
|
174
181
|
accessToken: hasScheme ? schemeValue : headerValue
|
|
175
|
-
}
|
|
182
|
+
}
|
|
176
183
|
}
|
|
177
184
|
}
|
package/src/options.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const defaultOptions = {
|
|
|
7
7
|
algorithm: 'HS256',
|
|
8
8
|
expiresIn: '1d'
|
|
9
9
|
}
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
export const authenticationSettingsSchema = {
|
|
13
13
|
type: 'object',
|
|
@@ -18,11 +18,14 @@ export const authenticationSettingsSchema = {
|
|
|
18
18
|
description: 'The JWT signing secret'
|
|
19
19
|
},
|
|
20
20
|
entity: {
|
|
21
|
-
oneOf: [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
oneOf: [
|
|
22
|
+
{
|
|
23
|
+
type: 'null'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'string'
|
|
27
|
+
}
|
|
28
|
+
],
|
|
26
29
|
description: 'The name of the authentication entity (e.g. user)'
|
|
27
30
|
},
|
|
28
31
|
entityId: {
|
|
@@ -41,7 +44,8 @@ export const authenticationSettingsSchema = {
|
|
|
41
44
|
parseStrategies: {
|
|
42
45
|
type: 'array',
|
|
43
46
|
items: { type: 'string' },
|
|
44
|
-
description:
|
|
47
|
+
description:
|
|
48
|
+
'A list of authentication strategy names that should parse HTTP headers for authentication information (defaults to `authStrategies`)'
|
|
45
49
|
},
|
|
46
50
|
jwtOptions: {
|
|
47
51
|
type: 'object'
|
|
@@ -84,11 +88,13 @@ export const authenticationSettingsSchema = {
|
|
|
84
88
|
},
|
|
85
89
|
entityUsernameField: {
|
|
86
90
|
type: 'string',
|
|
87
|
-
description:
|
|
91
|
+
description:
|
|
92
|
+
'Name of the username field on the entity if authentication request data and entity field names are different'
|
|
88
93
|
},
|
|
89
94
|
entityPasswordField: {
|
|
90
95
|
type: 'string',
|
|
91
|
-
description:
|
|
96
|
+
description:
|
|
97
|
+
'Name of the password field on the entity if authentication request data and entity field names are different'
|
|
92
98
|
}
|
|
93
99
|
}
|
|
94
100
|
},
|
|
@@ -112,4 +118,4 @@ export const authenticationSettingsSchema = {
|
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
}
|
|
115
|
-
} as const
|
|
121
|
+
} as const
|
package/src/service.ts
CHANGED
|
@@ -1,47 +1,51 @@
|
|
|
1
|
-
import merge from 'lodash/merge'
|
|
2
|
-
import { NotAuthenticated } from '@feathersjs/errors'
|
|
3
|
-
import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'
|
|
4
|
-
import { connection, event } from './hooks'
|
|
5
|
-
import '@feathersjs/transport-commons'
|
|
6
|
-
import { createDebug } from '@feathersjs/commons'
|
|
7
|
-
import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers'
|
|
8
|
-
import jsonwebtoken from 'jsonwebtoken'
|
|
1
|
+
import merge from 'lodash/merge'
|
|
2
|
+
import { NotAuthenticated } from '@feathersjs/errors'
|
|
3
|
+
import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'
|
|
4
|
+
import { connection, event } from './hooks'
|
|
5
|
+
import '@feathersjs/transport-commons'
|
|
6
|
+
import { createDebug } from '@feathersjs/commons'
|
|
7
|
+
import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers'
|
|
8
|
+
import jsonwebtoken from 'jsonwebtoken'
|
|
9
9
|
|
|
10
|
-
const debug = createDebug('@feathersjs/authentication/service')
|
|
10
|
+
const debug = createDebug('@feathersjs/authentication/service')
|
|
11
11
|
|
|
12
12
|
declare module '@feathersjs/feathers/lib/declarations' {
|
|
13
|
-
interface FeathersApplication<Services, Settings> {
|
|
13
|
+
interface FeathersApplication<Services, Settings> {
|
|
14
|
+
// eslint-disable-line
|
|
14
15
|
/**
|
|
15
16
|
* Returns the default authentication service or the
|
|
16
17
|
* authentication service for a given path.
|
|
17
18
|
*
|
|
18
19
|
* @param location The service path to use (optional)
|
|
19
20
|
*/
|
|
20
|
-
defaultAuthentication?
|
|
21
|
+
defaultAuthentication?(location?: string): AuthenticationService
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
interface Params {
|
|
24
|
-
authenticated?: boolean
|
|
25
|
-
authentication?: AuthenticationRequest
|
|
25
|
+
authenticated?: boolean
|
|
26
|
+
authentication?: AuthenticationRequest
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
// eslint-disable-next-line
|
|
30
31
|
export interface AuthenticationService extends ServiceAddons<AuthenticationResult, AuthenticationResult> {}
|
|
31
32
|
|
|
32
|
-
export class AuthenticationService
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
export class AuthenticationService
|
|
34
|
+
extends AuthenticationBase
|
|
35
|
+
implements Partial<ServiceMethods<AuthenticationResult, AuthenticationRequest, AuthenticationParams>>
|
|
36
|
+
{
|
|
37
|
+
constructor(app: any, configKey = 'authentication', options = {}) {
|
|
38
|
+
super(app, configKey, options)
|
|
35
39
|
|
|
36
40
|
if (typeof app.defaultAuthentication !== 'function') {
|
|
37
41
|
app.defaultAuthentication = function (location?: string) {
|
|
38
|
-
const configKey = app.get('defaultAuthentication')
|
|
39
|
-
const path =
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const configKey = app.get('defaultAuthentication')
|
|
43
|
+
const path =
|
|
44
|
+
location ||
|
|
45
|
+
Object.keys(this.services).find((current) => this.service(current).configKey === configKey)
|
|
42
46
|
|
|
43
|
-
return path ? this.service(path) : null
|
|
44
|
-
}
|
|
47
|
+
return path ? this.service(path) : null
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
@@ -51,11 +55,11 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
51
55
|
* @param _authResult The current authentication result
|
|
52
56
|
* @param params The service call parameters
|
|
53
57
|
*/
|
|
54
|
-
async getPayload
|
|
58
|
+
async getPayload(_authResult: AuthenticationResult, params: AuthenticationParams) {
|
|
55
59
|
// Uses `params.payload` or returns an empty payload
|
|
56
|
-
const { payload = {} } = params
|
|
60
|
+
const { payload = {} } = params
|
|
57
61
|
|
|
58
|
-
return payload
|
|
62
|
+
return payload
|
|
59
63
|
}
|
|
60
64
|
|
|
61
65
|
/**
|
|
@@ -65,24 +69,24 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
65
69
|
* @param authResult The authentication result
|
|
66
70
|
* @param params Service call parameters
|
|
67
71
|
*/
|
|
68
|
-
async getTokenOptions
|
|
69
|
-
const { service, entity, entityId } = this.configuration
|
|
70
|
-
const jwtOptions = merge({}, params.jwtOptions, params.jwt)
|
|
71
|
-
const value = service && entity && authResult[entity]
|
|
72
|
+
async getTokenOptions(authResult: AuthenticationResult, params: AuthenticationParams) {
|
|
73
|
+
const { service, entity, entityId } = this.configuration
|
|
74
|
+
const jwtOptions = merge({}, params.jwtOptions, params.jwt)
|
|
75
|
+
const value = service && entity && authResult[entity]
|
|
72
76
|
|
|
73
77
|
// Set the subject to the entity id if it is available
|
|
74
78
|
if (value && !jwtOptions.subject) {
|
|
75
|
-
const idProperty = entityId || this.app.service(service).id
|
|
76
|
-
const subject = value[idProperty]
|
|
79
|
+
const idProperty = entityId || this.app.service(service).id
|
|
80
|
+
const subject = value[idProperty]
|
|
77
81
|
|
|
78
82
|
if (subject === undefined) {
|
|
79
|
-
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`)
|
|
83
|
+
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`)
|
|
80
84
|
}
|
|
81
85
|
|
|
82
|
-
jwtOptions.subject = `${subject}
|
|
86
|
+
jwtOptions.subject = `${subject}`
|
|
83
87
|
}
|
|
84
88
|
|
|
85
|
-
return jwtOptions
|
|
89
|
+
return jwtOptions
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/**
|
|
@@ -92,36 +96,36 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
92
96
|
* @param data The authentication request (should include `strategy` key)
|
|
93
97
|
* @param params Service call parameters
|
|
94
98
|
*/
|
|
95
|
-
async create
|
|
96
|
-
const authStrategies = params.authStrategies || this.configuration.authStrategies
|
|
99
|
+
async create(data: AuthenticationRequest, params?: AuthenticationParams) {
|
|
100
|
+
const authStrategies = params.authStrategies || this.configuration.authStrategies
|
|
97
101
|
|
|
98
102
|
if (!authStrategies.length) {
|
|
99
|
-
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)')
|
|
103
|
+
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)')
|
|
100
104
|
}
|
|
101
105
|
|
|
102
|
-
const authResult = await this.authenticate(data, params, ...authStrategies)
|
|
106
|
+
const authResult = await this.authenticate(data, params, ...authStrategies)
|
|
103
107
|
|
|
104
|
-
debug('Got authentication result', authResult)
|
|
108
|
+
debug('Got authentication result', authResult)
|
|
105
109
|
|
|
106
110
|
if (authResult.accessToken) {
|
|
107
|
-
return authResult
|
|
111
|
+
return authResult
|
|
108
112
|
}
|
|
109
113
|
|
|
110
|
-
const [
|
|
114
|
+
const [payload, jwtOptions] = await Promise.all([
|
|
111
115
|
this.getPayload(authResult, params),
|
|
112
116
|
this.getTokenOptions(authResult, params)
|
|
113
|
-
])
|
|
117
|
+
])
|
|
114
118
|
|
|
115
|
-
debug('Creating JWT with', payload, jwtOptions)
|
|
119
|
+
debug('Creating JWT with', payload, jwtOptions)
|
|
116
120
|
|
|
117
|
-
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret)
|
|
121
|
+
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret)
|
|
118
122
|
|
|
119
123
|
return merge({ accessToken }, authResult, {
|
|
120
124
|
authentication: {
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
accessToken,
|
|
126
|
+
payload: jsonwebtoken.decode(accessToken)
|
|
123
127
|
}
|
|
124
|
-
})
|
|
128
|
+
})
|
|
125
129
|
}
|
|
126
130
|
|
|
127
131
|
/**
|
|
@@ -131,59 +135,63 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
131
135
|
* @param id The JWT to remove or null
|
|
132
136
|
* @param params Service call parameters
|
|
133
137
|
*/
|
|
134
|
-
async remove
|
|
135
|
-
const { authentication } = params
|
|
136
|
-
const { authStrategies } = this.configuration
|
|
138
|
+
async remove(id: string | null, params?: AuthenticationParams) {
|
|
139
|
+
const { authentication } = params
|
|
140
|
+
const { authStrategies } = this.configuration
|
|
137
141
|
|
|
138
142
|
// When an id is passed it is expected to be the authentication `accessToken`
|
|
139
143
|
if (id !== null && id !== authentication.accessToken) {
|
|
140
|
-
throw new NotAuthenticated('Invalid access token')
|
|
144
|
+
throw new NotAuthenticated('Invalid access token')
|
|
141
145
|
}
|
|
142
146
|
|
|
143
|
-
debug('Verifying authentication strategy in remove')
|
|
147
|
+
debug('Verifying authentication strategy in remove')
|
|
144
148
|
|
|
145
|
-
return this.authenticate(authentication, params, ...authStrategies)
|
|
149
|
+
return this.authenticate(authentication, params, ...authStrategies)
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
/**
|
|
149
153
|
* Validates the service configuration.
|
|
150
154
|
*/
|
|
151
|
-
async setup
|
|
152
|
-
await super.setup()
|
|
155
|
+
async setup() {
|
|
156
|
+
await super.setup()
|
|
153
157
|
|
|
154
158
|
// The setup method checks for valid settings and registers the
|
|
155
159
|
// connection and event (login, logout) hooks
|
|
156
|
-
const { secret, service, entity, entityId } = this.configuration
|
|
160
|
+
const { secret, service, entity, entityId } = this.configuration
|
|
157
161
|
|
|
158
162
|
if (typeof secret !== 'string') {
|
|
159
|
-
throw new Error(
|
|
163
|
+
throw new Error("A 'secret' must be provided in your authentication configuration")
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
if (entity !== null) {
|
|
163
167
|
if (service === undefined) {
|
|
164
|
-
throw new Error(
|
|
168
|
+
throw new Error("The 'service' option is not set in the authentication configuration")
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
if (this.app.service(service) === undefined) {
|
|
168
|
-
throw new Error(
|
|
172
|
+
throw new Error(
|
|
173
|
+
`The '${service}' entity service does not exist (set to 'null' if it is not required)`
|
|
174
|
+
)
|
|
169
175
|
}
|
|
170
176
|
|
|
171
177
|
if (this.app.service(service).id === undefined && entityId === undefined) {
|
|
172
|
-
throw new Error(
|
|
178
|
+
throw new Error(
|
|
179
|
+
`The '${service}' service does not have an 'id' property and no 'entityId' option is set.`
|
|
180
|
+
)
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
|
|
176
|
-
|
|
177
|
-
create: [
|
|
178
|
-
remove: [
|
|
179
|
-
})
|
|
184
|
+
this.hooks({
|
|
185
|
+
create: [connection('login'), event('login')],
|
|
186
|
+
remove: [connection('logout'), event('logout')]
|
|
187
|
+
} as any)
|
|
180
188
|
|
|
181
189
|
this.app.on('disconnect', async (connection) => {
|
|
182
|
-
await this.handleConnection('disconnect', connection)
|
|
183
|
-
})
|
|
190
|
+
await this.handleConnection('disconnect', connection)
|
|
191
|
+
})
|
|
184
192
|
|
|
185
193
|
if (typeof this.publish === 'function') {
|
|
186
|
-
this.publish(() => null)
|
|
194
|
+
this.publish(() => null)
|
|
187
195
|
}
|
|
188
196
|
}
|
|
189
197
|
}
|
package/src/strategy.ts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
import { AuthenticationStrategy, AuthenticationBase } from './core'
|
|
2
|
-
import { Application, Service } from '@feathersjs/feathers'
|
|
1
|
+
import { AuthenticationStrategy, AuthenticationBase } from './core'
|
|
2
|
+
import { Application, Service } from '@feathersjs/feathers'
|
|
3
3
|
|
|
4
4
|
export class AuthenticationBaseStrategy implements AuthenticationStrategy {
|
|
5
|
-
authentication?: AuthenticationBase
|
|
6
|
-
app?: Application
|
|
7
|
-
name?: string
|
|
5
|
+
authentication?: AuthenticationBase
|
|
6
|
+
app?: Application
|
|
7
|
+
name?: string
|
|
8
8
|
|
|
9
|
-
setAuthentication
|
|
10
|
-
this.authentication = auth
|
|
9
|
+
setAuthentication(auth: AuthenticationBase) {
|
|
10
|
+
this.authentication = auth
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
setApplication
|
|
14
|
-
this.app = app
|
|
13
|
+
setApplication(app: Application) {
|
|
14
|
+
this.app = app
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
setName
|
|
18
|
-
this.name = name
|
|
17
|
+
setName(name: string) {
|
|
18
|
+
this.name = name
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
get configuration
|
|
22
|
-
return this.authentication.configuration[this.name]
|
|
21
|
+
get configuration() {
|
|
22
|
+
return this.authentication.configuration[this.name]
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
get entityService
|
|
26
|
-
const { service } = this.configuration
|
|
25
|
+
get entityService(): Service {
|
|
26
|
+
const { service } = this.configuration
|
|
27
27
|
|
|
28
28
|
if (!service) {
|
|
29
|
-
return null
|
|
29
|
+
return null
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
return this.app.service(service) || null
|
|
32
|
+
return this.app.service(service) || null
|
|
33
33
|
}
|
|
34
34
|
}
|