@feathersjs/authentication
Advanced tools
Comparing version 5.0.0-pre.22 to 5.0.0-pre.23
/// <reference types="node" /> | ||
/// <reference types="node" /> | ||
import { SignOptions, Secret, VerifyOptions } from 'jsonwebtoken'; | ||
@@ -3,0 +4,0 @@ import { Application, Params } from '@feathersjs/feathers'; |
@@ -82,4 +82,3 @@ "use strict"; | ||
getStrategies(...names) { | ||
return names.map(name => this.strategies[name]) | ||
.filter(current => !!current); | ||
return names.map((name) => this.strategies[name]).filter((current) => !!current); | ||
} | ||
@@ -153,3 +152,4 @@ /** | ||
const additionalInfo = (!strategy && ' (no `strategy` set)') || | ||
(!strategyAllowed && ' (strategy not allowed in authStrategies)') || ''; | ||
(!strategyAllowed && ' (strategy not allowed in authStrategies)') || | ||
''; | ||
// If there are no valid strategies or `authentication` is not an object | ||
@@ -164,4 +164,3 @@ throw new errors_1.NotAuthenticated('Invalid authentication information' + additionalInfo); | ||
async handleConnection(event, connection, authResult) { | ||
const strategies = this.getStrategies(...Object.keys(this.strategies)) | ||
.filter(current => typeof current.handleConnection === 'function'); | ||
const strategies = this.getStrategies(...Object.keys(this.strategies)).filter((current) => typeof current.handleConnection === 'function'); | ||
for (const strategy of strategies) { | ||
@@ -179,4 +178,3 @@ await strategy.handleConnection(event, connection, authResult); | ||
async parse(req, res, ...names) { | ||
const strategies = this.getStrategies(...names) | ||
.filter(current => typeof current.parse === 'function'); | ||
const strategies = this.getStrategies(...names).filter((current) => typeof current.parse === 'function'); | ||
debug('Strategies parsing HTTP header for authentication information', names); | ||
@@ -183,0 +181,0 @@ for (const authStrategy of strategies) { |
@@ -31,3 +31,2 @@ "use strict"; | ||
} | ||
// @ts-ignore | ||
if (service === authService) { | ||
@@ -43,3 +42,5 @@ throw new errors_1.NotAuthenticated('The authenticate hook does not need to be used on the authentication service'); | ||
const authResult = await authService.authenticate(authentication, authParams, ...strategies); | ||
context.params = Object.assign({}, params, (0, omit_1.default)(authResult, 'accessToken'), { authenticated: true }); | ||
context.params = Object.assign({}, params, (0, omit_1.default)(authResult, 'accessToken'), { | ||
authenticated: true | ||
}); | ||
} | ||
@@ -46,0 +47,0 @@ else if (provider) { |
@@ -7,3 +7,3 @@ "use strict"; | ||
exports.JWTStrategy = void 0; | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/ban-ts-comment */ | ||
const omit_1 = __importDefault(require("lodash/omit")); | ||
@@ -35,3 +35,5 @@ const errors_1 = require("@feathersjs/errors"); | ||
async handleConnection(event, connection, authResult) { | ||
const isValidLogout = event === 'logout' && connection.authentication && authResult && | ||
const isValidLogout = event === 'logout' && | ||
connection.authentication && | ||
authResult && | ||
connection.authentication.accessToken === authResult.accessToken; | ||
@@ -43,3 +45,3 @@ const { accessToken } = authResult || {}; | ||
// The time (in ms) until the token expires | ||
const duration = (exp * 1000) - Date.now(); | ||
const duration = exp * 1000 - Date.now(); | ||
// This may have to be a `logout` event but right now we don't want | ||
@@ -137,3 +139,3 @@ // the whole context object lingering around until the timer is gone | ||
const [, scheme, schemeValue] = headerValue.match(SPLIT_HEADER) || []; | ||
const hasScheme = scheme && schemes.some(current => new RegExp(current, 'i').test(scheme)); | ||
const hasScheme = scheme && schemes.some((current) => new RegExp(current, 'i').test(scheme)); | ||
if (scheme && !hasScheme) { | ||
@@ -140,0 +142,0 @@ return null; |
@@ -23,7 +23,10 @@ "use strict"; | ||
entity: { | ||
oneOf: [{ | ||
oneOf: [ | ||
{ | ||
type: 'null' | ||
}, { | ||
}, | ||
{ | ||
type: 'string' | ||
}], | ||
} | ||
], | ||
description: 'The name of the authentication entity (e.g. user)' | ||
@@ -30,0 +33,0 @@ }, |
@@ -21,3 +21,4 @@ "use strict"; | ||
const configKey = app.get('defaultAuthentication'); | ||
const path = location || Object.keys(this.services).find(current => this.service(current).configKey === configKey); | ||
const path = location || | ||
Object.keys(this.services).find((current) => this.service(current).configKey === configKey); | ||
return path ? this.service(path) : null; | ||
@@ -117,7 +118,7 @@ }; | ||
if (typeof secret !== 'string') { | ||
throw new Error('A \'secret\' must be provided in your authentication configuration'); | ||
throw new Error("A 'secret' must be provided in your authentication configuration"); | ||
} | ||
if (entity !== null) { | ||
if (service === undefined) { | ||
throw new Error('The \'service\' option is not set in the authentication configuration'); | ||
throw new Error("The 'service' option is not set in the authentication configuration"); | ||
} | ||
@@ -124,0 +125,0 @@ if (this.app.service(service) === undefined) { |
{ | ||
"name": "@feathersjs/authentication", | ||
"description": "Add Authentication to your FeathersJS app.", | ||
"version": "5.0.0-pre.22", | ||
"version": "5.0.0-pre.23", | ||
"homepage": "https://feathersjs.com", | ||
@@ -55,6 +55,6 @@ "main": "lib/", | ||
"dependencies": { | ||
"@feathersjs/commons": "^5.0.0-pre.22", | ||
"@feathersjs/errors": "^5.0.0-pre.22", | ||
"@feathersjs/feathers": "^5.0.0-pre.22", | ||
"@feathersjs/transport-commons": "^5.0.0-pre.22", | ||
"@feathersjs/commons": "^5.0.0-pre.23", | ||
"@feathersjs/errors": "^5.0.0-pre.23", | ||
"@feathersjs/feathers": "^5.0.0-pre.23", | ||
"@feathersjs/transport-commons": "^5.0.0-pre.23", | ||
"@types/jsonwebtoken": "^8.5.8", | ||
@@ -67,14 +67,14 @@ "jsonwebtoken": "^8.5.1", | ||
"devDependencies": { | ||
"@feathersjs/memory": "^5.0.0-pre.22", | ||
"@feathersjs/schema": "^5.0.0-pre.22", | ||
"@feathersjs/memory": "^5.0.0-pre.23", | ||
"@feathersjs/schema": "^5.0.0-pre.23", | ||
"@types/lodash": "^4.14.182", | ||
"@types/mocha": "^9.1.1", | ||
"@types/node": "^17.0.31", | ||
"@types/node": "^17.0.40", | ||
"@types/uuid": "^8.3.4", | ||
"mocha": "^10.0.0", | ||
"shx": "^0.3.4", | ||
"ts-node": "^10.7.0", | ||
"typescript": "^4.6.4" | ||
"ts-node": "^10.8.1", | ||
"typescript": "^4.7.3" | ||
}, | ||
"gitHead": "e452e02063e6d8943a9cae2315ab585bc4f82fb6" | ||
"gitHead": "a60910bd730b88053ca6648337095f1ca1e3b39f" | ||
} |
202
src/core.ts
@@ -1,30 +0,30 @@ | ||
import merge from 'lodash/merge'; | ||
import jsonwebtoken, { SignOptions, Secret, VerifyOptions } from 'jsonwebtoken'; | ||
import { v4 as uuidv4 } from 'uuid'; | ||
import { NotAuthenticated } from '@feathersjs/errors'; | ||
import { createDebug } from '@feathersjs/commons'; | ||
import { Application, Params } from '@feathersjs/feathers'; | ||
import { IncomingMessage, ServerResponse } from 'http'; | ||
import { defaultOptions } from './options'; | ||
import merge from 'lodash/merge' | ||
import jsonwebtoken, { SignOptions, Secret, VerifyOptions } from 'jsonwebtoken' | ||
import { v4 as uuidv4 } from 'uuid' | ||
import { NotAuthenticated } from '@feathersjs/errors' | ||
import { createDebug } from '@feathersjs/commons' | ||
import { Application, Params } from '@feathersjs/feathers' | ||
import { IncomingMessage, ServerResponse } from 'http' | ||
import { defaultOptions } from './options' | ||
const debug = createDebug('@feathersjs/authentication/base'); | ||
const debug = createDebug('@feathersjs/authentication/base') | ||
export interface AuthenticationResult { | ||
[key: string]: any; | ||
[key: string]: any | ||
} | ||
export interface AuthenticationRequest { | ||
strategy?: string; | ||
[key: string]: any; | ||
strategy?: string | ||
[key: string]: any | ||
} | ||
export interface AuthenticationParams extends Params { | ||
payload?: { [key: string]: any }; | ||
jwtOptions?: SignOptions; | ||
authStrategies?: string[]; | ||
secret?: string; | ||
[key: string]: any; | ||
payload?: { [key: string]: any } | ||
jwtOptions?: SignOptions | ||
authStrategies?: string[] | ||
secret?: string | ||
[key: string]: any | ||
} | ||
export type ConnectionEvent = 'login' | 'logout' | 'disconnect'; | ||
export type ConnectionEvent = 'login' | 'logout' | 'disconnect' | ||
@@ -37,3 +37,3 @@ export interface AuthenticationStrategy { | ||
*/ | ||
setAuthentication? (auth: AuthenticationBase): void; | ||
setAuthentication?(auth: AuthenticationBase): void | ||
/** | ||
@@ -44,3 +44,3 @@ * Implement this method to get access to the Feathers application | ||
*/ | ||
setApplication? (app: Application): void; | ||
setApplication?(app: Application): void | ||
/** | ||
@@ -51,3 +51,3 @@ * Implement this method to get access to the strategy name | ||
*/ | ||
setName? (name: string): void; | ||
setName?(name: string): void | ||
/** | ||
@@ -57,3 +57,3 @@ * Implement this method to verify the current configuration | ||
*/ | ||
verifyConfiguration? (): void; | ||
verifyConfiguration?(): void | ||
/** | ||
@@ -64,3 +64,3 @@ * Implement this method to setup this strategy | ||
*/ | ||
setup? (auth: AuthenticationBase, name: string): Promise<void>; | ||
setup?(auth: AuthenticationBase, name: string): Promise<void> | ||
/** | ||
@@ -73,3 +73,6 @@ * Authenticate an authentication request with this strategy. | ||
*/ | ||
authenticate? (authentication: AuthenticationRequest, params: AuthenticationParams): Promise<AuthenticationResult>; | ||
authenticate?( | ||
authentication: AuthenticationRequest, | ||
params: AuthenticationParams | ||
): Promise<AuthenticationResult> | ||
/** | ||
@@ -81,3 +84,3 @@ * Update a real-time connection according to this strategy. | ||
*/ | ||
handleConnection? (event: ConnectionEvent, connection: any, authResult?: AuthenticationResult): Promise<void>; | ||
handleConnection?(event: ConnectionEvent, connection: any, authResult?: AuthenticationResult): Promise<void> | ||
/** | ||
@@ -89,7 +92,7 @@ * Parse a basic HTTP request and response for authentication request information. | ||
*/ | ||
parse? (req: IncomingMessage, res: ServerResponse): Promise<AuthenticationRequest | null>; | ||
parse?(req: IncomingMessage, res: ServerResponse): Promise<AuthenticationRequest | null> | ||
} | ||
export interface JwtVerifyOptions extends VerifyOptions { | ||
algorithm?: string | string[]; | ||
algorithm?: string | string[] | ||
} | ||
@@ -101,6 +104,6 @@ | ||
export class AuthenticationBase { | ||
app: Application; | ||
strategies: { [key: string]: AuthenticationStrategy }; | ||
configKey: string; | ||
isReady: boolean; | ||
app: Application | ||
strategies: { [key: string]: AuthenticationStrategy } | ||
configKey: string | ||
isReady: boolean | ||
@@ -114,14 +117,14 @@ /** | ||
*/ | ||
constructor (app: Application, configKey = 'authentication', options = {}) { | ||
constructor(app: Application, configKey = 'authentication', options = {}) { | ||
if (!app || typeof app.use !== 'function') { | ||
throw new Error('An application instance has to be passed to the authentication service'); | ||
throw new Error('An application instance has to be passed to the authentication service') | ||
} | ||
this.app = app; | ||
this.strategies = {}; | ||
this.configKey = configKey; | ||
this.isReady = false; | ||
this.app = app | ||
this.strategies = {} | ||
this.configKey = configKey | ||
this.isReady = false | ||
app.set('defaultAuthentication', app.get('defaultAuthentication') || configKey); | ||
app.set(configKey, merge({}, app.get(configKey), options)); | ||
app.set('defaultAuthentication', app.get('defaultAuthentication') || configKey) | ||
app.set(configKey, merge({}, app.get(configKey), options)) | ||
} | ||
@@ -132,5 +135,5 @@ | ||
*/ | ||
get configuration () { | ||
get configuration() { | ||
// Always returns a copy of the authentication configuration | ||
return Object.assign({}, defaultOptions, this.app.get(this.configKey)); | ||
return Object.assign({}, defaultOptions, this.app.get(this.configKey)) | ||
} | ||
@@ -141,4 +144,4 @@ | ||
*/ | ||
get strategyNames () { | ||
return Object.keys(this.strategies); | ||
get strategyNames() { | ||
return Object.keys(this.strategies) | ||
} | ||
@@ -152,25 +155,25 @@ | ||
*/ | ||
register (name: string, strategy: AuthenticationStrategy) { | ||
register(name: string, strategy: AuthenticationStrategy) { | ||
// Call the functions a strategy can implement | ||
if (typeof strategy.setName === 'function') { | ||
strategy.setName(name); | ||
strategy.setName(name) | ||
} | ||
if (typeof strategy.setApplication === 'function') { | ||
strategy.setApplication(this.app); | ||
strategy.setApplication(this.app) | ||
} | ||
if (typeof strategy.setAuthentication === 'function') { | ||
strategy.setAuthentication(this); | ||
strategy.setAuthentication(this) | ||
} | ||
if (typeof strategy.verifyConfiguration === 'function') { | ||
strategy.verifyConfiguration(); | ||
strategy.verifyConfiguration() | ||
} | ||
// Register strategy as name | ||
this.strategies[name] = strategy; | ||
this.strategies[name] = strategy | ||
if (this.isReady) { | ||
strategy.setup?.(this, name); | ||
strategy.setup?.(this, name) | ||
} | ||
@@ -184,5 +187,4 @@ } | ||
*/ | ||
getStrategies (...names: string[]) { | ||
return names.map(name => this.strategies[name]) | ||
.filter(current => !!current); | ||
getStrategies(...names: string[]) { | ||
return names.map((name) => this.strategies[name]).filter((current) => !!current) | ||
} | ||
@@ -196,4 +198,4 @@ | ||
*/ | ||
getStrategy (name: string) { | ||
return this.strategies[name]; | ||
getStrategy(name: string) { | ||
return this.strategies[name] | ||
} | ||
@@ -208,15 +210,19 @@ | ||
*/ | ||
async createAccessToken (payload: string | Buffer | object, optsOverride?: SignOptions, secretOverride?: Secret) { | ||
const { secret, jwtOptions } = this.configuration; | ||
async createAccessToken( | ||
payload: string | Buffer | object, | ||
optsOverride?: SignOptions, | ||
secretOverride?: Secret | ||
) { | ||
const { secret, jwtOptions } = this.configuration | ||
// Use configuration by default but allow overriding the secret | ||
const jwtSecret = secretOverride || secret; | ||
const jwtSecret = secretOverride || secret | ||
// Default jwt options merged with additional options | ||
const options = merge({}, jwtOptions, optsOverride); | ||
const options = merge({}, jwtOptions, optsOverride) | ||
if (!options.jwtid) { | ||
// Generate a UUID as JWT ID by default | ||
options.jwtid = uuidv4(); | ||
options.jwtid = uuidv4() | ||
} | ||
return jsonwebtoken.sign(payload, jwtSecret, options); | ||
return jsonwebtoken.sign(payload, jwtSecret, options) | ||
} | ||
@@ -231,20 +237,20 @@ | ||
*/ | ||
async verifyAccessToken (accessToken: string, optsOverride?: JwtVerifyOptions, secretOverride?: Secret) { | ||
const { secret, jwtOptions } = this.configuration; | ||
const jwtSecret = secretOverride || secret; | ||
const options = merge({}, jwtOptions, optsOverride); | ||
const { algorithm } = options; | ||
async verifyAccessToken(accessToken: string, optsOverride?: JwtVerifyOptions, secretOverride?: Secret) { | ||
const { secret, jwtOptions } = this.configuration | ||
const jwtSecret = secretOverride || secret | ||
const options = merge({}, jwtOptions, optsOverride) | ||
const { algorithm } = options | ||
// Normalize the `algorithm` setting into the algorithms array | ||
if (algorithm && !options.algorithms) { | ||
options.algorithms = Array.isArray(algorithm) ? algorithm : [ algorithm ]; | ||
delete options.algorithm; | ||
options.algorithms = Array.isArray(algorithm) ? algorithm : [algorithm] | ||
delete options.algorithm | ||
} | ||
try { | ||
const verified = jsonwebtoken.verify(accessToken, jwtSecret, options); | ||
const verified = jsonwebtoken.verify(accessToken, jwtSecret, options) | ||
return verified as any; | ||
return verified as any | ||
} catch (error: any) { | ||
throw new NotAuthenticated(error.message, error); | ||
throw new NotAuthenticated(error.message, error) | ||
} | ||
@@ -260,15 +266,21 @@ } | ||
*/ | ||
async authenticate (authentication: AuthenticationRequest, params: AuthenticationParams, ...allowed: string[]) { | ||
const { strategy } = authentication || {}; | ||
const [ authStrategy ] = this.getStrategies(strategy); | ||
const strategyAllowed = allowed.includes(strategy); | ||
async authenticate( | ||
authentication: AuthenticationRequest, | ||
params: AuthenticationParams, | ||
...allowed: string[] | ||
) { | ||
const { strategy } = authentication || {} | ||
const [authStrategy] = this.getStrategies(strategy) | ||
const strategyAllowed = allowed.includes(strategy) | ||
debug('Running authenticate for strategy', strategy, allowed); | ||
debug('Running authenticate for strategy', strategy, allowed) | ||
if (!authentication || !authStrategy || !strategyAllowed) { | ||
const additionalInfo = (!strategy && ' (no `strategy` set)') || | ||
(!strategyAllowed && ' (strategy not allowed in authStrategies)') || ''; | ||
const additionalInfo = | ||
(!strategy && ' (no `strategy` set)') || | ||
(!strategyAllowed && ' (strategy not allowed in authStrategies)') || | ||
'' | ||
// If there are no valid strategies or `authentication` is not an object | ||
throw new NotAuthenticated('Invalid authentication information' + additionalInfo); | ||
throw new NotAuthenticated('Invalid authentication information' + additionalInfo) | ||
} | ||
@@ -279,11 +291,12 @@ | ||
authenticated: true | ||
}); | ||
}) | ||
} | ||
async handleConnection (event: ConnectionEvent, connection: any, authResult?: AuthenticationResult) { | ||
const strategies = this.getStrategies(...Object.keys(this.strategies)) | ||
.filter(current => typeof current.handleConnection === 'function'); | ||
async handleConnection(event: ConnectionEvent, connection: any, authResult?: AuthenticationResult) { | ||
const strategies = this.getStrategies(...Object.keys(this.strategies)).filter( | ||
(current) => typeof current.handleConnection === 'function' | ||
) | ||
for (const strategy of strategies) { | ||
await strategy.handleConnection(event, connection, authResult); | ||
await strategy.handleConnection(event, connection, authResult) | ||
} | ||
@@ -299,28 +312,27 @@ } | ||
*/ | ||
async parse (req: IncomingMessage, res: ServerResponse, ...names: string[]) { | ||
const strategies = this.getStrategies(...names) | ||
.filter(current => typeof current.parse === 'function'); | ||
async parse(req: IncomingMessage, res: ServerResponse, ...names: string[]) { | ||
const strategies = this.getStrategies(...names).filter((current) => typeof current.parse === 'function') | ||
debug('Strategies parsing HTTP header for authentication information', names); | ||
debug('Strategies parsing HTTP header for authentication information', names) | ||
for (const authStrategy of strategies) { | ||
const value = await authStrategy.parse(req, res); | ||
const value = await authStrategy.parse(req, res) | ||
if (value !== null) { | ||
return value; | ||
return value | ||
} | ||
} | ||
return null; | ||
return null | ||
} | ||
async setup () { | ||
this.isReady = true; | ||
async setup() { | ||
this.isReady = true | ||
for (const name of Object.keys(this.strategies)) { | ||
const strategy = this.strategies[name]; | ||
const strategy = this.strategies[name] | ||
await strategy.setup?.(this, name); | ||
await strategy.setup?.(this, name) | ||
} | ||
} | ||
} |
@@ -1,63 +0,67 @@ | ||
import flatten from 'lodash/flatten'; | ||
import omit from 'lodash/omit'; | ||
import { HookContext, NextFunction } from '@feathersjs/feathers'; | ||
import { NotAuthenticated } from '@feathersjs/errors'; | ||
import { createDebug } from '@feathersjs/commons'; | ||
import flatten from 'lodash/flatten' | ||
import omit from 'lodash/omit' | ||
import { HookContext, NextFunction } from '@feathersjs/feathers' | ||
import { NotAuthenticated } from '@feathersjs/errors' | ||
import { createDebug } from '@feathersjs/commons' | ||
const debug = createDebug('@feathersjs/authentication/hooks/authenticate'); | ||
const debug = createDebug('@feathersjs/authentication/hooks/authenticate') | ||
export interface AuthenticateHookSettings { | ||
service?: string; | ||
strategies?: string[]; | ||
service?: string | ||
strategies?: string[] | ||
} | ||
export default (originalSettings: string | AuthenticateHookSettings, ...originalStrategies: string[]) => { | ||
const settings = typeof originalSettings === 'string' | ||
? { strategies: flatten([ originalSettings, ...originalStrategies ]) } | ||
: originalSettings; | ||
const settings = | ||
typeof originalSettings === 'string' | ||
? { strategies: flatten([originalSettings, ...originalStrategies]) } | ||
: originalSettings | ||
if (!originalSettings || settings.strategies.length === 0) { | ||
throw new Error('The authenticate hook needs at least one allowed strategy'); | ||
throw new Error('The authenticate hook needs at least one allowed strategy') | ||
} | ||
return async (context: HookContext, _next?: NextFunction) => { | ||
const next = typeof _next === 'function' ? _next : async () => context; | ||
const { app, params, type, path, service } = context; | ||
const { strategies } = settings; | ||
const { provider, authentication } = params; | ||
const authService = app.defaultAuthentication(settings.service); | ||
const next = typeof _next === 'function' ? _next : async () => context | ||
const { app, params, type, path, service } = context | ||
const { strategies } = settings | ||
const { provider, authentication } = params | ||
const authService = app.defaultAuthentication(settings.service) | ||
debug(`Running authenticate hook on '${path}'`); | ||
debug(`Running authenticate hook on '${path}'`) | ||
if (type && type !== 'before') { | ||
throw new NotAuthenticated('The authenticate hook must be used as a before hook'); | ||
throw new NotAuthenticated('The authenticate hook must be used as a before hook') | ||
} | ||
if (!authService || typeof authService.authenticate !== 'function') { | ||
throw new NotAuthenticated('Could not find a valid authentication service'); | ||
throw new NotAuthenticated('Could not find a valid authentication service') | ||
} | ||
// @ts-ignore | ||
if (service === authService) { | ||
throw new NotAuthenticated('The authenticate hook does not need to be used on the authentication service'); | ||
throw new NotAuthenticated( | ||
'The authenticate hook does not need to be used on the authentication service' | ||
) | ||
} | ||
if (params.authenticated === true) { | ||
return next(); | ||
return next() | ||
} | ||
if (authentication) { | ||
const authParams = omit(params, 'provider', 'authentication'); | ||
const authParams = omit(params, 'provider', 'authentication') | ||
debug('Authenticating with', authentication, strategies); | ||
debug('Authenticating with', authentication, strategies) | ||
const authResult = await authService.authenticate(authentication, authParams, ...strategies); | ||
const authResult = await authService.authenticate(authentication, authParams, ...strategies) | ||
context.params = Object.assign({}, params, omit(authResult, 'accessToken'), { authenticated: true }); | ||
context.params = Object.assign({}, params, omit(authResult, 'accessToken'), { | ||
authenticated: true | ||
}) | ||
} else if (provider) { | ||
throw new NotAuthenticated('Not authenticated'); | ||
throw new NotAuthenticated('Not authenticated') | ||
} | ||
return next(); | ||
}; | ||
}; | ||
return next() | ||
} | ||
} |
@@ -1,17 +0,20 @@ | ||
import { HookContext, NextFunction } from '@feathersjs/feathers'; | ||
import omit from 'lodash/omit'; | ||
import { AuthenticationBase, ConnectionEvent } from '../core'; | ||
import { HookContext, NextFunction } from '@feathersjs/feathers' | ||
import omit from 'lodash/omit' | ||
import { AuthenticationBase, ConnectionEvent } from '../core' | ||
export default (event: ConnectionEvent) => async (context: HookContext, next: NextFunction) => { | ||
await next(); | ||
await next() | ||
const { result, params: { connection } } = context; | ||
const { | ||
result, | ||
params: { connection } | ||
} = context | ||
if (connection) { | ||
const service = context.service as unknown as AuthenticationBase; | ||
const service = context.service as unknown as AuthenticationBase | ||
Object.assign(connection, omit(result, 'accessToken', 'authentication')); | ||
Object.assign(connection, omit(result, 'accessToken', 'authentication')) | ||
await service.handleConnection(event, connection, result); | ||
await service.handleConnection(event, connection, result) | ||
} | ||
}; | ||
} |
@@ -1,16 +0,16 @@ | ||
import { HookContext, NextFunction } from '@feathersjs/feathers'; | ||
import { createDebug } from '@feathersjs/commons'; | ||
import { ConnectionEvent } from '../core'; | ||
import { HookContext, NextFunction } from '@feathersjs/feathers' | ||
import { createDebug } from '@feathersjs/commons' | ||
import { ConnectionEvent } from '../core' | ||
const debug = createDebug('@feathersjs/authentication/hooks/connection'); | ||
const debug = createDebug('@feathersjs/authentication/hooks/connection') | ||
export default (event: ConnectionEvent) => async (context: HookContext, next: NextFunction) => { | ||
await next(); | ||
await next() | ||
const { app, result, params } = context; | ||
const { app, result, params } = context | ||
if (params.provider && result) { | ||
debug(`Sending authentication event '${event}'`); | ||
app.emit(event, result, params, context); | ||
debug(`Sending authentication event '${event}'`) | ||
app.emit(event, result, params, context) | ||
} | ||
}; | ||
} |
@@ -1,3 +0,3 @@ | ||
export { default as authenticate } from './authenticate'; | ||
export { default as connection } from './connection'; | ||
export { default as event } from './event'; | ||
export { default as authenticate } from './authenticate' | ||
export { default as connection } from './connection' | ||
export { default as event } from './event' |
@@ -1,3 +0,3 @@ | ||
export * as hooks from './hooks'; | ||
export { authenticate } from './hooks'; | ||
export * as hooks from './hooks' | ||
export { authenticate } from './hooks' | ||
export { | ||
@@ -10,6 +10,6 @@ AuthenticationBase, | ||
ConnectionEvent | ||
} from './core'; | ||
export { AuthenticationBaseStrategy } from './strategy'; | ||
export { AuthenticationService } from './service'; | ||
export { JWTStrategy } from './jwt'; | ||
export { authenticationSettingsSchema } from './options'; | ||
} from './core' | ||
export { AuthenticationBaseStrategy } from './strategy' | ||
export { AuthenticationService } from './service' | ||
export { JWTStrategy } from './jwt' | ||
export { authenticationSettingsSchema } from './options' |
161
src/jwt.ts
@@ -1,22 +0,22 @@ | ||
/* eslint-disable @typescript-eslint/no-unused-vars */ | ||
import omit from 'lodash/omit'; | ||
import { IncomingMessage } from 'http'; | ||
import { NotAuthenticated } from '@feathersjs/errors'; | ||
import { Params } from '@feathersjs/feathers'; | ||
import { createDebug } from '@feathersjs/commons'; | ||
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/ban-ts-comment */ | ||
import omit from 'lodash/omit' | ||
import { IncomingMessage } from 'http' | ||
import { NotAuthenticated } from '@feathersjs/errors' | ||
import { Params } from '@feathersjs/feathers' | ||
import { createDebug } from '@feathersjs/commons' | ||
// @ts-ignore | ||
import lt from 'long-timeout'; | ||
import lt from 'long-timeout' | ||
import { AuthenticationBaseStrategy } from './strategy'; | ||
import { AuthenticationParams, AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core'; | ||
import { AuthenticationBaseStrategy } from './strategy' | ||
import { AuthenticationParams, AuthenticationRequest, AuthenticationResult, ConnectionEvent } from './core' | ||
const debug = createDebug('@feathersjs/authentication/jwt'); | ||
const SPLIT_HEADER = /(\S+)\s+(\S+)/; | ||
const debug = createDebug('@feathersjs/authentication/jwt') | ||
const SPLIT_HEADER = /(\S+)\s+(\S+)/ | ||
export class JWTStrategy extends AuthenticationBaseStrategy { | ||
expirationTimers = new WeakMap(); | ||
expirationTimers = new WeakMap() | ||
get configuration () { | ||
const authConfig = this.authentication.configuration; | ||
const config = super.configuration; | ||
get configuration() { | ||
const authConfig = this.authentication.configuration | ||
const config = super.configuration | ||
@@ -28,50 +28,59 @@ return { | ||
header: 'Authorization', | ||
schemes: [ 'Bearer', 'JWT' ], | ||
schemes: ['Bearer', 'JWT'], | ||
...config | ||
}; | ||
} | ||
} | ||
async handleConnection (event: ConnectionEvent, connection: any, authResult?: AuthenticationResult): Promise<void> { | ||
const isValidLogout = event === 'logout' && connection.authentication && authResult && | ||
connection.authentication.accessToken === authResult.accessToken; | ||
async handleConnection( | ||
event: ConnectionEvent, | ||
connection: any, | ||
authResult?: AuthenticationResult | ||
): Promise<void> { | ||
const isValidLogout = | ||
event === 'logout' && | ||
connection.authentication && | ||
authResult && | ||
connection.authentication.accessToken === authResult.accessToken | ||
const { accessToken } = authResult || {}; | ||
const { accessToken } = authResult || {} | ||
if (accessToken && event === 'login') { | ||
debug('Adding authentication information to connection'); | ||
const { exp } = await this.authentication.verifyAccessToken(accessToken); | ||
debug('Adding authentication information to connection') | ||
const { exp } = await this.authentication.verifyAccessToken(accessToken) | ||
// The time (in ms) until the token expires | ||
const duration = (exp * 1000) - Date.now(); | ||
const duration = exp * 1000 - Date.now() | ||
// This may have to be a `logout` event but right now we don't want | ||
// the whole context object lingering around until the timer is gone | ||
const timer = lt.setTimeout(() => this.app.emit('disconnect', connection), duration); | ||
const timer = lt.setTimeout(() => this.app.emit('disconnect', connection), duration) | ||
debug(`Registering connection expiration timer for ${duration}ms`); | ||
lt.clearTimeout(this.expirationTimers.get(connection)); | ||
this.expirationTimers.set(connection, timer); | ||
debug(`Registering connection expiration timer for ${duration}ms`) | ||
lt.clearTimeout(this.expirationTimers.get(connection)) | ||
this.expirationTimers.set(connection, timer) | ||
debug('Adding authentication information to connection'); | ||
debug('Adding authentication information to connection') | ||
connection.authentication = { | ||
strategy: this.name, | ||
accessToken | ||
}; | ||
} | ||
} else if (event === 'disconnect' || isValidLogout) { | ||
debug('Removing authentication information and expiration timer from connection'); | ||
debug('Removing authentication information and expiration timer from connection') | ||
const { entity } = this.configuration; | ||
const { entity } = this.configuration | ||
delete connection[entity]; | ||
delete connection.authentication; | ||
delete connection[entity] | ||
delete connection.authentication | ||
lt.clearTimeout(this.expirationTimers.get(connection)); | ||
this.expirationTimers.delete(connection); | ||
lt.clearTimeout(this.expirationTimers.get(connection)) | ||
this.expirationTimers.delete(connection) | ||
} | ||
} | ||
verifyConfiguration () { | ||
const allowedKeys = [ 'entity', 'entityId', 'service', 'header', 'schemes' ]; | ||
verifyConfiguration() { | ||
const allowedKeys = ['entity', 'entityId', 'service', 'header', 'schemes'] | ||
for (const key of Object.keys(this.configuration)) { | ||
if (!allowedKeys.includes(key)) { | ||
throw new Error(`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?`); | ||
throw new Error( | ||
`Invalid JwtStrategy option 'authentication.${this.name}.${key}'. Did you mean to set it in 'authentication.jwtOptions'?` | ||
) | ||
} | ||
@@ -81,8 +90,8 @@ } | ||
if (typeof this.configuration.header !== 'string') { | ||
throw new Error(`The 'header' option for the ${this.name} strategy must be a string`); | ||
throw new Error(`The 'header' option for the ${this.name} strategy must be a string`) | ||
} | ||
} | ||
async getEntityQuery (_params: Params) { | ||
return {}; | ||
async getEntityQuery(_params: Params) { | ||
return {} | ||
} | ||
@@ -96,36 +105,36 @@ | ||
*/ | ||
async getEntity (id: string, params: Params) { | ||
const entityService = this.entityService; | ||
const { entity } = this.configuration; | ||
async getEntity(id: string, params: Params) { | ||
const entityService = this.entityService | ||
const { entity } = this.configuration | ||
debug('Getting entity', id); | ||
debug('Getting entity', id) | ||
if (entityService === null) { | ||
throw new NotAuthenticated('Could not find entity service'); | ||
throw new NotAuthenticated('Could not find entity service') | ||
} | ||
const query = await this.getEntityQuery(params); | ||
const getParams = Object.assign({}, omit(params, 'provider'), { query }); | ||
const result = await entityService.get(id, getParams); | ||
const query = await this.getEntityQuery(params) | ||
const getParams = Object.assign({}, omit(params, 'provider'), { query }) | ||
const result = await entityService.get(id, getParams) | ||
if (!params.provider) { | ||
return result; | ||
return result | ||
} | ||
return entityService.get(id, { ...params, [entity]: result }); | ||
return entityService.get(id, { ...params, [entity]: result }) | ||
} | ||
async getEntityId (authResult: AuthenticationResult, _params: Params) { | ||
return authResult.authentication.payload.sub; | ||
async getEntityId(authResult: AuthenticationResult, _params: Params) { | ||
return authResult.authentication.payload.sub | ||
} | ||
async authenticate (authentication: AuthenticationRequest, params: AuthenticationParams) { | ||
const { accessToken } = authentication; | ||
const { entity } = this.configuration; | ||
async authenticate(authentication: AuthenticationRequest, params: AuthenticationParams) { | ||
const { accessToken } = authentication | ||
const { entity } = this.configuration | ||
if (!accessToken) { | ||
throw new NotAuthenticated('No access token'); | ||
throw new NotAuthenticated('No access token') | ||
} | ||
const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt); | ||
const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt) | ||
const result = { | ||
@@ -138,10 +147,10 @@ accessToken, | ||
} | ||
}; | ||
} | ||
if (entity === null) { | ||
return result; | ||
return result | ||
} | ||
const entityId = await this.getEntityId(result, params); | ||
const value = await this.getEntity(entityId, params); | ||
const entityId = await this.getEntityId(result, params) | ||
const value = await this.getEntity(entityId, params) | ||
@@ -151,25 +160,23 @@ return { | ||
[entity]: value | ||
}; | ||
} | ||
} | ||
async parse (req: IncomingMessage): Promise<{ | ||
strategy: string; | ||
accessToken: string; | ||
async parse(req: IncomingMessage): Promise<{ | ||
strategy: string | ||
accessToken: string | ||
} | null> { | ||
const { header, schemes }: { header: string, schemes: string[] } = this.configuration; | ||
const headerValue = req.headers && req.headers[header.toLowerCase()]; | ||
const { header, schemes }: { header: string; schemes: string[] } = this.configuration | ||
const headerValue = req.headers && req.headers[header.toLowerCase()] | ||
if (!headerValue || typeof headerValue !== 'string') { | ||
return null; | ||
return null | ||
} | ||
debug('Found parsed header value'); | ||
debug('Found parsed header value') | ||
const [ , scheme, schemeValue ] = headerValue.match(SPLIT_HEADER) || []; | ||
const hasScheme = scheme && schemes.some( | ||
current => new RegExp(current, 'i').test(scheme) | ||
); | ||
const [, scheme, schemeValue] = headerValue.match(SPLIT_HEADER) || [] | ||
const hasScheme = scheme && schemes.some((current) => new RegExp(current, 'i').test(scheme)) | ||
if (scheme && !hasScheme) { | ||
return null; | ||
return null | ||
} | ||
@@ -180,4 +187,4 @@ | ||
accessToken: hasScheme ? schemeValue : headerValue | ||
}; | ||
} | ||
} | ||
} |
@@ -10,3 +10,3 @@ export const defaultOptions = { | ||
} | ||
}; | ||
} | ||
@@ -22,7 +22,10 @@ export const authenticationSettingsSchema = { | ||
entity: { | ||
oneOf: [{ | ||
type: 'null' | ||
}, { | ||
type: 'string' | ||
}], | ||
oneOf: [ | ||
{ | ||
type: 'null' | ||
}, | ||
{ | ||
type: 'string' | ||
} | ||
], | ||
description: 'The name of the authentication entity (e.g. user)' | ||
@@ -46,3 +49,4 @@ }, | ||
items: { type: 'string' }, | ||
description: 'A list of authentication strategy names that should parse HTTP headers for authentication information (defaults to `authStrategies`)' | ||
description: | ||
'A list of authentication strategy names that should parse HTTP headers for authentication information (defaults to `authStrategies`)' | ||
}, | ||
@@ -90,7 +94,9 @@ jwtOptions: { | ||
type: 'string', | ||
description: 'Name of the username field on the entity if authentication request data and entity field names are different' | ||
description: | ||
'Name of the username field on the entity if authentication request data and entity field names are different' | ||
}, | ||
entityPasswordField: { | ||
type: 'string', | ||
description: 'Name of the password field on the entity if authentication request data and entity field names are different' | ||
description: | ||
'Name of the password field on the entity if authentication request data and entity field names are different' | ||
} | ||
@@ -119,2 +125,2 @@ } | ||
} | ||
} as const; | ||
} as const |
@@ -1,14 +0,15 @@ | ||
import merge from 'lodash/merge'; | ||
import { NotAuthenticated } from '@feathersjs/errors'; | ||
import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core'; | ||
import { connection, event } from './hooks'; | ||
import '@feathersjs/transport-commons'; | ||
import { createDebug } from '@feathersjs/commons'; | ||
import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers'; | ||
import jsonwebtoken from 'jsonwebtoken'; | ||
import merge from 'lodash/merge' | ||
import { NotAuthenticated } from '@feathersjs/errors' | ||
import { AuthenticationBase, AuthenticationResult, AuthenticationRequest, AuthenticationParams } from './core' | ||
import { connection, event } from './hooks' | ||
import '@feathersjs/transport-commons' | ||
import { createDebug } from '@feathersjs/commons' | ||
import { ServiceMethods, ServiceAddons } from '@feathersjs/feathers' | ||
import jsonwebtoken from 'jsonwebtoken' | ||
const debug = createDebug('@feathersjs/authentication/service'); | ||
const debug = createDebug('@feathersjs/authentication/service') | ||
declare module '@feathersjs/feathers/lib/declarations' { | ||
interface FeathersApplication<Services, Settings> { // eslint-disable-line | ||
interface FeathersApplication<Services, Settings> { | ||
// eslint-disable-line | ||
/** | ||
@@ -20,8 +21,8 @@ * Returns the default authentication service or the | ||
*/ | ||
defaultAuthentication? (location?: string): AuthenticationService; | ||
defaultAuthentication?(location?: string): AuthenticationService | ||
} | ||
interface Params { | ||
authenticated?: boolean; | ||
authentication?: AuthenticationRequest; | ||
authenticated?: boolean | ||
authentication?: AuthenticationRequest | ||
} | ||
@@ -33,15 +34,18 @@ } | ||
export class AuthenticationService extends AuthenticationBase implements Partial<ServiceMethods<AuthenticationResult, AuthenticationRequest, AuthenticationParams>> { | ||
constructor (app: any, configKey = 'authentication', options = {}) { | ||
super(app, configKey, options); | ||
export class AuthenticationService | ||
extends AuthenticationBase | ||
implements Partial<ServiceMethods<AuthenticationResult, AuthenticationRequest, AuthenticationParams>> | ||
{ | ||
constructor(app: any, configKey = 'authentication', options = {}) { | ||
super(app, configKey, options) | ||
if (typeof app.defaultAuthentication !== 'function') { | ||
app.defaultAuthentication = function (location?: string) { | ||
const configKey = app.get('defaultAuthentication'); | ||
const path = location || Object.keys(this.services).find(current => | ||
this.service(current).configKey === configKey | ||
); | ||
const configKey = app.get('defaultAuthentication') | ||
const path = | ||
location || | ||
Object.keys(this.services).find((current) => this.service(current).configKey === configKey) | ||
return path ? this.service(path) : null; | ||
}; | ||
return path ? this.service(path) : null | ||
} | ||
} | ||
@@ -56,7 +60,7 @@ } | ||
*/ | ||
async getPayload (_authResult: AuthenticationResult, params: AuthenticationParams) { | ||
async getPayload(_authResult: AuthenticationResult, params: AuthenticationParams) { | ||
// Uses `params.payload` or returns an empty payload | ||
const { payload = {} } = params; | ||
const { payload = {} } = params | ||
return payload; | ||
return payload | ||
} | ||
@@ -71,20 +75,20 @@ | ||
*/ | ||
async getTokenOptions (authResult: AuthenticationResult, params: AuthenticationParams) { | ||
const { service, entity, entityId } = this.configuration; | ||
const jwtOptions = merge({}, params.jwtOptions, params.jwt); | ||
const value = service && entity && authResult[entity]; | ||
async getTokenOptions(authResult: AuthenticationResult, params: AuthenticationParams) { | ||
const { service, entity, entityId } = this.configuration | ||
const jwtOptions = merge({}, params.jwtOptions, params.jwt) | ||
const value = service && entity && authResult[entity] | ||
// Set the subject to the entity id if it is available | ||
if (value && !jwtOptions.subject) { | ||
const idProperty = entityId || this.app.service(service).id; | ||
const subject = value[idProperty]; | ||
const idProperty = entityId || this.app.service(service).id | ||
const subject = value[idProperty] | ||
if (subject === undefined) { | ||
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`); | ||
throw new NotAuthenticated(`Can not set subject from ${entity}.${idProperty}`) | ||
} | ||
jwtOptions.subject = `${subject}`; | ||
jwtOptions.subject = `${subject}` | ||
} | ||
return jwtOptions; | ||
return jwtOptions | ||
} | ||
@@ -99,32 +103,32 @@ | ||
*/ | ||
async create (data: AuthenticationRequest, params?: AuthenticationParams) { | ||
const authStrategies = params.authStrategies || this.configuration.authStrategies; | ||
async create(data: AuthenticationRequest, params?: AuthenticationParams) { | ||
const authStrategies = params.authStrategies || this.configuration.authStrategies | ||
if (!authStrategies.length) { | ||
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)'); | ||
throw new NotAuthenticated('No authentication strategies allowed for creating a JWT (`authStrategies`)') | ||
} | ||
const authResult = await this.authenticate(data, params, ...authStrategies); | ||
const authResult = await this.authenticate(data, params, ...authStrategies) | ||
debug('Got authentication result', authResult); | ||
debug('Got authentication result', authResult) | ||
if (authResult.accessToken) { | ||
return authResult; | ||
return authResult | ||
} | ||
const [ payload, jwtOptions ] = await Promise.all([ | ||
const [payload, jwtOptions] = await Promise.all([ | ||
this.getPayload(authResult, params), | ||
this.getTokenOptions(authResult, params) | ||
]); | ||
]) | ||
debug('Creating JWT with', payload, jwtOptions); | ||
debug('Creating JWT with', payload, jwtOptions) | ||
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret); | ||
const accessToken = await this.createAccessToken(payload, jwtOptions, params.secret) | ||
return merge({ accessToken }, authResult, { | ||
authentication: { | ||
accessToken, | ||
payload: jsonwebtoken.decode(accessToken) | ||
accessToken, | ||
payload: jsonwebtoken.decode(accessToken) | ||
} | ||
}); | ||
}) | ||
} | ||
@@ -139,14 +143,14 @@ | ||
*/ | ||
async remove (id: string | null, params?: AuthenticationParams) { | ||
const { authentication } = params; | ||
const { authStrategies } = this.configuration; | ||
async remove(id: string | null, params?: AuthenticationParams) { | ||
const { authentication } = params | ||
const { authStrategies } = this.configuration | ||
// When an id is passed it is expected to be the authentication `accessToken` | ||
if (id !== null && id !== authentication.accessToken) { | ||
throw new NotAuthenticated('Invalid access token'); | ||
throw new NotAuthenticated('Invalid access token') | ||
} | ||
debug('Verifying authentication strategy in remove'); | ||
debug('Verifying authentication strategy in remove') | ||
return this.authenticate(authentication, params, ...authStrategies); | ||
return this.authenticate(authentication, params, ...authStrategies) | ||
} | ||
@@ -157,11 +161,11 @@ | ||
*/ | ||
async setup () { | ||
await super.setup(); | ||
async setup() { | ||
await super.setup() | ||
// The setup method checks for valid settings and registers the | ||
// connection and event (login, logout) hooks | ||
const { secret, service, entity, entityId } = this.configuration; | ||
const { secret, service, entity, entityId } = this.configuration | ||
if (typeof secret !== 'string') { | ||
throw new Error('A \'secret\' must be provided in your authentication configuration'); | ||
throw new Error("A 'secret' must be provided in your authentication configuration") | ||
} | ||
@@ -171,27 +175,31 @@ | ||
if (service === undefined) { | ||
throw new Error('The \'service\' option is not set in the authentication configuration'); | ||
throw new Error("The 'service' option is not set in the authentication configuration") | ||
} | ||
if (this.app.service(service) === undefined) { | ||
throw new Error(`The '${service}' entity service does not exist (set to 'null' if it is not required)`); | ||
throw new Error( | ||
`The '${service}' entity service does not exist (set to 'null' if it is not required)` | ||
) | ||
} | ||
if (this.app.service(service).id === undefined && entityId === undefined) { | ||
throw new Error(`The '${service}' service does not have an 'id' property and no 'entityId' option is set.`); | ||
throw new Error( | ||
`The '${service}' service does not have an 'id' property and no 'entityId' option is set.` | ||
) | ||
} | ||
} | ||
(this as any).hooks({ | ||
create: [ connection('login'), event('login') ], | ||
remove: [ connection('logout'), event('logout') ] | ||
}); | ||
this.hooks({ | ||
create: [connection('login'), event('login')], | ||
remove: [connection('logout'), event('logout')] | ||
} as any) | ||
this.app.on('disconnect', async (connection) => { | ||
await this.handleConnection('disconnect', connection); | ||
}); | ||
await this.handleConnection('disconnect', connection) | ||
}) | ||
if (typeof this.publish === 'function') { | ||
this.publish(() => null); | ||
this.publish(() => null) | ||
} | ||
} | ||
} |
@@ -1,34 +0,34 @@ | ||
import { AuthenticationStrategy, AuthenticationBase } from './core'; | ||
import { Application, Service } from '@feathersjs/feathers'; | ||
import { AuthenticationStrategy, AuthenticationBase } from './core' | ||
import { Application, Service } from '@feathersjs/feathers' | ||
export class AuthenticationBaseStrategy implements AuthenticationStrategy { | ||
authentication?: AuthenticationBase; | ||
app?: Application; | ||
name?: string; | ||
authentication?: AuthenticationBase | ||
app?: Application | ||
name?: string | ||
setAuthentication (auth: AuthenticationBase) { | ||
this.authentication = auth; | ||
setAuthentication(auth: AuthenticationBase) { | ||
this.authentication = auth | ||
} | ||
setApplication (app: Application) { | ||
this.app = app; | ||
setApplication(app: Application) { | ||
this.app = app | ||
} | ||
setName (name: string) { | ||
this.name = name; | ||
setName(name: string) { | ||
this.name = name | ||
} | ||
get configuration () { | ||
return this.authentication.configuration[this.name]; | ||
get configuration() { | ||
return this.authentication.configuration[this.name] | ||
} | ||
get entityService (): Service { | ||
const { service } = this.configuration; | ||
get entityService(): Service { | ||
const { service } = this.configuration | ||
if (!service) { | ||
return null; | ||
return null | ||
} | ||
return this.app.service(service) || null; | ||
return this.app.service(service) || null | ||
} | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
211344
1999