@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/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: [ 'Bearer', 'JWT' ],
28
+ schemes: ['Bearer', 'JWT'],
29
29
  ...config
30
- };
30
+ }
31
31
  }
32
32
 
33
- async handleConnection (event: ConnectionEvent, connection: any, authResult?: AuthenticationResult): Promise<void> {
34
- const isValidLogout = event === 'logout' && connection.authentication && authResult &&
35
- connection.authentication.accessToken === authResult.accessToken;
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 = (exp * 1000) - Date.now();
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 = [ 'entity', 'entityId', 'service', 'header', 'schemes' ];
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(`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?`);
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 (_params: Params) {
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 (id: string, params: Params) {
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 (authResult: AuthenticationResult, _params: Params) {
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 (authentication: AuthenticationRequest, params: Params) {
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 (req: IncomingMessage) {
151
- const { header, schemes }: { header: string, schemes: string[] } = this.configuration;
152
- const headerValue = req.headers && req.headers[header.toLowerCase()];
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 [ , scheme, schemeValue ] = headerValue.match(SPLIT_HEADER) || [];
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
- export default {
2
- authStrategies: [],
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 { AuthenticationBase, AuthenticationResult, AuthenticationRequest } from './core';
4
- import { connection, event } from './hooks';
5
- import '@feathersjs/transport-commons';
6
- import { createDebug } from '@feathersjs/commons';
7
- import { Params, ServiceMethods, ServiceAddons } from '@feathersjs/feathers';
8
- import jsonwebtoken from 'jsonwebtoken';
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
- const debug = createDebug('@feathersjs/authentication/service');
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
- interface FeathersApplication<ServiceTypes, AppSettings> { // eslint-disable-line
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? (location?: string): AuthenticationService;
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 extends AuthenticationBase implements Partial<ServiceMethods<AuthenticationResult>> {
33
- constructor (app: any, configKey = 'authentication', options = {}) {
34
- super(app, configKey, options);
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 = location || Object.keys(this.services).find(current =>
40
- this.service(current).configKey === configKey
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 (_authResult: AuthenticationResult, params: Params) {
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 (authResult: AuthenticationResult, params: Params) {
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 (data: AuthenticationRequest, params?: Params) {
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 [ payload, jwtOptions ] = await Promise.all([
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 merge({ accessToken }, authResult, {
136
+ return {
137
+ accessToken,
138
+ ...authResult,
120
139
  authentication: {
121
- accessToken,
122
- payload: jsonwebtoken.decode(accessToken)
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 (id: string | null, params?: Params) {
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('A \'secret\' must be provided in your authentication configuration');
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('The \'service\' option is not set in the authentication configuration');
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(`The '${service}' entity service does not exist (set to 'null' if it is not required)`);
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(`The '${service}' service does not have an 'id' property and no 'entityId' option is set.`);
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 (auth: AuthenticationBase) {
10
- this.authentication = auth;
9
+ setAuthentication(auth: AuthenticationBase) {
10
+ this.authentication = auth
11
11
  }
12
12
 
13
- setApplication (app: Application) {
14
- this.app = app;
13
+ setApplication(app: Application) {
14
+ this.app = app
15
15
  }
16
16
 
17
- setName (name: string) {
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 (): Service<any> {
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
  }