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