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