diff --git a/packages/authentication-local/src/strategy.ts b/packages/authentication-local/src/strategy.ts index f76cbe4c88..febde71737 100644 --- a/packages/authentication-local/src/strategy.ts +++ b/packages/authentication-local/src/strategy.ts @@ -28,6 +28,7 @@ export class LocalStrategy extends AuthenticationBaseStrategy { hashSize: 10, service: authConfig.service, entity: authConfig.entity, + entityId: authConfig.entityId, errorMessage: 'Invalid login', entityPasswordField: config.passwordField, entityUsernameField: config.usernameField, @@ -59,7 +60,7 @@ export class LocalStrategy extends AuthenticationBaseStrategy { if (!Array.isArray(list) || list.length === 0) { debug(`No entity found`); - return Promise.reject(new NotAuthenticated(errorMessage)); + throw new NotAuthenticated(errorMessage); } const [ entity ] = list; @@ -67,6 +68,24 @@ export class LocalStrategy extends AuthenticationBaseStrategy { return entity; } + async getEntity (result: any, params: Params) { + const { entityService } = this; + const { entityId = entityService.id, entity } = this.configuration; + + if (!entityId || result[entityId] === undefined) { + throw new NotAuthenticated('Could not get local entity'); + } + + if (!params.provider) { + return result; + } + + return entityService.get(result[entityId], { + ...params, + [entity]: result + }); + } + async comparePassword (entity: any, password: string) { const { entityPasswordField, errorMessage } = this.configuration; // find password in entity, this allows for dot notation @@ -101,11 +120,9 @@ export class LocalStrategy extends AuthenticationBaseStrategy { await this.comparePassword(result, password); - const authEntity = await (params.provider ? this.findEntity(username, params) : result); - return { authentication: { strategy: this.name }, - [entity]: authEntity + [entity]: await this.getEntity(result, params) }; } } diff --git a/packages/authentication-local/test/fixture.js b/packages/authentication-local/test/fixture.js index 411cef1ee8..7772bcdc1a 100644 --- a/packages/authentication-local/test/fixture.js +++ b/packages/authentication-local/test/fixture.js @@ -34,7 +34,16 @@ module.exports = (app = feathers()) => { before: { create: hashPassword('password') }, - after: protect('password') + after: { + all: protect('password'), + get: [context => { + if (context.params.provider) { + context.result.fromGet = true; + } + + return context; + }] + } }); return app; diff --git a/packages/authentication-local/test/strategy.test.ts b/packages/authentication-local/test/strategy.test.ts index 4a94dd9a43..3207b1de70 100644 --- a/packages/authentication-local/test/strategy.test.ts +++ b/packages/authentication-local/test/strategy.test.ts @@ -1,4 +1,5 @@ import assert from 'assert'; +import { omit } from 'lodash'; import { LocalStrategy } from '../src'; // @ts-ignore import createApplication from './fixture'; @@ -45,6 +46,29 @@ describe('@feathersjs/authentication-local/strategy', () => { } }); + it('getEntity', async () => { + const [ strategy ] = app.service('authentication').getStrategies('local'); + let entity = await strategy.getEntity(user, {}); + + assert.deepStrictEqual(entity, user); + + entity = await strategy.getEntity(user, { + provider: 'testing' + }); + + assert.deepStrictEqual(entity, { + ...omit(user, 'password'), + fromGet: true + }); + + try { + await strategy.getEntity({}, {}); + assert.fail('Should never get here'); + } catch (error) { + assert.strictEqual(error.message, 'Could not get local entity'); + } + }); + it('strategy fails when strategy is different', async () => { const [ local ] = app.service('authentication').getStrategies('local'); @@ -128,6 +152,7 @@ describe('@feathersjs/authentication-local/strategy', () => { assert.ok(accessToken); assert.strictEqual(authResult.user.email, email); assert.strictEqual(authResult.user.passsword, undefined); + assert.ok(authResult.user.fromGet); const decoded = await authService.verifyAccessToken(accessToken); diff --git a/packages/authentication-oauth/package-lock.json b/packages/authentication-oauth/package-lock.json new file mode 100644 index 0000000000..564e187e84 --- /dev/null +++ b/packages/authentication-oauth/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "@feathersjs/authentication-oauth", + "version": "4.0.0-pre.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "11.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.10.tgz", + "integrity": "sha512-leUNzbFTMX94TWaIKz8N15Chu55F9QSH+INKayQr5xpkasBQBRF3qQXfo3/dOnMU/dEIit+Y/SU8HyOjq++GwA==", + "dev": true + } + } +} diff --git a/packages/authentication/src/hooks/authenticate.ts b/packages/authentication/src/hooks/authenticate.ts index 905fbcfd4e..0f68f2e25d 100644 --- a/packages/authentication/src/hooks/authenticate.ts +++ b/packages/authentication/src/hooks/authenticate.ts @@ -1,4 +1,4 @@ -import { flatten, merge, omit } from 'lodash'; +import { flatten, omit, merge } from 'lodash'; import { HookContext } from '@feathersjs/feathers'; import { NotAuthenticated } from '@feathersjs/errors'; import Debug from 'debug'; @@ -49,7 +49,7 @@ export default (originalSettings: string|AuthenticateHookSettings, ...originalSt } if (authentication) { - const authParams = omit(params, 'provider', 'authentication'); + const authParams = omit(params, 'provider', 'authentication', 'query'); debug('Authenticating with', authentication, strategies); diff --git a/packages/authentication/src/jwt.ts b/packages/authentication/src/jwt.ts index c2bb3538c1..62ed82f88a 100644 --- a/packages/authentication/src/jwt.ts +++ b/packages/authentication/src/jwt.ts @@ -1,4 +1,5 @@ import { NotAuthenticated } from '@feathersjs/errors'; +import { omit } from 'lodash'; import { AuthenticationRequest } from './core'; import { Params } from '@feathersjs/feathers'; import { IncomingMessage } from 'http'; @@ -36,14 +37,20 @@ export class JWTStrategy extends AuthenticationBaseStrategy { * @param params Service call parameters */ async getEntity (id: string, params: Params) { + const { entity } = this.configuration; const entityService = this.entityService; if (entityService === null) { throw new NotAuthenticated(`Could not find entity service`); } - // @ts-ignore - return entityService.get(id, params); + const result = await entityService.get(id, omit(params, 'provider')); + + if (!params.provider) { + return result; + } + + return entityService.get(id, { ...params, [entity]: result }); } async authenticate (authentication: AuthenticationRequest, params: Params) { @@ -51,7 +58,7 @@ export class JWTStrategy extends AuthenticationBaseStrategy { const { entity } = this.configuration; if (!accessToken) { - throw new NotAuthenticated('Not authenticated'); + throw new NotAuthenticated('No access token'); } const payload = await this.authentication.verifyAccessToken(accessToken, params.jwt); diff --git a/packages/authentication/test/jwt.test.ts b/packages/authentication/test/jwt.test.ts index df5618f558..569f0b920d 100644 --- a/packages/authentication/test/jwt.test.ts +++ b/packages/authentication/test/jwt.test.ts @@ -50,6 +50,18 @@ describe('authentication/jwt', () => { } }); + app.service('users').hooks({ + after: { + get: [context => { + if (context.params.provider) { + context.result.isExternal = true; + } + + return context; + }] + } + }); + user = await app.service('users').create({ name: 'David' }); @@ -61,6 +73,23 @@ describe('authentication/jwt', () => { payload = await service.verifyAccessToken(accessToken); }); + it('getEntity', async () => { + const [ strategy ] = app.service('authentication').getStrategies('jwt') as JWTStrategy[]; + + let entity = await strategy.getEntity(user.id, {}); + + assert.deepStrictEqual(entity, user); + + entity = await strategy.getEntity(user.id, { + provider: 'rest' + }); + + assert.deepStrictEqual(entity, { + ...user, + isExternal: true + }); + }); + describe('with authenticate hook', () => { it('fails for protected service and external call when not set', async () => { try { @@ -107,6 +136,21 @@ describe('authentication/jwt', () => { } }); + it('fails when accessToken is not set', async () => { + try { + await app.service('protected').get('test', { + provider: 'rest', + authentication: { + strategy: 'jwt' + } + }); + assert.fail('Should never get here'); + } catch (error) { + assert.strictEqual(error.name, 'NotAuthenticated'); + assert.strictEqual(error.message, 'No access token'); + } + }); + it('passes when authentication is set and merges params', async () => { const params = { provider: 'rest', diff --git a/packages/client/package-lock.json b/packages/client/package-lock.json new file mode 100644 index 0000000000..251b3c51f6 --- /dev/null +++ b/packages/client/package-lock.json @@ -0,0 +1,25 @@ +{ + "name": "@feathersjs/client", + "version": "4.0.0-pre.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + }, + "dependencies": { + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + } + } + } + } +} diff --git a/packages/tests/package-lock.json b/packages/tests/package-lock.json new file mode 100644 index 0000000000..2c14952cb8 --- /dev/null +++ b/packages/tests/package-lock.json @@ -0,0 +1,14 @@ +{ + "name": "@feathersjs/tests", + "version": "4.0.0-pre.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/node": { + "version": "11.13.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.10.tgz", + "integrity": "sha512-leUNzbFTMX94TWaIKz8N15Chu55F9QSH+INKayQr5xpkasBQBRF3qQXfo3/dOnMU/dEIit+Y/SU8HyOjq++GwA==", + "dev": true + } + } +}