@feathersjs/authentication 5.0.0-pre.22 → 5.0.0-pre.25
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 +27 -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 +12 -8
- 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 +80 -69
- 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,52 @@
|
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
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 { resolveDispatch } from '@feathersjs/schema'
|
|
9
|
+
import jsonwebtoken from 'jsonwebtoken'
|
|
10
|
+
|
|
11
|
+
const debug = createDebug('@feathersjs/authentication/service')
|
|
11
12
|
|
|
12
13
|
declare module '@feathersjs/feathers/lib/declarations' {
|
|
13
|
-
interface FeathersApplication<Services, Settings> {
|
|
14
|
+
interface FeathersApplication<Services, Settings> {
|
|
15
|
+
// eslint-disable-line
|
|
14
16
|
/**
|
|
15
17
|
* Returns the default authentication service or the
|
|
16
18
|
* authentication service for a given path.
|
|
17
19
|
*
|
|
18
20
|
* @param location The service path to use (optional)
|
|
19
21
|
*/
|
|
20
|
-
defaultAuthentication?
|
|
22
|
+
defaultAuthentication?(location?: string): AuthenticationService
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
interface Params {
|
|
24
|
-
authenticated?: boolean
|
|
25
|
-
authentication?: AuthenticationRequest
|
|
26
|
+
authenticated?: boolean
|
|
27
|
+
authentication?: AuthenticationRequest
|
|
26
28
|
}
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
// eslint-disable-next-line
|
|
30
32
|
export interface AuthenticationService extends ServiceAddons<AuthenticationResult, AuthenticationResult> {}
|
|
31
33
|
|
|
32
|
-
export class AuthenticationService
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
export class AuthenticationService
|
|
35
|
+
extends AuthenticationBase
|
|
36
|
+
implements Partial<ServiceMethods<AuthenticationResult, AuthenticationRequest, AuthenticationParams>>
|
|
37
|
+
{
|
|
38
|
+
constructor(app: any, configKey = 'authentication', options = {}) {
|
|
39
|
+
super(app, configKey, options)
|
|
35
40
|
|
|
36
41
|
if (typeof app.defaultAuthentication !== 'function') {
|
|
37
42
|
app.defaultAuthentication = function (location?: string) {
|
|
38
|
-
const configKey = app.get('defaultAuthentication')
|
|
39
|
-
const path =
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
const configKey = app.get('defaultAuthentication')
|
|
44
|
+
const path =
|
|
45
|
+
location ||
|
|
46
|
+
Object.keys(this.services).find((current) => this.service(current).configKey === configKey)
|
|
42
47
|
|
|
43
|
-
return path ? this.service(path) : null
|
|
44
|
-
}
|
|
48
|
+
return path ? this.service(path) : null
|
|
49
|
+
}
|
|
45
50
|
}
|
|
46
51
|
}
|
|
47
52
|
/**
|
|
@@ -51,11 +56,11 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
51
56
|
* @param _authResult The current authentication result
|
|
52
57
|
* @param params The service call parameters
|
|
53
58
|
*/
|
|
54
|
-
async getPayload
|
|
59
|
+
async getPayload(_authResult: AuthenticationResult, params: AuthenticationParams) {
|
|
55
60
|
// Uses `params.payload` or returns an empty payload
|
|
56
|
-
const { payload = {} } = params
|
|
61
|
+
const { payload = {} } = params
|
|
57
62
|
|
|
58
|
-
return payload
|
|
63
|
+
return payload
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/**
|
|
@@ -65,24 +70,24 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
65
70
|
* @param authResult The authentication result
|
|
66
71
|
* @param params Service call parameters
|
|
67
72
|
*/
|
|
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]
|
|
73
|
+
async getTokenOptions(authResult: AuthenticationResult, params: AuthenticationParams) {
|
|
74
|
+
const { service, entity, entityId } = this.configuration
|
|
75
|
+
const jwtOptions = merge({}, params.jwtOptions, params.jwt)
|
|
76
|
+
const value = service && entity && authResult[entity]
|
|
72
77
|
|
|
73
78
|
// Set the subject to the entity id if it is available
|
|
74
79
|
if (value && !jwtOptions.subject) {
|
|
75
|
-
const idProperty = entityId || this.app.service(service).id
|
|
76
|
-
const subject = value[idProperty]
|
|
80
|
+
const idProperty = entityId || this.app.service(service).id
|
|
81
|
+
const subject = value[idProperty]
|
|
77
82
|
|
|
78
83
|
if (subject === undefined) {
|
|
79
|
-
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`)
|
|
84
|
+
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`)
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
jwtOptions.subject = `${subject}
|
|
87
|
+
jwtOptions.subject = `${subject}`
|
|
83
88
|
}
|
|
84
89
|
|
|
85
|
-
return jwtOptions
|
|
90
|
+
return jwtOptions
|
|
86
91
|
}
|
|
87
92
|
|
|
88
93
|
/**
|
|
@@ -92,36 +97,38 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
92
97
|
* @param data The authentication request (should include `strategy` key)
|
|
93
98
|
* @param params Service call parameters
|
|
94
99
|
*/
|
|
95
|
-
async create
|
|
96
|
-
const authStrategies = params.authStrategies || this.configuration.authStrategies
|
|
100
|
+
async create(data: AuthenticationRequest, params?: AuthenticationParams) {
|
|
101
|
+
const authStrategies = params.authStrategies || this.configuration.authStrategies
|
|
97
102
|
|
|
98
103
|
if (!authStrategies.length) {
|
|
99
|
-
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)')
|
|
104
|
+
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)')
|
|
100
105
|
}
|
|
101
106
|
|
|
102
|
-
const authResult = await this.authenticate(data, params, ...authStrategies)
|
|
107
|
+
const authResult = await this.authenticate(data, params, ...authStrategies)
|
|
103
108
|
|
|
104
|
-
debug('Got authentication result', authResult)
|
|
109
|
+
debug('Got authentication result', authResult)
|
|
105
110
|
|
|
106
111
|
if (authResult.accessToken) {
|
|
107
|
-
return authResult
|
|
112
|
+
return authResult
|
|
108
113
|
}
|
|
109
114
|
|
|
110
|
-
const [
|
|
115
|
+
const [payload, jwtOptions] = await Promise.all([
|
|
111
116
|
this.getPayload(authResult, params),
|
|
112
117
|
this.getTokenOptions(authResult, params)
|
|
113
|
-
])
|
|
118
|
+
])
|
|
114
119
|
|
|
115
|
-
debug('Creating JWT with', payload, jwtOptions)
|
|
120
|
+
debug('Creating JWT with', payload, jwtOptions)
|
|
116
121
|
|
|
117
|
-
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret)
|
|
122
|
+
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret)
|
|
118
123
|
|
|
119
|
-
return
|
|
124
|
+
return {
|
|
125
|
+
accessToken,
|
|
126
|
+
...authResult,
|
|
120
127
|
authentication: {
|
|
121
|
-
|
|
122
|
-
|
|
128
|
+
...authResult.authentication,
|
|
129
|
+
payload: jsonwebtoken.decode(accessToken)
|
|
123
130
|
}
|
|
124
|
-
}
|
|
131
|
+
}
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
/**
|
|
@@ -131,59 +138,63 @@ export class AuthenticationService extends AuthenticationBase implements Partial
|
|
|
131
138
|
* @param id The JWT to remove or null
|
|
132
139
|
* @param params Service call parameters
|
|
133
140
|
*/
|
|
134
|
-
async remove
|
|
135
|
-
const { authentication } = params
|
|
136
|
-
const { authStrategies } = this.configuration
|
|
141
|
+
async remove(id: string | null, params?: AuthenticationParams) {
|
|
142
|
+
const { authentication } = params
|
|
143
|
+
const { authStrategies } = this.configuration
|
|
137
144
|
|
|
138
145
|
// When an id is passed it is expected to be the authentication `accessToken`
|
|
139
146
|
if (id !== null && id !== authentication.accessToken) {
|
|
140
|
-
throw new NotAuthenticated('Invalid access token')
|
|
147
|
+
throw new NotAuthenticated('Invalid access token')
|
|
141
148
|
}
|
|
142
149
|
|
|
143
|
-
debug('Verifying authentication strategy in remove')
|
|
150
|
+
debug('Verifying authentication strategy in remove')
|
|
144
151
|
|
|
145
|
-
return this.authenticate(authentication, params, ...authStrategies)
|
|
152
|
+
return this.authenticate(authentication, params, ...authStrategies)
|
|
146
153
|
}
|
|
147
154
|
|
|
148
155
|
/**
|
|
149
156
|
* Validates the service configuration.
|
|
150
157
|
*/
|
|
151
|
-
async setup
|
|
152
|
-
await super.setup()
|
|
158
|
+
async setup() {
|
|
159
|
+
await super.setup()
|
|
153
160
|
|
|
154
161
|
// The setup method checks for valid settings and registers the
|
|
155
162
|
// connection and event (login, logout) hooks
|
|
156
|
-
const { secret, service, entity, entityId } = this.configuration
|
|
163
|
+
const { secret, service, entity, entityId } = this.configuration
|
|
157
164
|
|
|
158
165
|
if (typeof secret !== 'string') {
|
|
159
|
-
throw new Error(
|
|
166
|
+
throw new Error("A 'secret' must be provided in your authentication configuration")
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
if (entity !== null) {
|
|
163
170
|
if (service === undefined) {
|
|
164
|
-
throw new Error(
|
|
171
|
+
throw new Error("The 'service' option is not set in the authentication configuration")
|
|
165
172
|
}
|
|
166
173
|
|
|
167
174
|
if (this.app.service(service) === undefined) {
|
|
168
|
-
throw new Error(
|
|
175
|
+
throw new Error(
|
|
176
|
+
`The '${service}' entity service does not exist (set to 'null' if it is not required)`
|
|
177
|
+
)
|
|
169
178
|
}
|
|
170
179
|
|
|
171
180
|
if (this.app.service(service).id === undefined && entityId === undefined) {
|
|
172
|
-
throw new Error(
|
|
181
|
+
throw new Error(
|
|
182
|
+
`The '${service}' service does not have an 'id' property and no 'entityId' option is set.`
|
|
183
|
+
)
|
|
173
184
|
}
|
|
174
185
|
}
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
create: [ connection('login'), event('login')
|
|
178
|
-
remove: [ connection('logout'), event('logout')
|
|
179
|
-
})
|
|
187
|
+
this.hooks({
|
|
188
|
+
create: [resolveDispatch(), connection('login'), event('login')],
|
|
189
|
+
remove: [resolveDispatch(), connection('logout'), event('logout')]
|
|
190
|
+
} as any)
|
|
180
191
|
|
|
181
192
|
this.app.on('disconnect', async (connection) => {
|
|
182
|
-
await this.handleConnection('disconnect', connection)
|
|
183
|
-
})
|
|
193
|
+
await this.handleConnection('disconnect', connection)
|
|
194
|
+
})
|
|
184
195
|
|
|
185
196
|
if (typeof this.publish === 'function') {
|
|
186
|
-
this.publish(() => null)
|
|
197
|
+
this.publish(() => null)
|
|
187
198
|
}
|
|
188
199
|
}
|
|
189
200
|
}
|
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
|
}
|