diff --git a/package.json b/package.json
index fd651e272c..388a942324 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"install": "lerna bootstrap",
"publish": "lerna publish",
"lint": "semistandard \"packages/**/lib/**/*.js\" \"packages/**/test/**/*.js\" --fix",
- "test": "npm run lint && nyc lerna run test --ignore @feathersjs/authentication-*",
+ "test": "npm run lint && nyc lerna run test --ignore @feathersjs/authentication-client",
"test:client": "grunt"
},
"semistandard": {
diff --git a/packages/authentication-jwt/.npmignore b/packages/authentication-jwt/.npmignore
deleted file mode 100644
index 65e3ba2eda..0000000000
--- a/packages/authentication-jwt/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-test/
diff --git a/packages/authentication-jwt/CHANGELOG.md b/packages/authentication-jwt/CHANGELOG.md
deleted file mode 100644
index 05cd7659d3..0000000000
--- a/packages/authentication-jwt/CHANGELOG.md
+++ /dev/null
@@ -1,233 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-## [2.0.10](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.9...@feathersjs/authentication-jwt@2.0.10) (2019-01-26)
-
-**Note:** Version bump only for package @feathersjs/authentication-jwt
-
-
-
-
-
-## [2.0.9](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.8...@feathersjs/authentication-jwt@2.0.9) (2019-01-02)
-
-
-### Bug Fixes
-
-* Update adapter common tests ([#1135](https://github.com/feathersjs/feathers/issues/1135)) ([8166dda](https://github.com/feathersjs/feathers/commit/8166dda))
-
-
-
-
-
-
-## [2.0.8](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.7...@feathersjs/authentication-jwt@2.0.8) (2018-12-16)
-
-
-### Bug Fixes
-
-* **chore:** Properly configure and run code linter ([#1092](https://github.com/feathersjs/feathers/issues/1092)) ([fd3fc34](https://github.com/feathersjs/feathers/commit/fd3fc34))
-
-
-
-
-
-
-## [2.0.7](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.6...@feathersjs/authentication-jwt@2.0.7) (2018-10-26)
-
-
-### Bug Fixes
-
-* support a secretProvider ([#1063](https://github.com/feathersjs/feathers/issues/1063)) ([9da26ad](https://github.com/feathersjs/feathers/commit/9da26ad))
-
-
-
-
-
-
-## [2.0.6](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.5...@feathersjs/authentication-jwt@2.0.6) (2018-10-25)
-
-
-### Bug Fixes
-
-* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803))
-
-
-
-
-
-
-## [2.0.5](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.4...@feathersjs/authentication-jwt@2.0.5) (2018-09-21)
-
-**Note:** Version bump only for package @feathersjs/authentication-jwt
-
-
-
-
-
-
-## [2.0.4](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.3...@feathersjs/authentication-jwt@2.0.4) (2018-09-17)
-
-**Note:** Version bump only for package @feathersjs/authentication-jwt
-
-
-
-
-
-
-## [2.0.3](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-jwt@2.0.2...@feathersjs/authentication-jwt@2.0.3) (2018-09-02)
-
-**Note:** Version bump only for package @feathersjs/authentication-jwt
-
-
-## 2.0.2
-
-- Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462))
-
-## [v2.0.1](https://github.com/feathersjs/authentication-jwt/tree/v2.0.1) (2018-05-04)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v2.0.0...v2.0.1)
-
-**Closed issues:**
-
-- "No auth token" with socketio [\#60](https://github.com/feathersjs/authentication-jwt/issues/60)
-
-**Merged pull requests:**
-
-- Update sinon to the latest version 🚀 [\#59](https://github.com/feathersjs/authentication-jwt/pull/59) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update passport-jwt to the latest version 🚀 [\#58](https://github.com/feathersjs/authentication-jwt/pull/58) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon-chai to the latest version 🚀 [\#57](https://github.com/feathersjs/authentication-jwt/pull/57) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Fix typo on documentation link [\#56](https://github.com/feathersjs/authentication-jwt/pull/56) ([bernardobelchior](https://github.com/bernardobelchior))
-
-## [v2.0.0](https://github.com/feathersjs/authentication-jwt/tree/v2.0.0) (2018-01-21)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v1.0.2...v2.0.0)
-
-**Closed issues:**
-
-- Purpuse of lower casing header option [\#52](https://github.com/feathersjs/authentication-jwt/issues/52)
-- Get error in custom verify [\#48](https://github.com/feathersjs/authentication-jwt/issues/48)
-- Strategy loses the params on its way [\#36](https://github.com/feathersjs/authentication-jwt/issues/36)
-- Strategy succeeds even if user is not found [\#27](https://github.com/feathersjs/authentication-jwt/issues/27)
-- Swallowing errors when getting user from service? [\#14](https://github.com/feathersjs/authentication-jwt/issues/14)
-
-**Merged pull requests:**
-
-- Bring back Cookie token extractor [\#55](https://github.com/feathersjs/authentication-jwt/pull/55) ([daffl](https://github.com/daffl))
-- Properly pass through errors in the verifier [\#54](https://github.com/feathersjs/authentication-jwt/pull/54) ([daffl](https://github.com/daffl))
-- Update mocha to the latest version 🚀 [\#53](https://github.com/feathersjs/authentication-jwt/pull/53) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.2](https://github.com/feathersjs/authentication-jwt/tree/v1.0.2) (2018-01-03)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v1.0.1...v1.0.2)
-
-**Closed issues:**
-
-- Include data into JWT response [\#47](https://github.com/feathersjs/authentication-jwt/issues/47)
-
-**Merged pull requests:**
-
-- Update documentation to correspond with latest release [\#51](https://github.com/feathersjs/authentication-jwt/pull/51) ([daffl](https://github.com/daffl))
-- Update semistandard to the latest version 🚀 [\#50](https://github.com/feathersjs/authentication-jwt/pull/50) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-memory to the latest version 🚀 [\#49](https://github.com/feathersjs/authentication-jwt/pull/49) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.1](https://github.com/feathersjs/authentication-jwt/tree/v1.0.1) (2017-11-16)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v1.0.0...v1.0.1)
-
-**Closed issues:**
-
-- Cannot authenticate using feathers token [\#43](https://github.com/feathersjs/authentication-jwt/issues/43)
-
-**Merged pull requests:**
-
-- Add default export for better ES module \(TypeScript\) compatibility [\#46](https://github.com/feathersjs/authentication-jwt/pull/46) ([daffl](https://github.com/daffl))
-- Update @feathersjs/authentication to the latest version 🚀 [\#45](https://github.com/feathersjs/authentication-jwt/pull/45) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.0](https://github.com/feathersjs/authentication-jwt/tree/v1.0.0) (2017-11-01)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v1.0.0-pre.1...v1.0.0)
-
-**Merged pull requests:**
-
-- Update dependencies for release [\#44](https://github.com/feathersjs/authentication-jwt/pull/44) ([daffl](https://github.com/daffl))
-
-## [v1.0.0-pre.1](https://github.com/feathersjs/authentication-jwt/tree/v1.0.0-pre.1) (2017-10-25)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v0.3.2...v1.0.0-pre.1)
-
-**Closed issues:**
-
-- Clean generated project throws Error: You must provide a 'header' in your authentication configuration [\#35](https://github.com/feathersjs/authentication-jwt/issues/35)
-- Problem with "header" configuration. Collision of config options? [\#34](https://github.com/feathersjs/authentication-jwt/issues/34)
-- Please add example for Auth0 integration [\#23](https://github.com/feathersjs/authentication-jwt/issues/23)
-
-**Merged pull requests:**
-
-- Update to Feathers v3 [\#42](https://github.com/feathersjs/authentication-jwt/pull/42) ([daffl](https://github.com/daffl))
-- Rename repository and update to npm scope [\#41](https://github.com/feathersjs/authentication-jwt/pull/41) ([daffl](https://github.com/daffl))
-- Update to new plugin infrastructure [\#40](https://github.com/feathersjs/authentication-jwt/pull/40) ([daffl](https://github.com/daffl))
-- Update mocha to the latest version 🚀 [\#38](https://github.com/feathersjs/authentication-jwt/pull/38) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#37](https://github.com/feathersjs/authentication-jwt/pull/37) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update jsonwebtoken to the latest version 🚀 [\#33](https://github.com/feathersjs/authentication-jwt/pull/33) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update passport-jwt to the latest version 🚀 [\#32](https://github.com/feathersjs/authentication-jwt/pull/32) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update debug to the latest version 🚀 [\#31](https://github.com/feathersjs/authentication-jwt/pull/31) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#29](https://github.com/feathersjs/authentication-jwt/pull/29) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update chai to the latest version 🚀 [\#26](https://github.com/feathersjs/authentication-jwt/pull/26) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.3.2](https://github.com/feathersjs/authentication-jwt/tree/v0.3.2) (2017-07-05)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v0.3.1...v0.3.2)
-
-**Closed issues:**
-
-- Verifier is not called in a custom service route [\#24](https://github.com/feathersjs/authentication-jwt/issues/24)
-- Module is using the wrong default config key [\#20](https://github.com/feathersjs/authentication-jwt/issues/20)
-- Using feathers-authentication-jwt for api key authentication [\#18](https://github.com/feathersjs/authentication-jwt/issues/18)
-- Possible to return user id in the payload when authenticate? [\#13](https://github.com/feathersjs/authentication-jwt/issues/13)
-
-**Merged pull requests:**
-
-- Add backwards compatible fallback for \#21 [\#25](https://github.com/feathersjs/authentication-jwt/pull/25) ([daffl](https://github.com/daffl))
-- use the correct config key name. Closes \#20 [\#21](https://github.com/feathersjs/authentication-jwt/pull/21) ([ekryski](https://github.com/ekryski))
-- Update feathers-socketio to the latest version 🚀 [\#19](https://github.com/feathersjs/authentication-jwt/pull/19) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update semistandard to the latest version 🚀 [\#17](https://github.com/feathersjs/authentication-jwt/pull/17) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-hooks to the latest version 🚀 [\#16](https://github.com/feathersjs/authentication-jwt/pull/16) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update dependencies to enable Greenkeeper 🌴 [\#15](https://github.com/feathersjs/authentication-jwt/pull/15) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.3.1](https://github.com/feathersjs/authentication-jwt/tree/v0.3.1) (2016-12-29)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v0.3.0...v0.3.1)
-
-**Closed issues:**
-
-- Remove `typeof 'string'` check for secret [\#9](https://github.com/feathersjs/authentication-jwt/issues/9)
-
-**Merged pull requests:**
-
-- Fix check for secret not being undefined [\#12](https://github.com/feathersjs/authentication-jwt/pull/12) ([daffl](https://github.com/daffl))
-
-## [v0.3.0](https://github.com/feathersjs/authentication-jwt/tree/v0.3.0) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v0.2.0...v0.3.0)
-
-**Closed issues:**
-
-- Add docs section on expected request params [\#7](https://github.com/feathersjs/authentication-jwt/issues/7)
-- Add support for 'Bearer token' style header [\#5](https://github.com/feathersjs/authentication-jwt/issues/5)
-- Re-using the same JWT with Primus / websockets [\#4](https://github.com/feathersjs/authentication-jwt/issues/4)
-
-**Merged pull requests:**
-
-- Document expected request data [\#8](https://github.com/feathersjs/authentication-jwt/pull/8) ([marshallswain](https://github.com/marshallswain))
-- Add support for using the Bearer scheme in the Auth header [\#6](https://github.com/feathersjs/authentication-jwt/pull/6) ([timelesshaze](https://github.com/timelesshaze))
-
-## [v0.2.0](https://github.com/feathersjs/authentication-jwt/tree/v0.2.0) (2016-11-23)
-[Full Changelog](https://github.com/feathersjs/authentication-jwt/compare/v0.1.0...v0.2.0)
-
-**Closed issues:**
-
-- Automatically attempt to put the entityId in the payload [\#2](https://github.com/feathersjs/authentication-jwt/issues/2)
-- what's the difference between this and feathers-authentication-local? [\#1](https://github.com/feathersjs/authentication-jwt/issues/1)
-
-**Merged pull requests:**
-
-- Adding payload support and fixing config values [\#3](https://github.com/feathersjs/authentication-jwt/pull/3) ([ekryski](https://github.com/ekryski))
-
-## [v0.1.0](https://github.com/feathersjs/authentication-jwt/tree/v0.1.0) (2016-11-16)
-
-
-\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/packages/authentication-jwt/LICENSE b/packages/authentication-jwt/LICENSE
deleted file mode 100644
index 6bfc0adefc..0000000000
--- a/packages/authentication-jwt/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2018 Feathers
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/packages/authentication-jwt/README.md b/packages/authentication-jwt/README.md
deleted file mode 100644
index d802539412..0000000000
--- a/packages/authentication-jwt/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# @feathersjs/authentication-jwt
-
-[![Build Status](https://travis-ci.org/feathersjs/feathers.png?branch=master)](https://travis-ci.org/feathersjs/feathers)
-[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/authentication-jwt)](https://david-dm.org/feathersjs/feathers?path=packages/authentication-jwt)
-[![Download Status](https://img.shields.io/npm/dm/@feathersjs/authentication-jwt.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/authentication-jwt)
-
-> JWT authentication strategy for feathers-authentication using Passport
-
-## Installation
-
-```
-npm install @feathersjs/authentication-jwt --save
-```
-
-## Quick example
-
-```js
-const feathers = require('@feathersjs/feathers');
-const authentication = require('feathers-authentication');
-const jwt = require('@feathersjs/authentication-jwt');
-const app = feathers();
-
-// Setup authentication
-app.configure(authentication(settings));
-app.configure(jwt());
-
-// Setup a hook to only allow valid JWTs to authenticate
-// and get new JWT access tokens
-app.service('authentication').hooks({
- before: {
- create: [
- authentication.hooks.authenticate(['jwt'])
- ]
- }
-});
-```
-
-## Documentation
-
-Please refer to the [@feathersjs/authentication-jwt documentation](https://docs.feathersjs.com/api/authentication/jwt.html) for more details.
-
-
-## License
-
-Copyright (c) 2018
-
-Licensed under the [MIT license](LICENSE).
diff --git a/packages/authentication-jwt/lib/index.js b/packages/authentication-jwt/lib/index.js
deleted file mode 100644
index 913f8089a4..0000000000
--- a/packages/authentication-jwt/lib/index.js
+++ /dev/null
@@ -1,104 +0,0 @@
-const Debug = require('debug');
-const merge = require('lodash.merge');
-const omit = require('lodash.omit');
-const pick = require('lodash.pick');
-const DefaultVerifier = require('./verifier');
-const passportJwt = require('passport-jwt');
-
-const debug = Debug('@feathersjs/authentication-jwt');
-const defaults = {
- name: 'jwt',
- bodyKey: 'accessToken'
-};
-
-const KEYS = [
- 'secret',
- 'header',
- 'entity',
- 'service',
- 'passReqToCallback',
- 'session',
- 'jwt'
-];
-
-function init (options = {}) {
- return function jwtAuth () {
- const app = this;
- const _super = app.setup;
- const { ExtractJwt, Strategy } = passportJwt;
-
- if (!app.passport) {
- throw new Error(`Can not find app.passport. Did you initialize feathers-authentication before @feathersjs/authentication-jwt?`);
- }
-
- const authOptions = app.get('auth') || app.get('authentication') || {};
- const jwtOptions = authOptions[options.name] || {};
- // NOTE (EK): Pull from global auth config to support legacy auth for an easier transition.
- const jwtSettings = merge({}, defaults, pick(authOptions, KEYS), jwtOptions, omit(options, ['Verifier']));
-
- if (typeof jwtSettings.header !== 'string') {
- throw new Error(`You must provide a 'header' in your authentication configuration or pass one explicitly`);
- }
-
- const extractors = [
- ExtractJwt.fromAuthHeaderWithScheme('jwt'),
- ExtractJwt.fromAuthHeaderAsBearerToken(),
- ExtractJwt.fromHeader(jwtSettings.header.toLowerCase()),
- ExtractJwt.fromBodyField(jwtSettings.bodyKey)
- ];
-
- if (authOptions.cookie && authOptions.cookie.name) {
- extractors.push(function (req) {
- if (req && req.cookies) {
- return req.cookies[authOptions.cookie.name];
- }
-
- return null;
- });
- }
-
- let Verifier = DefaultVerifier;
- let strategyOptions = merge({
- secretOrKey: typeof jwtSettings.secret !== 'function' ? jwtSettings.secret : null,
- secretOrKeyProvider: typeof jwtSettings.secret === 'function' ? jwtSettings.secret : null,
- jwtFromRequest: ExtractJwt.fromExtractors(extractors)
- }, jwtSettings.jwt, omit(jwtSettings, ['jwt', 'header', 'secret']));
-
- // Normalize algorithm key
- if (!strategyOptions.algorithms && strategyOptions.algorithm) {
- strategyOptions.algorithms = Array.isArray(strategyOptions.algorithm) ? strategyOptions.algorithm : [strategyOptions.algorithm];
- delete strategyOptions.algorithm;
- }
-
- // Support passing a custom verifier
- if (options.Verifier) {
- Verifier = options.Verifier;
- }
-
- app.setup = function () {
- let result = _super.apply(this, arguments);
- let verifier = new Verifier(app, jwtSettings);
-
- if (!verifier.verify) {
- throw new Error(`Your verifier must implement a 'verify' function. It should have the same signature as a jwt passport verify callback.`);
- }
-
- // Register 'jwt' strategy with passport
- debug('Registering jwt authentication strategy with options:', strategyOptions);
- app.passport.use(jwtSettings.name, new Strategy(strategyOptions, verifier.verify.bind(verifier)));
- app.passport.options(jwtSettings.name, jwtSettings);
-
- return result;
- };
- };
-}
-
-module.exports = init;
-
-// Exposed Modules
-Object.assign(module.exports, {
- defaults,
- default: init,
- ExtractJwt: passportJwt.ExtractJwt,
- Verifier: DefaultVerifier
-});
diff --git a/packages/authentication-jwt/lib/verifier.js b/packages/authentication-jwt/lib/verifier.js
deleted file mode 100644
index 8d62b53313..0000000000
--- a/packages/authentication-jwt/lib/verifier.js
+++ /dev/null
@@ -1,40 +0,0 @@
-const Debug = require('debug');
-const debug = Debug('@feathersjs/authentication-jwt:verify');
-
-class JWTVerifier {
- constructor (app, options = {}) {
- this.app = app;
- this.options = options;
- this.service = typeof options.service === 'string' ? app.service(options.service) : options.service;
-
- if (!this.service) {
- throw new Error(`options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-jwt.`);
- }
-
- this.verify = this.verify.bind(this);
- }
-
- verify (req, payload, done) {
- debug('Received JWT payload', payload);
-
- const id = payload[`${this.options.entity}Id`];
-
- if (id === undefined) {
- debug(`JWT payload does not contain ${this.options.entity}Id`);
- return done(null, {}, payload);
- }
-
- debug(`Looking up ${this.options.entity} by id`, id);
-
- this.service.get(id).then(entity => {
- const newPayload = { [`${this.options.entity}Id`]: id };
- return done(null, entity, newPayload);
- })
- .catch(error => {
- debug(`Error populating ${this.options.entity} with id ${id}`, error);
- return done(error);
- });
- }
-}
-
-module.exports = JWTVerifier;
diff --git a/packages/authentication-jwt/package.json b/packages/authentication-jwt/package.json
deleted file mode 100644
index 022a0a9f57..0000000000
--- a/packages/authentication-jwt/package.json
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- "name": "@feathersjs/authentication-jwt",
- "description": "JWT authentication strategy for feathers-authentication using Passport",
- "version": "2.0.10",
- "homepage": "https://feathersjs.com",
- "main": "lib/",
- "keywords": [
- "feathers",
- "feathers-plugin"
- ],
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git://github.com/feathersjs/feathers.git"
- },
- "author": {
- "name": "Feathers contributors",
- "email": "hello@feathersjs.com",
- "url": "https://feathersjs.com"
- },
- "contributors": [],
- "bugs": {
- "url": "https://github.com/feathersjs/feathers/issues"
- },
- "engines": {
- "node": ">= 6"
- },
- "scripts": {
- "test": "mocha --opts ../../mocha.opts"
- },
- "directories": {
- "lib": "lib"
- },
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@feathersjs/errors": "^3.3.6",
- "debug": "^4.1.1",
- "lodash.merge": "^4.6.1",
- "lodash.omit": "^4.5.0",
- "lodash.pick": "^4.4.0",
- "passport-jwt": "^4.0.0"
- },
- "devDependencies": {
- "@feathersjs/authentication": "^2.1.16",
- "@feathersjs/express": "^1.3.1",
- "@feathersjs/feathers": "^3.3.1",
- "@feathersjs/socketio": "^3.2.9",
- "body-parser": "^1.18.3",
- "chai": "^4.2.0",
- "feathers-memory": "^3.0.2",
- "jsonwebtoken": "^8.4.0",
- "mocha": "^5.2.0",
- "sinon": "^7.2.3",
- "sinon-chai": "^3.3.0"
- }
-}
diff --git a/packages/authentication-jwt/test/index.test.js b/packages/authentication-jwt/test/index.test.js
deleted file mode 100644
index 8dd8df5d7b..0000000000
--- a/packages/authentication-jwt/test/index.test.js
+++ /dev/null
@@ -1,472 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const JWT = require('jsonwebtoken');
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-const memory = require('feathers-memory');
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-const passportJWT = require('passport-jwt');
-const jwt = require('../lib');
-
-const { Verifier, ExtractJwt } = jwt;
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('@feathersjs/authentication-jwt', () => {
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib')).to.equal('function');
- });
-
- it('basic functionality', () => {
- expect(typeof jwt).to.equal('function');
- expect(jwt.default).to.equal(jwt);
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- expect(typeof jwt.Verifier).to.equal('function');
- });
-
- it('exposes the passport-jwt ExtractJwt functions', () => {
- expect(typeof ExtractJwt).to.equal('object');
- expect(typeof jwt.ExtractJwt).to.equal('object');
- expect(typeof ExtractJwt.fromHeader).to.equal('function');
- expect(typeof ExtractJwt.fromBodyField).to.equal('function');
- expect(typeof ExtractJwt.fromUrlQueryParameter).to.equal('function');
- expect(typeof ExtractJwt.fromAuthHeaderWithScheme).to.equal('function');
- expect(typeof ExtractJwt.fromExtractors).to.equal('function');
- });
-
- describe('secretOrKey', () => {
- describe('initialization', () => {
- let app;
- let validToken;
- let Payload = { userId: 0 };
-
- beforeEach(done => {
- app = expressify(feathers());
- app.use('/users', memory());
- app.configure(authentication({
- secret: 'supersecret',
- cookie: {
- enabled: true,
- name: 'feathers-jwt'
- }
- }));
-
- app.service('users').create({
- name: 'test user'
- });
-
- JWT.sign(Payload, 'supersecret', app.get('authentication').jwt, (error, token) => {
- if (error) { return done(error); }
- validToken = token;
- done();
- });
- });
-
- it('throws an error if passport has not been registered', () => {
- expect(() => {
- expressify(feathers()).configure(jwt());
- }).to.throw();
- });
-
- it('throws an error if header is not a string', () => {
- expect(() => {
- app.configure(jwt({ header: true }));
- app.setup();
- }).to.throw();
- });
-
- it('registers the jwt passport strategy', () => {
- sinon.spy(app.passport, 'use');
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt());
- app.setup();
-
- expect(passportJWT.Strategy).to.have.been.calledOnce;
- expect(app.passport.use).to.have.been.calledWith('jwt');
-
- app.passport.use.restore();
- passportJWT.Strategy.restore();
- });
-
- it('registers the strategy options', () => {
- sinon.spy(app.passport, 'options');
- app.configure(jwt());
- app.setup();
-
- expect(app.passport.options).to.have.been.calledOnce;
-
- app.passport.options.restore();
- });
-
- describe('passport strategy options', () => {
- let authOptions;
- let args;
-
- beforeEach(() => {
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt({ custom: true }));
- app.setup();
- authOptions = app.get('authentication');
- args = passportJWT.Strategy.getCall(0).args[0];
- });
-
- afterEach(() => {
- passportJWT.Strategy.restore();
- });
-
- it('sets secretOrKey', () => {
- expect(args.secretOrKey).to.equal('supersecret');
- });
-
- it('sets jwtFromRequest', () => {
- expect(args.jwtFromRequest).to.be.a('function');
- });
-
- it('sets session', () => {
- expect(args.session).to.equal(authOptions.session);
- });
-
- it('sets entity', () => {
- expect(args.entity).to.equal(authOptions.entity);
- });
-
- it('sets service', () => {
- expect(args.service).to.equal(authOptions.service);
- });
-
- it('sets passReqToCallback', () => {
- expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback);
- });
-
- it('sets algorithms', () => {
- expect(args.algorithms).to.deep.equal([authOptions.jwt.algorithm]);
- });
-
- it('sets audience', () => {
- expect(args.audience).to.equal(authOptions.jwt.audience);
- });
-
- it('sets expiresIn', () => {
- expect(args.expiresIn).to.equal(authOptions.jwt.expiresIn);
- });
-
- it('sets issuer', () => {
- expect(args.issuer).to.equal(authOptions.jwt.issuer);
- });
-
- it('sets subject', () => {
- expect(args.subject).to.equal(authOptions.jwt.subject);
- });
-
- it('sets header', () => {
- expect(args.header).to.deep.equal(authOptions.jwt.header);
- });
-
- it('supports setting custom options', () => {
- expect(args.custom).to.equal(true);
- });
- });
-
- it('supports overriding default options', () => {
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt({ subject: 'custom' }));
- app.setup();
-
- expect(passportJWT.Strategy.getCall(0).args[0].subject).to.equal('custom');
-
- passportJWT.Strategy.restore();
- });
-
- it('pulls options from global config with custom name', () => {
- sinon.spy(passportJWT, 'Strategy');
- let authOptions = app.get('authentication');
- authOptions.custom = { entity: 'device' };
- app.set('authentication', authOptions);
-
- app.configure(jwt({ name: 'custom' }));
- app.setup();
-
- expect(passportJWT.Strategy.getCall(0).args[0].entity).to.equal('device');
- expect(passportJWT.Strategy.getCall(0).args[0].bodyKey).to.equal('accessToken');
-
- passportJWT.Strategy.restore();
- });
-
- describe('Bearer scheme', () => {
- it('authenticates using the default verifier', () => {
- const req = {
- query: {},
- body: {},
- headers: {
- authorization: `Bearer ${validToken}`
- },
- cookies: {}
- };
-
- app.configure(jwt());
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.success).to.equal(true);
- });
- });
- });
-
- describe('Cookie', () => {
- it('authenticates using a cookie if set in options', () => {
- const req = {
- query: {},
- body: {},
- headers: {},
- cookies: {
- 'feathers-jwt': validToken
- }
- };
-
- app.configure(jwt());
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.success).to.equal(true);
- });
- });
- });
-
- describe('custom Verifier', () => {
- it('throws an error if a verify function is missing', () => {
- expect(() => {
- class CustomVerifier {
- constructor (app) {
- this.app = app;
- }
- }
-
- app.configure(jwt({ Verifier: CustomVerifier }));
- app.setup();
- }).to.throw();
- });
-
- it('verifies through custom verify function', () => {
- const req = {
- query: {},
- body: {},
- headers: {
- authorization: `${validToken}`
- },
- cookies: {}
- };
- class CustomVerifier extends Verifier {
- verify (req, payload, done) {
- expect(payload.userId).to.equal(Payload.userId);
- done(null, payload, Payload);
- }
- }
-
- app.configure(jwt({ Verifier: CustomVerifier }));
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.data.payload.userId).to.deep.equal(Payload.userId);
- });
- });
- });
- });
- });
-
- describe('secretOrKeyProvider', () => {
- describe('initialization', () => {
- let app;
- let validToken;
- let Payload = { userId: 0 };
-
- beforeEach(done => {
- app = expressify(feathers());
- app.use('/users', memory());
- app.configure(authentication({
- secret: sinon.spy(function (request, token, done) {
- done(null, 'supersecret');
- }),
- cookie: {
- enabled: true,
- name: 'feathers-jwt'
- }
- }));
-
- app.service('users').create({
- name: 'test user'
- });
-
- JWT.sign(Payload, 'supersecret', app.get('authentication').jwt, (error, token) => {
- if (error) { return done(error); }
- validToken = token;
- done();
- });
- });
-
- it('throws an error if passport has not been registered', () => {
- expect(() => {
- expressify(feathers()).configure(jwt());
- }).to.throw();
- });
-
- it('throws an error if header is not a string', () => {
- expect(() => {
- app.configure(jwt({ header: true }));
- app.setup();
- }).to.throw();
- });
-
- it('registers the jwt passport strategy', () => {
- sinon.spy(app.passport, 'use');
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt());
- app.setup();
-
- expect(passportJWT.Strategy).to.have.been.calledOnce;
- expect(app.passport.use).to.have.been.calledWith('jwt');
-
- app.passport.use.restore();
- passportJWT.Strategy.restore();
- });
-
- it('registers the strategy options', () => {
- sinon.spy(app.passport, 'options');
- app.configure(jwt());
- app.setup();
-
- expect(app.passport.options).to.have.been.calledOnce;
-
- app.passport.options.restore();
- });
-
- describe('passport strategy options', () => {
- let args;
-
- beforeEach(() => {
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt({ custom: true }));
- app.setup();
- args = passportJWT.Strategy.getCall(0).args[0];
- });
-
- afterEach(() => {
- passportJWT.Strategy.restore();
- });
-
- it('sets secretOrKeyProvider', () => {
- expect(args.secretOrKeyProvider).to.be.a('function');
- });
- });
-
- it('supports overriding default options', () => {
- sinon.spy(passportJWT, 'Strategy');
- app.configure(jwt({ subject: 'custom' }));
- app.setup();
-
- expect(passportJWT.Strategy.getCall(0).args[0].subject).to.equal('custom');
-
- passportJWT.Strategy.restore();
- });
-
- it('pulls options from global config with custom name', () => {
- sinon.spy(passportJWT, 'Strategy');
- let authOptions = app.get('authentication');
- authOptions.custom = { entity: 'device' };
- app.set('authentication', authOptions);
-
- app.configure(jwt({ name: 'custom' }));
- app.setup();
-
- expect(passportJWT.Strategy.getCall(0).args[0].entity).to.equal('device');
- expect(passportJWT.Strategy.getCall(0).args[0].bodyKey).to.equal('accessToken');
-
- passportJWT.Strategy.restore();
- });
-
- describe('Bearer scheme', () => {
- it('authenticates using the default verifier', () => {
- const req = {
- query: {},
- body: {},
- headers: {
- authorization: `Bearer ${validToken}`
- },
- cookies: {}
- };
-
- app.configure(jwt());
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.success).to.equal(true);
- });
- });
- });
-
- describe('Cookie', () => {
- it('authenticates using a cookie if set in options', () => {
- const req = {
- query: {},
- body: {},
- headers: {},
- cookies: {
- 'feathers-jwt': validToken
- }
- };
-
- app.configure(jwt());
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.success).to.equal(true);
- });
- });
- });
-
- describe('custom Verifier', () => {
- it('throws an error if a verify function is missing', () => {
- expect(() => {
- class CustomVerifier {
- constructor (app) {
- this.app = app;
- }
- }
-
- app.configure(jwt({ Verifier: CustomVerifier }));
- app.setup();
- }).to.throw();
- });
-
- it('verifies through custom verify function', () => {
- const req = {
- query: {},
- body: {},
- headers: {
- authorization: `${validToken}`
- },
- cookies: {}
- };
- class CustomVerifier extends Verifier {
- verify (req, payload, done) {
- expect(payload.userId).to.equal(Payload.userId);
- done(null, payload, Payload);
- }
- }
-
- app.configure(jwt({ Verifier: CustomVerifier }));
- app.setup();
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.data.payload.userId).to.deep.equal(Payload.userId);
- });
- });
- });
- });
- });
-});
diff --git a/packages/authentication-jwt/test/integration.test.js b/packages/authentication-jwt/test/integration.test.js
deleted file mode 100644
index eac4b3769a..0000000000
--- a/packages/authentication-jwt/test/integration.test.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-const memory = require('feathers-memory');
-const { expect } = require('chai');
-const jwt = require('../lib');
-
-describe('integration', () => {
- it('verifies', () => {
- const User = {
- email: 'admin@feathersjs.com',
- password: 'password'
- };
-
- const req = {
- query: {},
- body: {},
- headers: {},
- cookies: {}
- };
-
- const issueJWT = () => {
- return hook => {
- const app = hook.app;
- const id = hook.result.id;
- return app.passport.createJWT({
- userId: id
- }, app.get('authentication')).then(accessToken => {
- hook.result.accessToken = accessToken;
- return Promise.resolve(hook);
- });
- };
- };
-
- const app = expressify(feathers());
-
- app.use('/users', memory())
- .configure(authentication({ secret: 'secret' }))
- .configure(jwt());
-
- app.service('users').hooks({
- after: {
- create: issueJWT()
- }
- });
-
- app.setup();
-
- return app.service('users').create(User).then(user => {
- req.headers = { 'authorization': user.accessToken };
-
- return app.authenticate('jwt')(req).then(result => {
- expect(result.success).to.equal(true);
- expect(result.data.user.email).to.equal(User.email);
- expect(result.data.user.password).to.not.equal(undefined);
- });
- });
- });
-
- it('errors when user is not found', () => {
- const app = expressify(feathers());
- const req = {
- query: {},
- body: {},
- headers: {},
- cookies: {}
- };
-
- app.use('/users', memory())
- .configure(authentication({ secret: 'secret' }))
- .configure(jwt());
-
- app.setup();
-
- return app.passport.createJWT({
- userId: 'wrong'
- }, app.get('authentication')).then(accessToken => {
- req.headers = { 'authorization': accessToken };
-
- return app.authenticate('jwt')(req).then(() => {
- throw new Error('Should never get here');
- }).catch(error => {
- expect(error.name).to.equal('NotFound');
- });
- });
- });
-});
diff --git a/packages/authentication-jwt/test/verifier.test.js b/packages/authentication-jwt/test/verifier.test.js
deleted file mode 100644
index 0aebb10153..0000000000
--- a/packages/authentication-jwt/test/verifier.test.js
+++ /dev/null
@@ -1,142 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-
-const { Verifier } = require('../lib');
-
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('Verifier', () => {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- app = expressify(feathers());
- user = { email: 'admin@feathersjs.com' };
- service = {
- id: 'id',
- get: sinon.stub().returns(Promise.resolve(user))
- };
-
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = app.get('authentication');
- verifier = new Verifier(app, options);
- });
-
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib/verifier')).to.equal('function');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- });
-
- describe('constructor', () => {
- it('retains an app reference', () => {
- expect(verifier.app).to.deep.equal(app);
- });
-
- it('sets options', () => {
- expect(verifier.options).to.deep.equal(options);
- });
-
- it('sets service using service path', () => {
- expect(verifier.service).to.deep.equal(app.service('users'));
- });
-
- it('sets a passed in service instance', () => {
- options.service = service;
- expect(new Verifier(app, options).service).to.deep.equal(service);
- });
-
- describe('when service is undefined', () => {
- it('throws an error', () => {
- expect(() => {
- new Verifier(app, {}); // eslint-disable-line
- }).to.throw();
- });
- });
- });
-
- describe('verify', () => {
- describe('when userId is present in payload', () => {
- it('calls get on the provided service', done => {
- verifier.verify({}, { userId: 1 }, () => {
- expect(service.get).to.have.been.calledOnce;
- expect(service.get).to.have.been.calledWith(1);
- done();
- });
- });
-
- it('returns the payload', done => {
- const payload = { userId: 1 };
-
- verifier.verify({}, payload, (error, entity, p) => {
- if (error) {
- return done(error);
- }
-
- expect(p).to.deep.equal(payload);
- done();
- });
- });
-
- it('returns the entity', done => {
- verifier.verify({}, { userId: 1 }, (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- describe('when service call errors', () => {
- it('returns the payload', done => {
- const service = {
- id: 'id',
- get: () => Promise.reject(new Error('User missing'))
- };
-
- options.service = service;
- const erroringVerifier = new Verifier(app, options);
- const payload = { userId: 1 };
- erroringVerifier.verify({}, payload, (error, entity, p) => {
- expect(error).to.exist;
- expect(error.message).to.equal('User missing');
- done();
- });
- });
- });
- });
-
- describe('when userId is not present in payload', () => {
- it('does not call get on the provided service', done => {
- verifier.verify({}, {}, () => {
- expect(service.get).to.not.have.been.called;
- done();
- });
- });
-
- it('returns the payload', done => {
- const payload = { name: 'Eric' };
- verifier.verify({}, payload, (error, entity, p) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal({});
- expect(p).to.deep.equal(payload);
- done();
- });
- });
- });
- });
-});
diff --git a/packages/authentication-local/lib/hooks/hash-password.js b/packages/authentication-local/lib/hooks/hash-password.js
index a5649ad5c2..b69540efb8 100644
--- a/packages/authentication-local/lib/hooks/hash-password.js
+++ b/packages/authentication-local/lib/hooks/hash-password.js
@@ -1,54 +1,51 @@
-const hasher = require('../utils/hash');
-const { merge, get, set, cloneDeep } = require('lodash');
-const Debug = require('debug');
+const { get, set, cloneDeep } = require('lodash');
+const { BadRequest } = require('@feathersjs/errors');
-const debug = Debug('@feathersjs/authentication-local:hooks:hash-password');
+const debug = require('debug')('@feathersjs/authentication-local/hooks/hash-password');
+
+module.exports = function hashPassword (field, options = {}) {
+ if (!field) {
+ throw new Error('The hashPassword hook requires a field name option');
+ }
-module.exports = function hashPassword (options = {}) {
return function (context) {
if (context.type !== 'before') {
- return Promise.reject(new Error(`The 'hashPassword' hook should only be used as a 'before' hook.`));
+ return Promise.reject(
+ new Error(`The 'hashPassword' hook should only be used as a 'before' hook`)
+ );
}
- const app = context.app;
- const authOptions = app.get('authentication') || {};
-
- options = merge({ passwordField: 'password' }, authOptions.local, options);
-
- debug('Running hashPassword hook with options:', options);
-
- const field = options.passwordField;
- const hashPw = options.hash || hasher;
+ const { app, data, params } = context;
+ const password = get(data, field);
- if (typeof field !== 'string') {
- return Promise.reject(new Error(`You must provide a 'passwordField' in your authentication configuration or pass one explicitly`));
+ if (data === undefined || password === undefined) {
+ debug(`hook.data or hook.data.${field} is undefined. Skipping hashPassword hook.`);
+ return Promise.resolve(context);
}
- if (typeof hashPw !== 'function') {
- return Promise.reject(new Error(`'hash' must be a function that takes a password and returns Promise that resolves with a hashed password.`));
- }
+ const serviceName = options.authentication || app.get('defaultAuthentication');
+ const authService = app.service(serviceName);
+ const { strategy = 'local' } = options;
- if (context.data === undefined) {
- debug(`hook.data is undefined. Skipping hashPassword hook.`);
- return Promise.resolve(context);
+ if (!authService || typeof authService.getStrategies !== 'function') {
+ return Promise.reject(
+ new BadRequest(`Could not find '${serviceName}' service to hash password`)
+ );
}
- const dataIsArray = Array.isArray(context.data);
- const data = dataIsArray ? context.data : [ context.data ];
+ const [ localStrategy ] = authService.getStrategies(strategy);
- return Promise.all(data.map(item => {
- const password = get(item, field);
- if (password) {
- return hashPw(password).then(hashedPassword =>
- set(cloneDeep(item), field, hashedPassword)
- );
- }
+ if (!localStrategy || typeof localStrategy.hashPassword !== 'function') {
+ return Promise.reject(
+ new BadRequest(`Could not find '${strategy}' strategy to hash password`)
+ );
+ }
- return item;
- })).then(results => {
- context.data = dataIsArray ? results : results[0];
+ return localStrategy.hashPassword(password, params)
+ .then(hashedPassword => {
+ context.data = set(cloneDeep(data), field, hashedPassword);
- return context;
- });
+ return context;
+ });
};
};
diff --git a/packages/authentication-local/lib/hooks/index.js b/packages/authentication-local/lib/hooks/index.js
deleted file mode 100644
index 6059b9388c..0000000000
--- a/packages/authentication-local/lib/hooks/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const hashPassword = require('./hash-password');
-const protect = require('./protect');
-
-module.exports = {
- hashPassword,
- protect
-};
diff --git a/packages/authentication-local/lib/index.js b/packages/authentication-local/lib/index.js
index c5965c0c2f..2ab9462a0d 100644
--- a/packages/authentication-local/lib/index.js
+++ b/packages/authentication-local/lib/index.js
@@ -1,69 +1,7 @@
-const Debug = require('debug');
-const { merge, omit, pick } = require('lodash');
-const hooks = require('./hooks');
-const DefaultVerifier = require('./verifier');
+const LocalStrategy = require('./strategy');
+const hashPassword = require('./hooks/hash-password');
+const protect = require('./hooks/protect');
-const passportLocal = require('passport-local');
-
-const debug = Debug('@feathersjs/authentication-local');
-const defaults = {
- name: 'local',
- usernameField: 'email',
- passwordField: 'password'
-};
-
-const KEYS = [
- 'entity',
- 'service',
- 'passReqToCallback',
- 'session'
-];
-
-function init (options = {}) {
- return function localAuth () {
- const app = this;
- const _super = app.setup;
-
- if (!app.passport) {
- throw new Error(`Can not find app.passport. Did you initialize feathers-authentication before @feathersjs/authentication-local?`);
- }
-
- let name = options.name || defaults.name;
- let authOptions = app.get('authentication') || {};
- let localOptions = authOptions[name] || {};
-
- // NOTE (EK): Pull from global auth config to support legacy auth for an easier transition.
- const localSettings = merge({}, defaults, pick(authOptions, KEYS), localOptions, omit(options, ['Verifier']));
- let Verifier = DefaultVerifier;
-
- if (options.Verifier) {
- Verifier = options.Verifier;
- }
-
- app.setup = function () {
- let result = _super.apply(this, arguments);
- let verifier = new Verifier(app, localSettings);
-
- if (!verifier.verify) {
- throw new Error(`Your verifier must implement a 'verify' function. It should have the same signature as a local passport verify callback.`);
- }
-
- // Register 'local' strategy with passport
- debug('Registering local authentication strategy with options:', localSettings);
- app.passport.use(localSettings.name, new passportLocal.Strategy(localSettings, verifier.verify.bind(verifier)));
- app.passport.options(localSettings.name, localSettings);
-
- return result;
- };
- };
-}
-
-module.exports = init;
-
-// Exposed Modules
-Object.assign(module.exports, {
- default: init,
- defaults,
- hooks,
- Verifier: DefaultVerifier
-});
+exports.LocalStrategy = LocalStrategy;
+exports.protect = protect;
+exports.hashPassword = hashPassword;
diff --git a/packages/authentication-local/lib/strategy.js b/packages/authentication-local/lib/strategy.js
new file mode 100644
index 0000000000..628c31aa73
--- /dev/null
+++ b/packages/authentication-local/lib/strategy.js
@@ -0,0 +1,123 @@
+const bcrypt = require('bcryptjs');
+const { NotAuthenticated } = require('@feathersjs/errors');
+const { get, omit } = require('lodash');
+
+const debug = require('debug')('@feathersjs/authentication-local/strategy');
+
+module.exports = class LocalStrategy {
+ setAuthentication (auth) {
+ this.authentication = auth;
+ }
+
+ setApplication (app) {
+ this.app = app;
+ }
+
+ setName (name) {
+ this.name = name;
+ }
+
+ verifyConfiguration () {
+ const config = this.configuration;
+
+ [ 'usernameField', 'passwordField' ].forEach(prop => {
+ if (typeof config[prop] !== 'string') {
+ throw new Error(`'${this.name}' authentication strategy requires a '${prop}' setting`);
+ }
+ });
+ }
+
+ get configuration () {
+ const authConfig = this.authentication.configuration;
+ const config = authConfig[this.name] || {};
+
+ return Object.assign({}, {
+ hashSize: 10,
+ service: authConfig.service,
+ entity: authConfig.entity,
+ errorMessage: 'Invalid login',
+ entityPasswordField: config.passwordField,
+ entityUsernameField: config.usernameField
+ }, config);
+ }
+
+ getEntityQuery (query) {
+ return Promise.resolve(Object.assign({
+ $limit: 1
+ }, query));
+ }
+
+ findEntity (username, params) {
+ const { entityUsernameField, service, errorMessage } = this.configuration;
+
+ return this.getEntityQuery({
+ [entityUsernameField]: username
+ }, params).then(query => {
+ const findParams = Object.assign({}, params, { query });
+ const entityService = this.app.service(service);
+
+ debug('Finding entity with query', params.query);
+
+ return entityService.find(findParams);
+ }).then(result => {
+ const list = Array.isArray(result) ? result : result.data;
+
+ if (!Array.isArray(list) || list.length === 0) {
+ debug(`No entity found`);
+
+ return Promise.reject(new NotAuthenticated(errorMessage));
+ }
+
+ const [ entity ] = list;
+
+ return entity;
+ });
+ }
+
+ comparePassword (entity, password) {
+ const { entityPasswordField, errorMessage } = this.configuration;
+ // find password in entity, this allows for dot notation
+ const hash = get(entity, entityPasswordField);
+
+ if (!hash) {
+ debug(`Record is missing the '${entityPasswordField}' password field`);
+
+ return Promise.reject(new NotAuthenticated(errorMessage));
+ }
+
+ debug('Verifying password');
+
+ return bcrypt.compare(password, hash).then(result => {
+ if (result) {
+ return entity;
+ }
+
+ throw new NotAuthenticated(errorMessage);
+ });
+ }
+
+ hashPassword (password) {
+ return bcrypt.hash(password, this.configuration.hashSize);
+ }
+
+ authenticate (data, params) {
+ const { passwordField, usernameField, entity, errorMessage } = this.configuration;
+ const username = data[usernameField];
+ const password = data[passwordField];
+
+ if (data.strategy && data.strategy !== this.name) {
+ return Promise.reject(new NotAuthenticated(errorMessage));
+ }
+
+ return this.findEntity(username, omit(params, 'provider'))
+ .then(entity => this.comparePassword(entity, password))
+ .then(entity => params.provider
+ ? this.findEntity(username, params) : entity
+ ).then(authEntity => {
+ return {
+ authentication: { strategy: this.name },
+ [entity]: authEntity
+ };
+ });
+ }
+};
diff --git a/packages/authentication-local/lib/utils/hash.js b/packages/authentication-local/lib/utils/hash.js
deleted file mode 100644
index 90b3b6391a..0000000000
--- a/packages/authentication-local/lib/utils/hash.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const bcrypt = require('bcryptjs');
-
-const BCRYPT_WORK_FACTOR_BASE = 12;
-const BCRYPT_DATE_BASE = 1483228800000;
-const BCRYPT_WORK_INCREASE_INTERVAL = 47300000000;
-
-module.exports = function hasher (password) {
- return new Promise((resolve, reject) => {
- let BCRYPT_CURRENT_DATE = new Date().getTime();
- let BCRYPT_WORK_INCREASE = Math.max(0, Math.floor((BCRYPT_CURRENT_DATE - BCRYPT_DATE_BASE) / BCRYPT_WORK_INCREASE_INTERVAL));
- let BCRYPT_WORK_FACTOR = Math.min(19, BCRYPT_WORK_FACTOR_BASE + BCRYPT_WORK_INCREASE);
-
- bcrypt.genSalt(BCRYPT_WORK_FACTOR, function (error, salt) {
- if (error) {
- return reject(error);
- }
-
- bcrypt.hash(password, salt, function (error, hashedPassword) {
- if (error) {
- return reject(error);
- }
-
- resolve(hashedPassword);
- });
- });
- });
-};
diff --git a/packages/authentication-local/lib/verifier.js b/packages/authentication-local/lib/verifier.js
deleted file mode 100644
index 188ee4baed..0000000000
--- a/packages/authentication-local/lib/verifier.js
+++ /dev/null
@@ -1,103 +0,0 @@
-const Debug = require('debug');
-const bcrypt = require('bcryptjs');
-const { get, omit } = require('lodash');
-
-const debug = Debug('@feathersjs/authentication-local:verify');
-
-class LocalVerifier {
- constructor (app, options = {}) {
- this.app = app;
- this.options = options;
- this.service = typeof options.service === 'string' ? app.service(options.service) : options.service;
-
- if (!this.service) {
- throw new Error(`options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-local.`);
- }
-
- this._comparePassword = this._comparePassword.bind(this);
- this._normalizeResult = this._normalizeResult.bind(this);
- this.verify = this.verify.bind(this);
- }
-
- _comparePassword (entity, password) {
- // select entity password field - take entityPasswordField over passwordField
- const passwordField = this.options.entityPasswordField || this.options.passwordField;
-
- // find password in entity, this allows for dot notation
- const hash = get(entity, passwordField);
-
- if (!hash) {
- return Promise.reject(new Error(`'${this.options.entity}' record in the database is missing a '${passwordField}'`));
- }
-
- debug('Verifying password');
-
- return new Promise((resolve, reject) => {
- bcrypt.compare(password, hash, function (error, result) {
- // Handle 500 server error.
- if (error) {
- return reject(error);
- }
-
- if (!result) {
- debug('Password incorrect');
- return reject(false); // eslint-disable-line
- }
-
- debug('Password correct');
- return resolve(entity);
- });
- });
- }
-
- _normalizeResult (results) {
- // Paginated services return the array of results in the data attribute.
- let entities = results.data ? results.data : results;
- let entity = entities[0];
-
- // Handle bad username.
- if (!entity) {
- return Promise.reject(false); // eslint-disable-line
- }
-
- debug(`${this.options.entity} found`);
- return Promise.resolve(entity);
- }
-
- verify (req, username, password, done) {
- debug('Checking credentials', username, password);
-
- const id = this.service.id;
- const usernameField = this.options.entityUsernameField || this.options.usernameField;
- const params = Object.assign({
- 'query': {
- [usernameField]: username,
- '$limit': 1
- }
- }, omit(req.params, 'query', 'provider', 'headers', 'session', 'cookies'));
-
- if (id === null || id === undefined) {
- debug('failed: the service.id was not set');
- return done(new Error('the `id` property must be set on the entity service for authentication'));
- }
-
- // Look up the entity
- this.service.find(params)
- .then(response => {
- const results = response.data || response;
- if (!results.length) {
- debug(`a record with ${usernameField} of '${username}' did not exist`);
- }
- return this._normalizeResult(response);
- })
- .then(entity => this._comparePassword(entity, password))
- .then(entity => {
- const id = entity[this.service.id];
- const payload = { [`${this.options.entity}Id`]: id };
- done(null, entity, payload);
- })
- .catch(error => error ? done(error) : done(null, error, { message: 'Invalid login' }));
- }
-}
-
-module.exports = LocalVerifier;
diff --git a/packages/authentication-local/package.json b/packages/authentication-local/package.json
index 33ad1d936e..af4729a1db 100644
--- a/packages/authentication-local/package.json
+++ b/packages/authentication-local/package.json
@@ -38,20 +38,11 @@
"@feathersjs/errors": "^3.3.6",
"bcryptjs": "^2.4.3",
"debug": "^4.1.1",
- "lodash": "^4.17.11",
- "passport-local": "^1.0.0"
+ "lodash": "^4.17.11"
},
"devDependencies": {
"@feathersjs/authentication": "^2.1.16",
- "@feathersjs/authentication-jwt": "^2.0.10",
- "@feathersjs/express": "^1.3.1",
"@feathersjs/feathers": "^3.3.1",
- "@feathersjs/socketio": "^3.2.9",
- "body-parser": "^1.18.3",
- "chai": "^4.2.0",
- "feathers-memory": "^3.0.2",
- "mocha": "^5.2.0",
- "sinon": "^7.2.3",
- "sinon-chai": "^3.3.0"
+ "mocha": "^5.2.0"
}
}
diff --git a/packages/authentication-local/test/fixture.js b/packages/authentication-local/test/fixture.js
new file mode 100644
index 0000000000..8c8964e44e
--- /dev/null
+++ b/packages/authentication-local/test/fixture.js
@@ -0,0 +1,38 @@
+const feathers = require('@feathersjs/feathers');
+const memory = require('feathers-memory');
+const { AuthenticationService, JWTStrategy } = require('@feathersjs/authentication');
+
+const { LocalStrategy, hashPassword, protect } = require('../lib');
+
+module.exports = (app = feathers()) => {
+ const authentication = new AuthenticationService(app);
+
+ app.set('authentication', {
+ secret: 'supersecret',
+ strategies: [ 'local', 'jwt' ],
+ local: {
+ usernameField: 'email',
+ passwordField: 'password'
+ }
+ });
+
+ authentication.register('jwt', new JWTStrategy());
+ authentication.register('local', new LocalStrategy());
+
+ app.use('/authentication', authentication);
+ app.use('/users', memory({
+ paginate: {
+ default: 10,
+ max: 20
+ }
+ }));
+
+ app.service('users').hooks({
+ before: {
+ create: hashPassword('password')
+ },
+ after: protect('password')
+ });
+
+ return app;
+};
diff --git a/packages/authentication-local/test/hooks/hash-password.test.js b/packages/authentication-local/test/hooks/hash-password.test.js
index d354a4e6ee..db57af297f 100644
--- a/packages/authentication-local/test/hooks/hash-password.test.js
+++ b/packages/authentication-local/test/hooks/hash-password.test.js
@@ -1,178 +1,85 @@
-/* eslint-disable no-unused-expressions */
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
+const assert = require('assert');
-const { hashPassword } = require('../../lib/hooks');
-const { expect } = chai;
+const getApp = require('../fixture');
+const { hashPassword } = require('../../lib');
-chai.use(sinonChai);
-
-describe('hooks:hashPassword', () => {
- let hook;
+describe('@feathersjs/authentication-local/hooks/hash-password', () => {
+ let app;
beforeEach(() => {
- hook = {
- type: 'before',
- data: { password: 'secret' },
- params: {},
- app: {
- get: () => {
- return {
- local: {
- passwordField: 'password'
- }
- };
- }
- }
- };
+ app = getApp();
});
- describe('when not called as a before hook', () => {
- it('returns an error', () => {
- hook.type = 'after';
- return hashPassword()(hook).catch(error => {
- expect(error).to.not.equal(undefined);
- });
- });
+ it('throws error when no field provided', () => {
+ try {
+ hashPassword();
+ assert.fail('Should never get here');
+ } catch (error) {
+ assert.strictEqual(error.message, 'The hashPassword hook requires a field name option');
+ }
});
- describe('when data does not exist', () => {
- it('does not do anything', () => {
- delete hook.data;
- return hashPassword()(hook).then(returnedHook => {
- expect(returnedHook).to.deep.equal(hook);
- });
- });
- });
-
- describe('when data does not have a prototype', () => {
- it('does not do anything', () => {
- hook.data = Object.create(null);
- return hashPassword()(hook).then(returnedHook => {
- expect(returnedHook).to.deep.equal(hook);
- });
- });
- });
+ it('errors when authentication service does not exist', () => {
+ delete app.services.authentication;
- describe('when password does not exist', () => {
- it('does not do anything', () => {
- delete hook.data.password;
- return hashPassword()(hook).then(returnedHook => {
- expect(returnedHook).to.deep.equal(hook);
- });
+ return app.service('users').create({
+ email: 'dave@hashpassword.com',
+ password: 'supersecret'
+ }).then(() => assert.fail('Should never get here')).catch(error => {
+ assert.strictEqual(error.message,
+ `Could not find 'authentication' service to hash password`
+ );
});
});
- describe('when password exists', () => {
- it('hashes with options from global auth config', () => {
- return hashPassword()(hook).then(hook => {
- expect(hook.data.password).to.not.equal(undefined);
- expect(hook.data.password).to.not.equal('secret');
- });
- });
-
- it('does not modify the original object', () => {
- const data = { password: 'secret' };
-
- hook.data = data;
-
- return hashPassword()(hook).then(hook => {
- expect(hook.data.password).to.not.equal(undefined);
- expect(hook.data.password).to.not.equal('secret');
- expect(data.password).to.equal('secret');
- });
- });
-
- it('hashes with custom options', () => {
- hook.data.pass = 'secret';
+ it('errors when authentication strategy does not exist', () => {
+ delete app.services.authentication.strategies.local;
- return hashPassword({ passwordField: 'pass' })(hook).then(hook => {
- expect(hook.data.pass).to.not.equal(undefined);
- expect(hook.data.pass).to.not.equal('secret');
- });
- });
-
- it('hashes with nested password field custom option', () => {
- hook.data = {
- nested: {
- pass: 'secret'
- }
- };
-
- return hashPassword({ passwordField: 'nested.pass' })(hook).then(hook => {
- expect(hook.data.nested.pass).to.not.equal(undefined);
- expect(hook.data.nested.pass).to.not.equal('secret');
- });
- });
-
- it('calls custom hash function', () => {
- const fn = sinon.stub().returns(Promise.resolve());
- return hashPassword({ hash: fn })(hook).then(() => {
- expect(fn).to.have.been.calledOnce;
- expect(fn).to.have.been.calledWith('secret');
- });
- });
-
- it('returns an error when custom hash is not a function', () => {
- return hashPassword({ hash: true })(hook).catch(error => {
- expect(error).to.not.equal(undefined);
- });
+ return app.service('users').create({
+ email: 'dave@hashpassword.com',
+ password: 'supersecret'
+ }).then(() => assert.fail('Should never get here')).catch(error => {
+ assert.strictEqual(error.message,
+ `Could not find 'local' strategy to hash password`
+ );
});
});
- describe('when password exists in bulk', () => {
- beforeEach(() => {
- hook.data = [
- { password: 'secret' },
- { password: 'secret' }
- ];
- });
+ it('errors when authentication strategy does not exist', () => {
+ const users = app.service('users');
- it('hashes with options from global auth config', () => {
- return hashPassword()(hook).then(hook => {
- hook.data.map(item => {
- expect(item.password).to.not.equal(undefined);
- expect(item.password).to.not.equal('secret');
- });
- });
+ users.hooks({
+ after: {
+ create: hashPassword('password')
+ }
});
- it('does not remove things if there is no password', () => {
- hook.data = [
- { id: 0, password: 'secret' },
- { id: 1 }
- ];
-
- return hashPassword()(hook).then(hook => {
- const { data } = hook;
-
- expect(data.length).to.equal(2);
- expect(data[0].password).to.not.equal('secret');
- expect(data[1]).to.exist;
- });
+ return users.create({
+ email: 'dave@hashpassword.com',
+ password: 'supersecret'
+ }).then(() => assert.fail('Should never get here')).catch(error => {
+ assert.strictEqual(error.message,
+ `The 'hashPassword' hook should only be used as a 'before' hook`
+ );
});
+ });
- it('hashes with custom options', () => {
- hook.data = [
- { pass: 'secret' },
- { pass: 'secret' }
- ];
+ it('hashes password on field', () => {
+ const password = 'supersecret';
- return hashPassword({ passwordField: 'pass' })(hook).then(hook => {
- hook.data.map(item => {
- expect(item.pass).to.not.equal(undefined);
- expect(item.pass).to.not.equal('secret');
- });
- });
+ return app.service('users').create({
+ email: 'dave@hashpassword.com',
+ password
+ }).then(user => {
+ assert.notStrictEqual(user.password, password);
});
+ });
- it('calls custom hash function', () => {
- const fn = sinon.stub().returns(Promise.resolve());
- return hashPassword({ hash: fn })(hook).then(() => {
- expect(fn).to.have.been.calledTwice;
- expect(fn).to.have.been.calledWith('secret');
- });
+ it('does nothing when field is not present', () => {
+ return app.service('users').create({
+ email: 'dave@hashpassword.com'
+ }).then(user => {
+ assert.strictEqual(user.password, undefined);
});
});
});
diff --git a/packages/authentication-local/test/hooks/index.test.js b/packages/authentication-local/test/hooks/index.test.js
deleted file mode 100644
index 8893f0b4c7..0000000000
--- a/packages/authentication-local/test/hooks/index.test.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const { expect } = require('chai');
-const hooks = require('../../lib/hooks');
-
-describe('hooks', () => {
- it('is CommonJS compatible', () => {
- expect(typeof require('../../lib/hooks')).to.equal('object');
- });
-
- it('is ES6 compatible', () => {
- expect(typeof hooks).to.equal('object');
- });
-
- it('exposes hashPassword hook', () => {
- expect(typeof hooks.hashPassword).to.equal('function');
- });
-});
diff --git a/packages/authentication-local/test/hooks/protect.test.js b/packages/authentication-local/test/hooks/protect.test.js
index e51d963d0f..8defea60df 100644
--- a/packages/authentication-local/test/hooks/protect.test.js
+++ b/packages/authentication-local/test/hooks/protect.test.js
@@ -1,8 +1,5 @@
-/* eslint-disable no-unused-expressions */
-const chai = require('chai');
-
-const { protect } = require('../../lib/hooks');
-const { expect } = chai;
+const assert = require('assert');
+const { protect } = require('../../lib');
function testOmit (title, property) {
describe(title, () => {
@@ -18,7 +15,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
dispatch: { email: 'test@user.com' }
});
@@ -37,7 +34,7 @@ function testOmit (title, property) {
};
const result = hook(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
dispatch: { user: { email: 'test@user.com' } }
});
@@ -54,7 +51,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
dispatch: { email: 'test@user.com', data: 'yes' }
});
@@ -76,7 +73,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
dispatch: { email: 'test@user.com' }
});
@@ -95,7 +92,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
dispatch: [
{ email: 'test1@user.com' },
@@ -121,7 +118,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
method: 'find',
[property]: data,
dispatch: {
@@ -149,7 +146,7 @@ function testOmit (title, property) {
};
const result = fn(context);
- expect(result).to.deep.equal({
+ assert.deepStrictEqual(result, {
[property]: data,
params,
result: [
@@ -165,12 +162,12 @@ function testOmit (title, property) {
});
}
-describe('hooks:protect', () => {
+describe('@feathersjs/authentication-local/hooks/protect', () => {
it('does nothing when called with no result', () => {
const fn = protect();
const original = {};
- expect(fn(original)).to.deep.equal(original);
+ assert.deepStrictEqual(fn(original), original);
});
testOmit('with hook.result', 'result');
diff --git a/packages/authentication-local/test/index.test.js b/packages/authentication-local/test/index.test.js
deleted file mode 100644
index 20d28fdbeb..0000000000
--- a/packages/authentication-local/test/index.test.js
+++ /dev/null
@@ -1,204 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-const memory = require('feathers-memory');
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-const passportLocal = require('passport-local');
-const local = require('../lib');
-
-const { Verifier } = local;
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('@feathersjs/authentication-local', () => {
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib')).to.equal('function');
- });
-
- it('basic functionality', () => {
- expect(typeof local).to.equal('function');
- });
-
- it('exposes default', () => {
- expect(local.default).to.equal(local);
- });
-
- it('exposes hooks', () => {
- expect(typeof local.hooks).to.equal('object');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- expect(typeof local.Verifier).to.equal('function');
- });
-
- describe('initialization', () => {
- let app;
-
- beforeEach(() => {
- app = expressify(feathers());
- app.use('/users', memory());
- app.configure(authentication({ secret: 'supersecret' }));
- });
-
- it('throws an error if passport has not been registered', () => {
- expect(() => {
- expressify(feathers()).configure(local());
- }).to.throw();
- });
-
- it('registers the local passport strategy', () => {
- sinon.spy(app.passport, 'use');
- sinon.spy(passportLocal, 'Strategy');
- app.configure(local());
- app.setup();
-
- expect(passportLocal.Strategy).to.have.been.calledOnce;
- expect(app.passport.use).to.have.been.calledWith('local');
-
- app.passport.use.restore();
- passportLocal.Strategy.restore();
- });
-
- it('registers the strategy options', () => {
- sinon.spy(app.passport, 'options');
- app.configure(local());
- app.setup();
-
- expect(app.passport.options).to.have.been.calledOnce;
-
- app.passport.options.restore();
- });
-
- describe('passport strategy options', () => {
- let authOptions;
- let args;
-
- beforeEach(() => {
- sinon.spy(passportLocal, 'Strategy');
- app.configure(local({ custom: true }));
- app.setup();
- authOptions = app.get('authentication');
- args = passportLocal.Strategy.getCall(0).args[0];
- });
-
- afterEach(() => {
- passportLocal.Strategy.restore();
- });
-
- it('sets usernameField', () => {
- expect(args.usernameField).to.equal('email');
- });
-
- it('sets passwordField', () => {
- expect(args.passwordField).to.equal('password');
- });
-
- it('sets entity', () => {
- expect(args.entity).to.equal(authOptions.entity);
- });
-
- it('sets service', () => {
- expect(args.service).to.equal(authOptions.service);
- });
-
- it('sets session', () => {
- expect(args.session).to.equal(authOptions.session);
- });
-
- it('sets passReqToCallback', () => {
- expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback);
- });
-
- it('supports setting custom options', () => {
- expect(args.custom).to.equal(true);
- });
- });
-
- it('supports overriding default options', () => {
- sinon.spy(passportLocal, 'Strategy');
- app.configure(local({ usernameField: 'username' }));
- app.setup();
-
- expect(passportLocal.Strategy.getCall(0).args[0].usernameField).to.equal('username');
-
- passportLocal.Strategy.restore();
- });
-
- it('pulls options from global config', () => {
- sinon.spy(passportLocal, 'Strategy');
- let authOptions = app.get('authentication');
- authOptions.local = { usernameField: 'username' };
- app.set('authentication', authOptions);
-
- app.configure(local());
- app.setup();
-
- expect(passportLocal.Strategy.getCall(0).args[0].usernameField).to.equal('username');
- expect(passportLocal.Strategy.getCall(0).args[0].passwordField).to.equal('password');
-
- passportLocal.Strategy.restore();
- });
-
- it('pulls options from global config with custom name', () => {
- sinon.spy(passportLocal, 'Strategy');
- let authOptions = app.get('authentication');
- authOptions.custom = { usernameField: 'username' };
- app.set('authentication', authOptions);
-
- app.configure(local({ name: 'custom' }));
- app.setup();
-
- expect(passportLocal.Strategy.getCall(0).args[0].usernameField).to.equal('username');
- expect(passportLocal.Strategy.getCall(0).args[0].passwordField).to.equal('password');
-
- passportLocal.Strategy.restore();
- });
-
- describe('custom Verifier', () => {
- it('throws an error if a verify function is missing', () => {
- expect(() => {
- class CustomVerifier {
- constructor (app) {
- this.app = app;
- }
- }
- app.configure(local({ Verifier: CustomVerifier }));
- app.setup();
- }).to.throw();
- });
-
- it('verifies through custom verify function', () => {
- const User = {
- email: 'admin@feathersjs.com',
- password: 'password'
- };
-
- const req = {
- query: {},
- body: Object.assign({}, User),
- headers: {},
- cookies: {}
- };
- class CustomVerifier extends Verifier {
- verify (req, username, password, done) {
- expect(username).to.equal(User.email);
- expect(password).to.equal(User.password);
- done(null, User);
- }
- }
-
- app.configure(local({ Verifier: CustomVerifier }));
- app.setup();
-
- return app.authenticate('local')(req).then(result => {
- expect(result.data.user).to.deep.equal(User);
- });
- });
- });
- });
-});
diff --git a/packages/authentication-local/test/integration.test.js b/packages/authentication-local/test/integration.test.js
deleted file mode 100644
index a5bf64ee53..0000000000
--- a/packages/authentication-local/test/integration.test.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-const memory = require('feathers-memory');
-const local = require('../lib');
-
-const { expect } = require('chai');
-
-describe('integration', () => {
- it('verifies', () => {
- const User = {
- email: 'admin@feathersjs.com',
- password: 'password'
- };
-
- const req = {
- query: {},
- body: Object.assign({}, User),
- headers: {},
- cookies: {},
- params: {
- query: {},
- provider: 'socketio',
- headers: {},
- session: {},
- cookies: {},
- data: 'Hello, world'
- }
- };
-
- const app = expressify(feathers());
- let paramsReceived = false;
- let dataReceived;
-
- app.configure(authentication({ secret: 'secret' }))
- .configure(local())
- .use('/users', memory());
-
- app.service('users').hooks({
- before: {
- find: (hook) => {
- paramsReceived = Object.keys(hook.params);
- dataReceived = hook.params.data;
- },
- create: local.hooks.hashPassword({ passwordField: 'password' })
- }
- });
-
- app.setup();
-
- return app.service('users').create(User).then(() => {
- return app.authenticate('local')(req).then(result => {
- expect(result.success).to.equal(true);
- expect(result.data.user.email).to.equal(User.email);
- expect(result.data.user.password).to.not.equal(undefined);
- expect(paramsReceived).to.have.members(['data', 'query']);
- expect(dataReceived).to.equal('Hello, world');
- });
- });
- });
-});
diff --git a/packages/authentication-local/test/strategy.test.js b/packages/authentication-local/test/strategy.test.js
new file mode 100644
index 0000000000..8e1cd7ad08
--- /dev/null
+++ b/packages/authentication-local/test/strategy.test.js
@@ -0,0 +1,133 @@
+const assert = require('assert');
+const { LocalStrategy } = require('../lib');
+const getApp = require('./fixture');
+
+describe('@feathersjs/authentication-local/strategy', () => {
+ const password = 'localsecret';
+ const email = 'localtester@feathersjs.com';
+
+ let app, user;
+
+ beforeEach(() => {
+ app = getApp();
+
+ return app.service('users')
+ .create({ email, password })
+ .then(createdUser => {
+ user = createdUser;
+ });
+ });
+
+ it('throw error when configuration is not set', () => {
+ const auth = app.service('authentication');
+
+ try {
+ auth.register('something', new LocalStrategy());
+ assert.fail('Should never get here');
+ } catch (error) {
+ assert.strictEqual(error.message,
+ `'something' authentication strategy requires a 'usernameField' setting`
+ );
+ }
+ });
+
+ it('fails when entity not found', () => {
+ const authService = app.service('authentication');
+ return authService.create({
+ strategy: 'local',
+ email: 'not in database',
+ password
+ }).then(() => {
+ assert.fail('Should never get here');
+ }).catch(error => {
+ assert.strictEqual(error.name, 'NotAuthenticated');
+ assert.strictEqual(error.message, 'Invalid login');
+ });
+ });
+
+ it('strategy fails when strategy is different', () => {
+ const [ local ] = app.service('authentication').getStrategies('local');
+ return local.authenticate({
+ strategy: 'not-me',
+ password: 'dummy',
+ email
+ }).then(() => {
+ assert.fail('Should never get here');
+ }).catch(error => {
+ assert.strictEqual(error.name, 'NotAuthenticated');
+ assert.strictEqual(error.message, 'Invalid login');
+ });
+ });
+
+ it('fails when password is wrong', () => {
+ const authService = app.service('authentication');
+ return authService.create({
+ strategy: 'local',
+ email,
+ password: 'dummy'
+ }).then(() => {
+ assert.fail('Should never get here');
+ }).catch(error => {
+ assert.strictEqual(error.name, 'NotAuthenticated');
+ assert.strictEqual(error.message, 'Invalid login');
+ });
+ });
+
+ it('fails when password field is not available', () => {
+ const userEmail = 'someuser@localtest.com';
+ const authService = app.service('authentication');
+
+ return app.service('users').create({
+ email: userEmail
+ }).then(() => authService.create({
+ strategy: 'local',
+ password: 'dummy',
+ email: userEmail
+ })).then(() => {
+ assert.fail('Should never get here');
+ }).catch(error => {
+ assert.strictEqual(error.name, 'NotAuthenticated');
+ assert.strictEqual(error.message, 'Invalid login');
+ });
+ });
+
+ it('authenticates an existing user', () => {
+ const authService = app.service('authentication');
+ return authService.create({
+ strategy: 'local',
+ email,
+ password
+ }).then(authResult => {
+ const { accessToken } = authResult;
+
+ assert.ok(accessToken);
+ assert.strictEqual(authResult.user.email, email);
+
+ return authService.verifyJWT(accessToken);
+ }).then(decoded => {
+ assert.strictEqual(decoded.sub, `${user.id}`);
+ });
+ });
+
+ it('returns safe result when params.provider is set, works without pagination', () => {
+ const authService = app.service('authentication');
+ return authService.create({
+ strategy: 'local',
+ email,
+ password
+ }, {
+ provider: 'rest',
+ paginate: false
+ }).then(authResult => {
+ const { accessToken } = authResult;
+
+ assert.ok(accessToken);
+ assert.strictEqual(authResult.user.email, email);
+ assert.strictEqual(authResult.user.passsword, undefined);
+
+ return authService.verifyJWT(accessToken);
+ }).then(decoded => {
+ assert.strictEqual(decoded.sub, `${user.id}`);
+ });
+ });
+});
diff --git a/packages/authentication-local/test/verifier.test.js b/packages/authentication-local/test/verifier.test.js
deleted file mode 100644
index 07004edf6c..0000000000
--- a/packages/authentication-local/test/verifier.test.js
+++ /dev/null
@@ -1,305 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const authentication = require('@feathersjs/authentication');
-const expressify = require('@feathersjs/express');
-const hasher = require('../lib/utils/hash');
-const { Verifier, defaults } = require('../lib');
-
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const { expect } = require('chai');
-
-chai.use(sinonChai);
-
-describe('Verifier', () => {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- app = expressify(feathers());
-
- return hasher('admin').then(password => {
- user = {
- email: 'admin@feathersjs.com',
- password
- };
-
- service = {
- id: 'id',
- find () {}
- };
-
- sinon.stub(service, 'find').callsFake(function (params) {
- return new Promise((resolve, reject) => {
- const { email } = params && params.query;
- if (email === 'nonexistinguser@gmail.com') {
- return resolve([]);
- }
- return resolve([user]);
- });
- });
-
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = Object.assign({}, defaults, app.get('authentication'));
-
- verifier = new Verifier(app, options);
- });
- });
-
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib/verifier')).to.equal('function');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- });
-
- describe('constructor', () => {
- it('retains an app reference', () => {
- expect(verifier.app).to.deep.equal(app);
- });
-
- it('sets options', () => {
- expect(verifier.options).to.deep.equal(options);
- });
-
- it('sets service using service path', () => {
- expect(verifier.service).to.deep.equal(app.service('users'));
- });
-
- it('sets a passed in service instance', () => {
- options.service = service;
- expect(new Verifier(app, options).service).to.deep.equal(service);
- });
-
- describe('when service is undefined', () => {
- it('throws an error', () => {
- expect(() => {
- new Verifier(app, {}); // eslint-disable-line
- }).to.throw();
- });
- });
- });
-
- describe('_comparePassword', () => {
- describe('when entity is missing password field', () => {
- it('returns an error', () => {
- return verifier._comparePassword({}).catch(error => {
- expect(error).to.not.equal(undefined);
- });
- });
- });
-
- describe('password comparison fails', () => {
- it('rejects with false', () => {
- return verifier._comparePassword(user, 'invalid').catch(error => {
- expect(error).to.equal(false);
- });
- });
- });
-
- describe('password comparison succeeds', () => {
- it('returns the entity', () => {
- return verifier._comparePassword(user, 'admin').then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('allows dot notation for password field', () => {
- user.password = {
- value: user.password
- };
-
- verifier.options.passwordField = 'password.value';
-
- return verifier._comparePassword(user, 'admin').then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('prefers entityPasswordField over passwordField', () => {
- user.password = {
- value: user.password
- };
-
- verifier.options.passwordField = 'password';
- verifier.options.entityPasswordField = 'password.value';
-
- return verifier._comparePassword(user, 'admin').then(result => {
- expect(result).to.deep.equal(user);
- });
- });
- });
- });
-
- describe('_normalizeResult', () => {
- describe('when has results', () => {
- it('returns entity when paginated', () => {
- return verifier._normalizeResult({ data: [user] }).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('returns entity when not paginated', () => {
- return verifier._normalizeResult([user]).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
- });
-
- describe('when no results', () => {
- it('rejects with false when paginated', () => {
- return verifier._normalizeResult({ data: [] }).catch(error => {
- expect(error).to.equal(false);
- });
- });
-
- it('rejects with false when not paginated', () => {
- return verifier._normalizeResult([]).catch(error => {
- expect(error).to.equal(false);
- });
- });
- });
- });
-
- describe('verify', () => {
- it('calls find on the provided service', done => {
- verifier.verify({}, user.email, 'admin', () => {
- const query = { email: user.email, $limit: 1 };
- expect(service.find).to.have.been.calledOnce;
- expect(service.find).to.have.been.calledWith({ query });
- done();
- });
- });
-
- it('allows overriding of usernameField', done => {
- verifier.options.usernameField = 'username';
-
- user.username = 'username';
-
- verifier.verify({}, 'username', 'admin', (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- it('prefers entityUsernameField over usernameField', done => {
- verifier.options.usernameField = 'username';
- verifier.options.entityUsernameField = 'users.username';
-
- user.username = 'invalid';
-
- user.users = {
- username: 'valid'
- };
-
- verifier.verify({}, 'valid', 'admin', (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- it('calls _normalizeResult', done => {
- sinon.spy(verifier, '_normalizeResult');
- verifier.verify({}, user.email, 'admin', () => {
- expect(verifier._normalizeResult).to.have.been.calledOnce;
- verifier._normalizeResult.restore();
- done();
- });
- });
-
- it('produces an error message when the user did not exist', done => {
- verifier.verify({}, 'nonexistinguser@gmail.com', 'admin', (err, user, info) => {
- expect(err).to.not.be.undefined;
- expect(info.message).to.equal('Invalid login');
- done();
- });
- });
-
- it('calls _comparePassword', done => {
- sinon.spy(verifier, '_comparePassword');
- verifier.verify({}, user.email, 'admin', () => {
- expect(verifier._comparePassword).to.have.been.calledOnce;
- verifier._comparePassword.restore();
- done();
- });
- });
-
- it('returns the entity', done => {
- verifier.verify({}, user.email, 'admin', (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- it('handles false rejections in promise chain', (done) => {
- verifier._normalizeResult = () => Promise.reject(false); // eslint-disable-line
- verifier.verify({}, user.email, 'admin', (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.equal(false);
- done();
- });
- });
-
- it('returns errors', (done) => {
- const authError = new Error('An error');
- verifier._normalizeResult = () => Promise.reject(authError);
- verifier.verify({}, user.email, 'admin', (error, entity) => {
- expect(error).to.equal(authError);
- expect(entity).to.equal(undefined);
- done();
- });
- });
- });
-});
-
-describe('Verifier without service.id', function () {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- app = expressify(feathers());
-
- return hasher('admin').then(password => {
- user = {
- email: 'admin@feathersjs.com',
- password
- };
-
- // testing a missing service.id
- service = {
- find () {
- return Promise.resolve([]);
- }
- };
-
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = Object.assign({}, defaults, app.get('authentication'));
-
- verifier = new Verifier(app, options);
- });
- });
-
- it('throws an error when service.id is not set', done => {
- verifier.verify({}, user.email, 'admin', (error, entity) => {
- expect(error.message.includes('the `id` property must be set')).to.equal(true);
- expect(entity).to.equal(undefined);
- done();
- });
- });
-});
diff --git a/packages/authentication-oauth1/.npmignore b/packages/authentication-oauth1/.npmignore
deleted file mode 100644
index 65e3ba2eda..0000000000
--- a/packages/authentication-oauth1/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-test/
diff --git a/packages/authentication-oauth1/CHANGELOG.md b/packages/authentication-oauth1/CHANGELOG.md
deleted file mode 100644
index e95ad655b6..0000000000
--- a/packages/authentication-oauth1/CHANGELOG.md
+++ /dev/null
@@ -1,208 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-## [1.1.1](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.1.0...@feathersjs/authentication-oauth1@1.1.1) (2019-01-26)
-
-
-### Bug Fixes
-
-* **authentication:** Fall back when req.app is not the application when emitting events ([#1185](https://github.com/feathersjs/feathers/issues/1185)) ([6a534f0](https://github.com/feathersjs/feathers/commit/6a534f0))
-
-
-
-
-
-# [1.1.0](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.10...@feathersjs/authentication-oauth1@1.1.0) (2019-01-06)
-
-
-### Features
-
-* Make custom query for oAuth authentication ([#1124](https://github.com/feathersjs/feathers/issues/1124)) ([5d43e3c](https://github.com/feathersjs/feathers/commit/5d43e3c))
-
-
-
-
-
-## [1.0.10](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.9...@feathersjs/authentication-oauth1@1.0.10) (2019-01-02)
-
-
-### Bug Fixes
-
-* Update adapter common tests ([#1135](https://github.com/feathersjs/feathers/issues/1135)) ([8166dda](https://github.com/feathersjs/feathers/commit/8166dda))
-
-
-
-
-
-
-## [1.0.9](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.8...@feathersjs/authentication-oauth1@1.0.9) (2018-12-16)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth1
-
-
-
-
-
-
-## [1.0.8](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.7...@feathersjs/authentication-oauth1@1.0.8) (2018-10-25)
-
-
-### Bug Fixes
-
-* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803))
-
-
-
-
-
-
-## [1.0.7](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.6...@feathersjs/authentication-oauth1@1.0.7) (2018-09-21)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth1
-
-
-
-
-
-
-## [1.0.6](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.5...@feathersjs/authentication-oauth1@1.0.6) (2018-09-17)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth1
-
-
-
-
-
-
-## [1.0.5](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth1@1.0.4...@feathersjs/authentication-oauth1@1.0.5) (2018-09-02)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth1
-
-
-## 1.0.4
-
-- Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462))
-
-## [v1.0.3](https://github.com/feathersjs/authentication-oauth1/tree/v1.0.3) (2018-01-03)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v1.0.2...v1.0.3)
-
-**Merged pull requests:**
-
-- Update documentation to correspond with latest release [\#40](https://github.com/feathersjs/authentication-oauth1/pull/40) ([daffl](https://github.com/daffl))
-- Update semistandard to the latest version 🚀 [\#39](https://github.com/feathersjs/authentication-oauth1/pull/39) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-memory to the latest version 🚀 [\#38](https://github.com/feathersjs/authentication-oauth1/pull/38) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.2](https://github.com/feathersjs/authentication-oauth1/tree/v1.0.2) (2017-11-16)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v1.0.1...v1.0.2)
-
-**Closed issues:**
-
-- No error handler? [\#34](https://github.com/feathersjs/authentication-oauth1/issues/34)
-
-**Merged pull requests:**
-
-- Add default ES module default exports to make TypeScript integration … [\#37](https://github.com/feathersjs/authentication-oauth1/pull/37) ([daffl](https://github.com/daffl))
-- Update @feathersjs/authentication to the latest version 🚀 [\#36](https://github.com/feathersjs/authentication-oauth1/pull/36) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.1](https://github.com/feathersjs/authentication-oauth1/tree/v1.0.1) (2017-11-06)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v1.0.0...v1.0.1)
-
-**Merged pull requests:**
-
-- Add a missing errorr handler [\#35](https://github.com/feathersjs/authentication-oauth1/pull/35) ([Avnerus](https://github.com/Avnerus))
-
-## [v1.0.0](https://github.com/feathersjs/authentication-oauth1/tree/v1.0.0) (2017-11-01)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v1.0.0-pre.1...v1.0.0)
-
-**Merged pull requests:**
-
-- Update dependencies for release [\#33](https://github.com/feathersjs/authentication-oauth1/pull/33) ([daffl](https://github.com/daffl))
-
-## [v1.0.0-pre.1](https://github.com/feathersjs/authentication-oauth1/tree/v1.0.0-pre.1) (2017-10-25)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.6...v1.0.0-pre.1)
-
-**Merged pull requests:**
-
-- Update to Feathers v3 [\#32](https://github.com/feathersjs/authentication-oauth1/pull/32) ([daffl](https://github.com/daffl))
-- Rename repository and use npm scope [\#31](https://github.com/feathersjs/authentication-oauth1/pull/31) ([daffl](https://github.com/daffl))
-- Update to new plugin infrastructure [\#30](https://github.com/feathersjs/authentication-oauth1/pull/30) ([daffl](https://github.com/daffl))
-
-## [v0.2.6](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.6) (2017-10-15)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.5...v0.2.6)
-
-**Closed issues:**
-
-- An in-range update of feathers is breaking the build 🚨 [\#23](https://github.com/feathersjs/authentication-oauth1/issues/23)
-- Log a warning if this.service.id is undefined or null [\#16](https://github.com/feathersjs/authentication-oauth1/issues/16)
-
-**Merged pull requests:**
-
-- Erroring if service.id is undefined. Closes \#16 [\#29](https://github.com/feathersjs/authentication-oauth1/pull/29) ([ekryski](https://github.com/ekryski))
-- Fix old property name fallback for backwards compatibility [\#28](https://github.com/feathersjs/authentication-oauth1/pull/28) ([ekryski](https://github.com/ekryski))
-- changing to patch instead of update when updating an existing user [\#27](https://github.com/feathersjs/authentication-oauth1/pull/27) ([ekryski](https://github.com/ekryski))
-- Update mocha to the latest version 🚀 [\#26](https://github.com/feathersjs/authentication-oauth1/pull/26) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#25](https://github.com/feathersjs/authentication-oauth1/pull/25) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Add babel-polyfill and package-lock.json [\#24](https://github.com/feathersjs/authentication-oauth1/pull/24) ([daffl](https://github.com/daffl))
-- Update debug to the latest version 🚀 [\#22](https://github.com/feathersjs/authentication-oauth1/pull/22) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#21](https://github.com/feathersjs/authentication-oauth1/pull/21) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.2.5](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.5) (2017-06-21)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.4...v0.2.5)
-
-**Closed issues:**
-
-- Module is using the wrong default config key [\#17](https://github.com/feathersjs/authentication-oauth1/issues/17)
-- When authenticating with stored localStorage jwt, payload doesn't have userId. [\#15](https://github.com/feathersjs/authentication-oauth1/issues/15)
-- Support oauth1 endpoint within sub-app or reverse proxy [\#9](https://github.com/feathersjs/authentication-oauth1/issues/9)
-- failureRedirect is not followed if it fails during OAuth dance [\#8](https://github.com/feathersjs/authentication-oauth1/issues/8)
-
-**Merged pull requests:**
-
-- chore\(package\): update chai to version 4.0.2 [\#20](https://github.com/feathersjs/authentication-oauth1/pull/20) ([daffl](https://github.com/daffl))
-- using correct auth key. Closes 17 [\#18](https://github.com/feathersjs/authentication-oauth1/pull/18) ([ekryski](https://github.com/ekryski))
-- Update semistandard to the latest version 🚀 [\#14](https://github.com/feathersjs/authentication-oauth1/pull/14) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-hooks to the latest version 🚀 [\#13](https://github.com/feathersjs/authentication-oauth1/pull/13) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update dependencies to enable Greenkeeper 🌴 [\#12](https://github.com/feathersjs/authentication-oauth1/pull/12) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.2.4](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.4) (2017-03-31)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.3...v0.2.4)
-
-**Closed issues:**
-
-- userId is missing in jwt payload [\#5](https://github.com/feathersjs/authentication-oauth1/issues/5)
-- accessToken is not returned when successRedirect is set [\#4](https://github.com/feathersjs/authentication-oauth1/issues/4)
-
-**Merged pull requests:**
-
-- Issue \#9: Support oauth1 endpoint within sub-app or reverse proxy [\#11](https://github.com/feathersjs/authentication-oauth1/pull/11) ([buske](https://github.com/buske))
-- Issue \#8: Follow failureRedirect on oauth1 authentication failure [\#10](https://github.com/feathersjs/authentication-oauth1/pull/10) ([buske](https://github.com/buske))
-
-## [v0.2.3](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.3) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.2...v0.2.3)
-
-## [v0.2.2](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.2) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.1...v0.2.2)
-
-**Merged pull requests:**
-
-- setting \_\_oauth value and fixing redirects [\#3](https://github.com/feathersjs/authentication-oauth1/pull/3) ([ekryski](https://github.com/ekryski))
-
-## [v0.2.1](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.1) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.2.0...v0.2.1)
-
-## [v0.2.0](https://github.com/feathersjs/authentication-oauth1/tree/v0.2.0) (2016-11-23)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.1.1...v0.2.0)
-
-**Merged pull requests:**
-
-- adding compatibility with payload generation [\#2](https://github.com/feathersjs/authentication-oauth1/pull/2) ([ekryski](https://github.com/ekryski))
-
-## [v0.1.1](https://github.com/feathersjs/authentication-oauth1/tree/v0.1.1) (2016-11-20)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth1/compare/v0.1.0...v0.1.1)
-
-## [v0.1.0](https://github.com/feathersjs/authentication-oauth1/tree/v0.1.0) (2016-11-19)
-
-
-\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/packages/authentication-oauth1/LICENSE b/packages/authentication-oauth1/LICENSE
deleted file mode 100644
index 6bfc0adefc..0000000000
--- a/packages/authentication-oauth1/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2018 Feathers
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/packages/authentication-oauth1/README.md b/packages/authentication-oauth1/README.md
deleted file mode 100644
index bdc03d54e9..0000000000
--- a/packages/authentication-oauth1/README.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# @feathersjs/authentication-oauth1
-
-[![Build Status](https://travis-ci.org/feathersjs/feathers.png?branch=master)](https://travis-ci.org/feathersjs/feathers)
-[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/authentication-oauth1)](https://david-dm.org/feathersjs/feathers?path=packages/authentication-oauth1)
-[![Download Status](https://img.shields.io/npm/dm/@feathersjs/authentication-oauth1.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/authentication-oauth1)
-
-> A Feathers OAuth1 authentication strategy
-
-## Installation
-
-```
-npm install @feathersjs/authentication-oauth1 --save
-```
-
-## Quick example
-
-```js
-const feathers = require('@feathersjs/feathers');
-const authentication = require('feathers-authentication');
-const jwt = require('feathers-authentication-jwt');
-const oauth1 = require('@feathersjs/authentication-oauth1');
-const session = require('express-session');
-const TwitterStrategy = require('passport-twitter').Strategy;
-const app = feathers();
-
-// Setup in memory session
-app.use(session({
- secret: 'super secret',
- resave: true,
- saveUninitialized: true
-}));
-
-// Setup authentication
-app.configure(authentication(settings));
-app.configure(jwt());
-app.configure(oauth1({
- name: 'twitter',
- Strategy: TwitterStrategy,
- consumerKey: '',
- consumerSecret: ''
-}));
-
-// Setup a hook to only allow valid JWTs to authenticate
-// and get new JWT access tokens
-app.service('authentication').hooks({
- before: {
- create: [
- authentication.hooks.authenticate(['jwt'])
- ]
- }
-});
-```
-
-## Documentation
-
-Please refer to the [@feathersjs/authentication-oauth1 documentation](http://docs.feathersjs.com/) for more details.
-
-## License
-
-Copyright (c) 2018
-
-Licensed under the [MIT license](LICENSE).
diff --git a/packages/authentication-oauth1/lib/express/error-handler.js b/packages/authentication-oauth1/lib/express/error-handler.js
deleted file mode 100644
index cdb6211806..0000000000
--- a/packages/authentication-oauth1/lib/express/error-handler.js
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = function OAuthErrorHandler (options = {}) {
- return function (err, req, res, next) {
- // Set __redirect so that later middleware (e.g., auth.express.failureRedirect) can redirect accordingly
- if (options.failureRedirect) {
- res.hook = { data: {} };
- Object.defineProperty(res.hook.data, '__redirect', { value: { status: 302, url: options.failureRedirect } });
- }
-
- next(err);
- };
-};
diff --git a/packages/authentication-oauth1/lib/express/handler.js b/packages/authentication-oauth1/lib/express/handler.js
deleted file mode 100644
index 75cb83e39c..0000000000
--- a/packages/authentication-oauth1/lib/express/handler.js
+++ /dev/null
@@ -1,34 +0,0 @@
-const Debug = require('debug');
-
-const debug = Debug('@feathersjs/authentication-oauth1:handler');
-
-module.exports = function OAuthHandler (options = {}) {
- return function (req, res, next) {
- const app = req.app;
- const authSettings = app.get('auth') || app.get('authentication') || {};
- const entity = req[options.entity];
- const payload = req.payload;
- const params = {
- authenticated: true,
- [options.entity]: entity,
- payload
- };
- const data = {
- [options.entity]: entity,
- payload
- };
-
- debug(`Executing '${options.name}' OAuth Callback`);
- debug(`Calling create on '${authSettings.path}' service with`, entity);
- app.service(authSettings.path).create(data, params).then(result => {
- res.data = result;
-
- if (options.successRedirect) {
- res.hook = { data: {} };
- Object.defineProperty(res.hook.data, '__redirect', { value: { status: 302, url: options.successRedirect } });
- }
-
- next();
- }).catch(next);
- };
-};
diff --git a/packages/authentication-oauth1/lib/index.js b/packages/authentication-oauth1/lib/index.js
deleted file mode 100644
index 1651671ea3..0000000000
--- a/packages/authentication-oauth1/lib/index.js
+++ /dev/null
@@ -1,113 +0,0 @@
-const Debug = require('debug');
-const auth = require('@feathersjs/authentication');
-const rest = require('@feathersjs/express/rest');
-
-const { makeUrl, _ } = require('@feathersjs/commons');
-const { omit, pick } = _;
-
-const merge = require('lodash.merge');
-const defaultHandler = require('./express/handler');
-const defaultErrorHandler = require('./express/error-handler');
-const DefaultVerifier = require('./verifier');
-
-const debug = Debug('@feathersjs/authentication-oauth1');
-
-const INCLUDE_KEYS = [
- 'entity',
- 'service',
- 'passReqToCallback'
-];
-
-const EXCLUDE_KEYS = ['Verifier', 'Strategy', 'formatter'];
-
-function init (options = {}) {
- return function oauth1Auth () {
- const app = this;
- const _super = app.setup;
-
- if (!app.passport) {
- throw new Error(`Can not find app.passport. Did you initialize feathers-authentication before @feathersjs/authentication-oauth1?`);
- }
-
- let { name, Strategy } = options;
-
- if (!name) {
- throw new Error(`You must provide a strategy 'name'.`);
- }
-
- if (!Strategy) {
- throw new Error(`You must provide a passport 'Strategy' instance.`);
- }
-
- const authSettings = app.get('auth') || app.get('authentication') || {};
-
- // Attempt to pull options from the global auth config
- // for this provider.
- const providerSettings = authSettings[name] || {};
- const oauth1Settings = merge({
- idField: `${name}Id`,
- path: `/auth/${name}`,
- session: true,
- __oauth: true
- }, pick(authSettings, ...INCLUDE_KEYS), providerSettings, omit(options, ...EXCLUDE_KEYS));
-
- // Set callback defaults based on provided path
- oauth1Settings.callbackPath = oauth1Settings.callbackPath || `${oauth1Settings.path}/callback`;
- oauth1Settings.callbackURL = oauth1Settings.callbackURL || makeUrl(oauth1Settings.callbackPath, app);
-
- if (!oauth1Settings.consumerKey) {
- throw new Error(`You must provide a 'consumerKey' in your authentication configuration or pass one explicitly`);
- }
-
- if (!oauth1Settings.consumerSecret) {
- throw new Error(`You must provide a 'consumerSecret' in your authentication configuration or pass one explicitly`);
- }
-
- const Verifier = options.Verifier || DefaultVerifier;
- const formatter = options.formatter || rest.formatter;
- const handler = options.handler || defaultHandler(oauth1Settings);
- const errorHandler = defaultErrorHandler(oauth1Settings);
-
- // register OAuth middleware
- debug(`Registering '${name}' Express OAuth middleware`);
- app.get(oauth1Settings.path, auth.express.authenticate(name, oauth1Settings));
- app.get(
- oauth1Settings.callbackPath,
- // NOTE (EK): We register failure redirect here so that we can
- // retain the natural express middleware redirect ability like
- // you would have with vanilla passport.
- auth.express.authenticate(name, oauth1Settings),
- handler,
- errorHandler,
- auth.express.emitEvents(authSettings, app),
- auth.express.setCookie(authSettings),
- auth.express.successRedirect(),
- auth.express.failureRedirect(authSettings),
- formatter
- );
-
- app.setup = function () {
- let result = _super.apply(this, arguments);
- let verifier = new Verifier(app, oauth1Settings);
-
- if (!verifier.verify) {
- throw new Error(`Your verifier must implement a 'verify' function. It should have the same signature as a oauth1 passport verify callback.`);
- }
-
- // Register 'oauth1' strategy with passport
- debug('Registering oauth1 authentication strategy with options:', oauth1Settings);
- app.passport.use(name, new Strategy(oauth1Settings, verifier.verify.bind(verifier)));
- app.passport.options(name, oauth1Settings);
-
- return result;
- };
- };
-}
-
-module.exports = init;
-
-// Exposed Modules
-Object.assign(module.exports, {
- default: init,
- Verifier: DefaultVerifier
-});
diff --git a/packages/authentication-oauth1/lib/verifier.js b/packages/authentication-oauth1/lib/verifier.js
deleted file mode 100644
index df9c490b88..0000000000
--- a/packages/authentication-oauth1/lib/verifier.js
+++ /dev/null
@@ -1,120 +0,0 @@
-const Debug = require('debug');
-
-const debug = Debug('@feathersjs/authentication-oauth1:verify');
-
-class OAuth1Vierifier {
- constructor (app, options = {}) {
- this.app = app;
- this.options = options;
- this.service = typeof options.service === 'string' ? app.service(options.service) : options.service;
-
- options.makeQuery = options.makeQuery || function (profile, options) {
- return { [options.idField]: profile.id }; // facebookId: profile.id
- };
-
- if (!this.service) {
- throw new Error(`options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-oauth1.`);
- }
-
- this._createEntity = this._createEntity.bind(this);
- this._updateEntity = this._updateEntity.bind(this);
- this._normalizeResult = this._normalizeResult.bind(this);
- this.verify = this.verify.bind(this);
- }
-
- _normalizeResult (results) {
- // Paginated services return the array of results in the data attribute.
- let entities = results.data ? results.data : results;
- let entity = entities[0];
-
- // Handle entity not found.
- if (!entity) {
- return Promise.resolve(null);
- }
-
- // Handle updating mongoose models
- if (typeof entity.toObject === 'function') {
- entity = entity.toObject();
- } else if (typeof entity.toJSON === 'function') {
- // Handle updating Sequelize models
- entity = entity.toJSON();
- }
-
- debug(`${this.options.entity} found`);
- return Promise.resolve(entity);
- }
-
- _updateEntity (entity, data) {
- const options = this.options;
- const name = options.name;
- const id = entity[this.service.id];
- debug(`Updating ${options.entity}: ${id}`);
-
- const newData = {
- [options.idField]: data.profile.id,
- [name]: data
- };
-
- return this.service.patch(id, newData, { oauth: { provider: name } });
- }
-
- _createEntity (data) {
- const options = this.options;
- const name = options.name;
- const entity = {
- [options.idField]: data.profile.id,
- [name]: data
- };
-
- const id = entity[options.idField];
- debug(`Creating new ${options.entity} with ${options.idField}: ${id}`);
-
- return this.service.create(entity, { oauth: { provider: name } });
- }
-
- verify (req, accessToken, refreshToken, profile, done) {
- debug('Checking credentials');
- const options = this.options;
- const query = Object.assign({}, options.makeQuery(profile, options), { $limit: 1 });
- const data = { profile, accessToken, refreshToken };
- let existing;
-
- if (this.service.id === null || this.service.id === undefined) {
- debug('failed: the service.id was not set');
- return done(new Error('the `id` property must be set on the entity service for authentication'));
- }
-
- // Check request object for an existing entity
- if (req && req[options.entity]) {
- existing = req[options.entity];
- }
-
- // Check the request that came from a hook for an existing entity
- if (!existing && req && req.params && req.params[options.entity]) {
- existing = req.params[options.entity];
- }
-
- // If there is already an entity on the request object (ie. they are
- // already authenticated) attach the profile to the existing entity
- // because they are likely "linking" social accounts/profiles.
- if (existing) {
- return this._updateEntity(existing, data)
- .then(entity => done(null, entity))
- .catch(error => error ? done(error) : done(null, error));
- }
-
- // Find or create the user since they could have signed up via facebook.
- this.service
- .find({ query })
- .then(this._normalizeResult)
- .then(entity => entity ? this._updateEntity(entity, data) : this._createEntity(data))
- .then(entity => {
- const id = entity[this.service.id];
- const payload = { [`${this.options.entity}Id`]: id };
- done(null, entity, payload);
- })
- .catch(error => error ? done(error) : done(null, error));
- }
-}
-
-module.exports = OAuth1Vierifier;
diff --git a/packages/authentication-oauth1/package.json b/packages/authentication-oauth1/package.json
deleted file mode 100644
index c6935d4c80..0000000000
--- a/packages/authentication-oauth1/package.json
+++ /dev/null
@@ -1,57 +0,0 @@
-{
- "name": "@feathersjs/authentication-oauth1",
- "description": "A Feathers OAuth1 authentication strategy",
- "version": "1.1.1",
- "homepage": "https://feathersjs.com",
- "main": "lib/",
- "keywords": [
- "feathers",
- "feathers-plugin"
- ],
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git://github.com/feathersjs/feathers.git"
- },
- "author": {
- "name": "Feathers contributors",
- "email": "hello@feathersjs.com",
- "url": "https://feathersjs.com"
- },
- "contributors": [],
- "bugs": {
- "url": "https://github.com/feathersjs/feathers/issues"
- },
- "engines": {
- "node": ">= 6"
- },
- "scripts": {
- "test": "mocha --opts ../../mocha.opts"
- },
- "directories": {
- "lib": "lib"
- },
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@feathersjs/commons": "^4.0.0",
- "@feathersjs/errors": "^3.3.6",
- "debug": "^4.1.1",
- "lodash.merge": "^4.6.1"
- },
- "devDependencies": {
- "@feathersjs/authentication": "^2.1.16",
- "@feathersjs/express": "^1.3.1",
- "@feathersjs/feathers": "^3.3.1",
- "body-parser": "^1.18.3",
- "chai": "^4.2.0",
- "express-session": "^1.15.6",
- "feathers-memory": "^3.0.2",
- "mocha": "^5.2.0",
- "passport-strategy": "^1.0.0",
- "passport-twitter": "^1.0.4",
- "sinon": "^7.2.3",
- "sinon-chai": "^3.3.0"
- }
-}
diff --git a/packages/authentication-oauth1/test/express/error-handler.test.js b/packages/authentication-oauth1/test/express/error-handler.test.js
deleted file mode 100644
index c7a9fe35ac..0000000000
--- a/packages/authentication-oauth1/test/express/error-handler.test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const { expect } = require('chai');
-
-const errorHandler = require('../../lib/express/error-handler');
-
-describe('express:error-handler', () => {
- let req;
- let res;
- let error;
- let options;
-
- beforeEach(() => {
- req = {};
- options = {};
- res = {
- hook: {
- data: {
- __redirect: {
- url: '/app'
- }
- }
- }
- };
- error = new Error('Authentication Error');
- });
-
- describe('when failureRedirect is set', () => {
- it('sets the redirect object on the response', done => {
- options.failureRedirect = '/login';
- errorHandler(options)(error, req, res, () => {
- expect(res.hook.data.__redirect).to.deep.equal({ status: 302, url: options.failureRedirect });
- done();
- });
- });
-
- it('calls next with error', done => {
- delete res.hook;
- errorHandler(options)(error, req, res, e => {
- expect(e).to.equal(error);
- done();
- });
- });
- });
-
- describe('when failureRedirect is not set', done => {
- it('calls next with error', done => {
- delete res.hook;
- errorHandler(options)(error, req, res, e => {
- expect(e).to.equal(error);
- done();
- });
- });
- });
-});
diff --git a/packages/authentication-oauth1/test/express/handler.test.js b/packages/authentication-oauth1/test/express/handler.test.js
deleted file mode 100644
index 665a25b563..0000000000
--- a/packages/authentication-oauth1/test/express/handler.test.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-const handler = require('../../lib/express/handler');
-
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('express:handler', () => {
- let req;
- let res;
- let service;
- let options;
- let user;
- let payload;
- let accessToken = 'access';
-
- beforeEach(() => {
- payload = { userId: 1 };
- user = { name: 'Bob' };
- options = { entity: 'user', name: 'github' };
- service = {
- create: sinon.stub().returns(Promise.resolve({ accessToken }))
- };
-
- req = {
- user,
- payload,
- app: {
- get: sinon.stub().returns({ path: '/authentication' }),
- service: sinon.stub().returns(service)
- }
- };
- res = {};
- });
-
- afterEach(() => {
- req.app.service.reset();
- service.create.reset();
- });
-
- it('calls create on the authentication service', done => {
- const params = {
- authenticated: true,
- payload,
- user
- };
-
- handler(options)(req, res, () => {
- expect(req.app.service).to.have.been.calledOnce;
- expect(req.app.service).to.have.been.calledWith('/authentication');
- expect(service.create).to.have.been.calledOnce;
- expect(service.create).to.have.been.calledWith({ user, payload }, params);
- done();
- });
- });
-
- describe('when create succeeds', () => {
- it('sets res.data', done => {
- handler(options)(req, res, () => {
- expect(res.data).to.deep.equal({ accessToken });
- done();
- });
- });
-
- it('calls next', done => {
- handler(options)(req, res, done);
- });
-
- describe('when successRedirect is set', () => {
- it('sets the redirect object on the request', done => {
- options.successRedirect = '/app';
- handler(options)(req, res, () => {
- expect(res.hook.data.__redirect).to.deep.equal({ status: 302, url: options.successRedirect });
- done();
- });
- });
- });
- });
-
- describe('when create fails', () => {
- beforeEach(() => {
- service.create = sinon.stub().returns(Promise.reject(new Error('Auth Error')));
- req.app.service = sinon.stub().returns(service);
- });
-
- it('calls next with an error', done => {
- handler(options)(req, res, error => {
- expect(error).to.not.equal(undefined);
- done();
- });
- });
- });
-});
diff --git a/packages/authentication-oauth1/test/fixtures/strategy.js b/packages/authentication-oauth1/test/fixtures/strategy.js
deleted file mode 100644
index 58142848d4..0000000000
--- a/packages/authentication-oauth1/test/fixtures/strategy.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const passport = require('passport-strategy');
-const util = require('util');
-
-function Strategy (options, verify) {
- passport.Strategy.call(this);
- this.name = 'mock';
- this._options = options;
- this._verify = verify;
-}
-
-util.inherits(Strategy, passport.Strategy);
-
-Strategy.prototype.authenticate = function (req, options) {
- const accessToken = 'mockAccessToken';
- const refreshToken = 'mockRefreshToken';
- const profile = { name: 'Mocky Mockerson' };
-
- const callback = function (error, user, info) {
- if (error) {
- return this.error(error);
- }
-
- if (info && info.pass) {
- return this.pass();
- }
-
- if (info && info.url) {
- return this.redirect(info.url, info.status);
- }
-
- if (!user) {
- return this.fail(info.challenge, info.status);
- }
-
- return this.success(user, info);
- }.bind(this);
-
- this._verify(req, accessToken, refreshToken, profile, callback);
-};
-
-module.exports = Strategy;
diff --git a/packages/authentication-oauth1/test/index.test.js b/packages/authentication-oauth1/test/index.test.js
deleted file mode 100644
index 7ed3393775..0000000000
--- a/packages/authentication-oauth1/test/index.test.js
+++ /dev/null
@@ -1,290 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const authentication = require('@feathersjs/authentication');
-const expressify = require('@feathersjs/express');
-const memory = require('feathers-memory');
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const oauth1 = require('../lib');
-const Strategy = require('./fixtures/strategy');
-
-const { Verifier } = oauth1;
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('@feathersjs/authentication-oauth1', () => {
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib')).to.equal('function');
- });
-
- it('basic functionality', () => {
- expect(typeof oauth1).to.equal('function');
- });
-
- it('exports default', () => {
- expect(oauth1.default).to.equal(oauth1);
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- expect(typeof oauth1.Verifier).to.equal('function');
- });
-
- describe('initialization', () => {
- let app;
- let config;
- let globalConfig;
-
- beforeEach(() => {
- config = {
- name: 'twitter',
- Strategy,
- consumerKey: '1234',
- consumerSecret: 'secret'
- };
-
- globalConfig = {
- secret: 'supersecret',
- twitter: {
- consumerKey: '1234',
- consumerSecret: 'secret',
- scope: ['user']
- }
- };
-
- app = expressify(feathers());
- app.set('host', 'localhost');
- app.set('port', 3030);
- app.use('/users', memory());
- app.configure(authentication(globalConfig));
- });
-
- it('throws an error if passport has not been registered', () => {
- expect(() => {
- expressify(feathers()).configure(oauth1());
- }).to.throw();
- });
-
- it('throws an error if strategy name is missing', () => {
- expect(() => {
- delete config.name;
- app.configure(oauth1(config));
- }).to.throw();
- });
-
- it('throws an error if Strategy is missing', () => {
- expect(() => {
- delete config.Strategy;
- app.configure(oauth1(config));
- }).to.throw();
- });
-
- it('throws an error if consumerKey is missing', () => {
- expect(() => {
- delete config.consumerKey;
- delete globalConfig.twitter.consumerKey;
- expressify(feathers()).configure(authentication(globalConfig)).configure(oauth1(config));
- }).to.throw();
- });
-
- it('throws an error if consumerSecret is missing', () => {
- expect(() => {
- delete config.consumerSecret;
- delete globalConfig.twitter.consumerSecret;
- expressify(feathers()).configure(authentication(globalConfig)).configure(oauth1(config));
- }).to.throw();
- });
-
- it('registers the oauth1 passport strategy', () => {
- sinon.spy(app.passport, 'use');
- sinon.spy(config, 'Strategy');
- app.configure(oauth1(config));
- app.setup();
-
- expect(config.Strategy).to.have.been.calledOnce;
- expect(app.passport.use).to.have.been.calledWith(config.name);
-
- app.passport.use.restore();
- config.Strategy.restore();
- });
-
- it('registers the strategy options', () => {
- sinon.spy(app.passport, 'options');
- app.configure(oauth1(config));
- app.setup();
-
- expect(app.passport.options).to.have.been.calledOnce;
-
- app.passport.options.restore();
- });
-
- it('registers the redirect options on strategy options', () => {
- sinon.spy(authentication.express, 'authenticate');
- const mergedOptions = Object.assign({}, config, globalConfig);
- app.configure(oauth1(mergedOptions));
- app.setup();
-
- delete mergedOptions.Strategy;
- expect(authentication.express.authenticate).to.have.been.calledWith(config.name, sinon.match(mergedOptions));
-
- authentication.express.authenticate.restore();
- });
-
- describe('passport strategy options', () => {
- let authOptions;
- let args;
-
- beforeEach(() => {
- config.custom = true;
- sinon.spy(config, 'Strategy');
- app.configure(oauth1(config));
- app.setup();
- authOptions = app.get('authentication');
- args = config.Strategy.getCall(0).args[0];
- });
-
- afterEach(() => {
- config.Strategy.restore();
- });
-
- it('sets path', () => {
- expect(args.path).to.equal(`/auth/${config.name}`);
- });
-
- it('sets callbackPath', () => {
- expect(args.callbackPath).to.equal(`/auth/${config.name}/callback`);
- });
-
- it('sets callbackURL', () => {
- expect(args.callbackURL).to.equal(`http://localhost:3030/auth/${config.name}/callback`);
- });
-
- it('sets idField', () => {
- expect(args.idField).to.equal(`${config.name}Id`);
- });
-
- it('sets entity', () => {
- expect(args.entity).to.equal(authOptions.entity);
- });
-
- it('sets service', () => {
- expect(args.service).to.equal(authOptions.service);
- });
-
- it('sets session', () => {
- expect(args.session).to.equal(true);
- });
-
- it('sets passReqToCallback', () => {
- expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback);
- });
-
- it('supports setting custom options', () => {
- expect(args.custom).to.equal(true);
- });
- });
-
- it('mixes in global config for strategy', () => {
- delete config.consumerKey;
- delete config.consumerSecret;
- sinon.spy(config, 'Strategy');
-
- app.configure(oauth1(config));
- app.setup();
-
- expect(config.Strategy.getCall(0).args[0].scope).to.deep.equal(['user']);
-
- config.Strategy.restore();
- });
-
- it('supports overriding default options', () => {
- sinon.spy(config, 'Strategy');
- config.entity = 'organization';
- app.configure(oauth1(config));
- app.setup();
-
- expect(config.Strategy.getCall(0).args[0].entity).to.equal('organization');
-
- config.Strategy.restore();
- });
-
- it('registers express get route', () => {
- sinon.spy(app, 'get');
- app.configure(oauth1(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(`/auth/${config.name}`);
-
- app.get.restore();
- });
-
- it('registers express callback route', () => {
- sinon.spy(app, 'get');
- app.configure(oauth1(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(`/auth/${config.name}/callback`);
-
- app.get.restore();
- });
-
- it('registers custom express callback route', () => {
- sinon.spy(app, 'get');
- config.callbackPath = `/v1/api/auth/${config.name}/callback`;
- app.configure(oauth1(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(config.callbackPath);
-
- app.get.restore();
- });
-
- describe('custom Verifier', () => {
- it('throws an error if a verify function is missing', () => {
- expect(() => {
- class CustomVerifier {
- constructor (app) {
- this.app = app;
- }
- }
- config.Verifier = CustomVerifier;
- app.configure(oauth1(config));
- app.setup();
- }).to.throw();
- });
-
- it('verifies through custom verify function', () => {
- const User = {
- email: 'admin@feathersjs.com',
- password: 'password'
- };
-
- const req = {
- query: {},
- body: Object.assign({}, User),
- headers: {},
- cookies: {}
- };
- class CustomVerifier extends Verifier {
- verify (req, accessToken, refreshToken, profile, done) {
- expect(accessToken).to.equal('mockAccessToken');
- expect(refreshToken).to.equal('mockRefreshToken');
- expect(profile).to.deep.equal({ name: 'Mocky Mockerson' });
- done(null, User);
- }
- }
-
- config.Verifier = CustomVerifier;
- app.configure(oauth1(config));
- app.setup();
-
- return app.authenticate('twitter')(req).then(result => {
- expect(result.data.user).to.deep.equal(User);
- });
- });
- });
- });
-});
diff --git a/packages/authentication-oauth1/test/verifier.test.js b/packages/authentication-oauth1/test/verifier.test.js
deleted file mode 100644
index e096835cb0..0000000000
--- a/packages/authentication-oauth1/test/verifier.test.js
+++ /dev/null
@@ -1,331 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-
-const { Verifier } = require('../lib');
-
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('Verifier', () => {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- user = { id: 'test', email: 'admin@feathersjs.com' };
- service = {
- id: 'id',
- find: sinon.stub().returns(Promise.resolve([user])),
- create: sinon.stub().returns(Promise.resolve(user)),
- patch: sinon.stub().returns(Promise.resolve(user))
- };
-
- app = expressify(feathers());
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = app.get('authentication');
- options.name = 'twitter';
- options.idField = 'twitterId';
-
- verifier = new Verifier(app, options);
- });
-
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib/verifier')).to.equal('function');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- });
-
- describe('constructor', () => {
- it('retains an app reference', () => {
- expect(verifier.app).to.deep.equal(app);
- });
-
- it('sets options', () => {
- expect(verifier.options).to.deep.equal(options);
- });
-
- it('sets service using service path', () => {
- expect(verifier.service).to.deep.equal(app.service('users'));
- });
-
- it('sets a passed in service instance', () => {
- options.service = service;
- expect(new Verifier(app, options).service).to.deep.equal(service);
- });
-
- describe('when service is undefined', () => {
- it('throws an error', () => {
- expect(() => {
- new Verifier(app, {}); // eslint-disable-line
- }).to.throw();
- });
- });
- });
-
- describe('_updateEntity', () => {
- let entity;
- let data;
- let args;
-
- beforeEach(() => {
- entity = { id: 1, name: 'Admin' };
- data = {
- accessToken: 'access',
- refreshToken: 'refresh',
- profile: {
- id: 1234,
- name: 'Admin'
- }
- };
- return verifier._updateEntity(entity, data).then(() => {
- args = service.patch.getCall(0).args;
- });
- });
-
- it('calls patch on passed in service', () => {
- expect(service.patch).to.have.been.calledOnce;
- });
-
- it('passes id', () => {
- expect(args[0]).to.equal(entity.id);
- });
-
- it('passes patch data', () => {
- expect(args[1].twitterId).to.equal(data.profile.id);
- expect(args[1].twitter).to.deep.equal(data);
- });
-
- it('passes oauth provider via params', () => {
- expect(args[2]).to.deep.equal({ oauth: { provider: 'twitter' } });
- });
- });
-
- describe('_createEntity', () => {
- let data;
- let args;
-
- beforeEach(() => {
- data = {
- accessToken: 'access',
- refreshToken: 'refresh',
- profile: {
- id: 1234,
- name: 'Admin'
- }
- };
- return verifier._createEntity(data).then(() => {
- args = service.create.getCall(0).args;
- });
- });
-
- it('calls create on passed in service', () => {
- expect(service.create).to.have.been.calledOnce;
- });
-
- it('passes entity', () => {
- expect(args[0].twitterId).to.equal(data.profile.id);
- expect(args[0].twitter).to.deep.equal(data);
- });
-
- it('passes oauth provider via params', () => {
- expect(args[1]).to.deep.equal({ oauth: { provider: 'twitter' } });
- });
- });
-
- describe('_normalizeResult', () => {
- describe('when has results', () => {
- it('returns entity when paginated', () => {
- return verifier._normalizeResult({ data: [user] }).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('returns entity when not paginated', () => {
- return verifier._normalizeResult([user]).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('calls toObject on entity when present', () => {
- user.toObject = sinon.spy();
- return verifier._normalizeResult({ data: [user] }).then(() => {
- expect(user.toObject).to.have.been.calledOnce;
- });
- });
-
- it('calls toJSON on entity when present', () => {
- user.toJSON = sinon.spy();
- return verifier._normalizeResult({ data: [user] }).then(() => {
- expect(user.toJSON).to.have.been.calledOnce;
- });
- });
- });
-
- describe('when no results', () => {
- it('rejects with false when paginated', () => {
- return verifier._normalizeResult({ data: [] }).catch(error => {
- expect(error).to.equal(false);
- });
- });
-
- it('rejects with false when not paginated', () => {
- return verifier._normalizeResult([]).catch(error => {
- expect(error).to.equal(false);
- });
- });
- });
- });
-
- describe('verify', () => {
- it('calls find on the provided service', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- const query = { twitterId: 1234, $limit: 1 };
- expect(service.find).to.have.been.calledOnce;
- expect(service.find).to.have.been.calledWith({ query });
- done();
- });
- });
-
- it('calls with query from makeQuery', done => {
- options = Object.assign({}, options, { makeQuery: sinon.stub().returns({ key: 'value' }) });
- verifier = new Verifier(app, options);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- const query = { key: 'value', $limit: 1 };
- expect(options.makeQuery).to.have.been.calledOnce;
- expect(service.find).to.have.been.calledWith({ query });
- done();
- });
- });
-
- it('calls _normalizeResult', done => {
- sinon.spy(verifier, '_normalizeResult');
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._normalizeResult).to.have.been.calledOnce;
- verifier._normalizeResult.restore();
- done();
- });
- });
-
- describe('when entity exists on request object', () => {
- it('calls _updateEntity', done => {
- sinon.spy(verifier, '_updateEntity');
- const req = { 'user': { name: 'Admin' } };
- verifier.verify(req, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
- });
-
- describe('when entity exists on request.params object', () => {
- it('calls _updateEntity', done => {
- sinon.spy(verifier, '_updateEntity');
- const req = {
- params: {
- 'user': { name: 'Admin' }
- }
- };
- verifier.verify(req, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
- });
-
- it('calls _createEntity when entity not found', done => {
- sinon.spy(verifier, '_createEntity');
- verifier._normalizeResult = () => Promise.resolve(null);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._createEntity).to.have.been.calledOnce;
- verifier._createEntity.restore();
- done();
- });
- });
-
- it('calls _updateEntity when entity is found', done => {
- sinon.spy(verifier, '_updateEntity');
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
-
- it('returns the entity', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- it('handles false rejections in promise chain', done => {
- verifier._updateEntity = () => Promise.reject(false); // eslint-disable-line
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.equal(false);
- done();
- });
- });
-
- it('returns errors', done => {
- const authError = new Error('An error');
- verifier._normalizeResult = () => Promise.reject(authError);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(authError);
- expect(entity).to.equal(undefined);
- done();
- });
- });
- });
-});
-
-describe('Verifier without service.id', function () {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- user = { id: 1, email: 'admin@feathersjs.com' };
- service = {
- find: sinon.stub().returns(Promise.resolve([user])),
- create: sinon.stub().returns(Promise.resolve(user)),
- patch: sinon.stub().returns(Promise.resolve(user))
- };
-
- app = expressify(feathers());
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = app.get('authentication');
- options.name = 'twitter';
- options.idField = 'twitterId';
-
- verifier = new Verifier(app, options);
- });
-
- it('throws an error when service.id is not set', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error.message.includes('the `id` property must be set')).to.equal(true);
- expect(entity).to.equal(undefined);
- done();
- });
- });
-});
diff --git a/packages/authentication-oauth2/.npmignore b/packages/authentication-oauth2/.npmignore
deleted file mode 100644
index 65e3ba2eda..0000000000
--- a/packages/authentication-oauth2/.npmignore
+++ /dev/null
@@ -1 +0,0 @@
-test/
diff --git a/packages/authentication-oauth2/CHANGELOG.md b/packages/authentication-oauth2/CHANGELOG.md
deleted file mode 100644
index 5d86872f7e..0000000000
--- a/packages/authentication-oauth2/CHANGELOG.md
+++ /dev/null
@@ -1,310 +0,0 @@
-# Change Log
-
-All notable changes to this project will be documented in this file.
-See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
-
-## [1.3.1](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.3.0...@feathersjs/authentication-oauth2@1.3.1) (2019-01-26)
-
-
-### Bug Fixes
-
-* **authentication:** Fall back when req.app is not the application when emitting events ([#1185](https://github.com/feathersjs/feathers/issues/1185)) ([6a534f0](https://github.com/feathersjs/feathers/commit/6a534f0))
-
-
-
-
-
-# [1.3.0](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.7...@feathersjs/authentication-oauth2@1.3.0) (2019-01-06)
-
-
-### Features
-
-* Make custom query for oAuth authentication ([#1124](https://github.com/feathersjs/feathers/issues/1124)) ([5d43e3c](https://github.com/feathersjs/feathers/commit/5d43e3c))
-
-
-
-
-
-## [1.2.7](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.6...@feathersjs/authentication-oauth2@1.2.7) (2019-01-02)
-
-
-### Bug Fixes
-
-* Update adapter common tests ([#1135](https://github.com/feathersjs/feathers/issues/1135)) ([8166dda](https://github.com/feathersjs/feathers/commit/8166dda))
-
-
-
-
-
-
-## [1.2.6](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.5...@feathersjs/authentication-oauth2@1.2.6) (2018-12-16)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth2
-
-
-
-
-
-
-## [1.2.5](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.4...@feathersjs/authentication-oauth2@1.2.5) (2018-10-25)
-
-
-### Bug Fixes
-
-* Make Mocha a proper devDependency for every repository ([#1053](https://github.com/feathersjs/feathers/issues/1053)) ([9974803](https://github.com/feathersjs/feathers/commit/9974803))
-
-
-
-
-
-
-## [1.2.4](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.3...@feathersjs/authentication-oauth2@1.2.4) (2018-09-21)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth2
-
-
-
-
-
-
-## [1.2.3](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.2...@feathersjs/authentication-oauth2@1.2.3) (2018-09-17)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth2
-
-
-
-
-
-
-## [1.2.2](https://github.com/feathersjs/feathers/compare/@feathersjs/authentication-oauth2@1.2.1...@feathersjs/authentication-oauth2@1.2.2) (2018-09-02)
-
-**Note:** Version bump only for package @feathersjs/authentication-oauth2
-
-
-## 1.2.1
-
-- Migrate to Monorepo ([feathers#462](https://github.com/feathersjs/feathers/issues/462))
-
-## [v1.2.0](https://github.com/feathersjs/authentication-oauth2/tree/v1.2.0) (2018-08-28)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.1.0...v1.2.0)
-
-**Closed issues:**
-
-- Entity could be an array? [\#87](https://github.com/feathersjs/authentication-oauth2/issues/87)
-- Cannot use Passport-Google-Token through POST request on /authentication [\#83](https://github.com/feathersjs/authentication-oauth2/issues/83)
-- JWT sub is always 'anonymous' after using verifier [\#82](https://github.com/feathersjs/authentication-oauth2/issues/82)
-- How to authenticate token from Facebook on Feathers server [\#80](https://github.com/feathersjs/authentication-oauth2/issues/80)
-- How to use FBSDK Login with React Native and FeathersJS? [\#22](https://github.com/feathersjs/authentication-oauth2/issues/22)
-- Create custom formatter to support calling back a mobile deep link [\#8](https://github.com/feathersjs/authentication-oauth2/issues/8)
-- Create custom formatter for returning an html page that calls back to parent window [\#7](https://github.com/feathersjs/authentication-oauth2/issues/7)
-
-**Merged pull requests:**
-
-- Update all dependencies [\#89](https://github.com/feathersjs/authentication-oauth2/pull/89) ([daffl](https://github.com/daffl))
-- Allowing return type of \_createEntity to be an Array [\#88](https://github.com/feathersjs/authentication-oauth2/pull/88) ([testless](https://github.com/testless))
-- Fix for Update an existing entity in verifier \#18 [\#86](https://github.com/feathersjs/authentication-oauth2/pull/86) ([nsainaney](https://github.com/nsainaney))
-- Update @feathersjs/commons to the latest version 🚀 [\#84](https://github.com/feathersjs/authentication-oauth2/pull/84) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Fix state=true in authorizationURL integrating passport-oauth2 [\#81](https://github.com/feathersjs/authentication-oauth2/pull/81) ([happydenn](https://github.com/happydenn))
-
-## [v1.1.0](https://github.com/feathersjs/authentication-oauth2/tree/v1.1.0) (2018-06-23)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.0.3...v1.1.0)
-
-**Closed issues:**
-
-- Authentication always logging as first user [\#77](https://github.com/feathersjs/authentication-oauth2/issues/77)
-- Restricting certain email domains [\#75](https://github.com/feathersjs/authentication-oauth2/issues/75)
-- Error when save callback payload google login into users service [\#74](https://github.com/feathersjs/authentication-oauth2/issues/74)
-- Confusing in doc of 'OAuth2 Authentication' [\#73](https://github.com/feathersjs/authentication-oauth2/issues/73)
-- Deleted [\#71](https://github.com/feathersjs/authentication-oauth2/issues/71)
-- Callback URL problem in production when using oauth [\#53](https://github.com/feathersjs/authentication-oauth2/issues/53)
-- Unable to override Facebook display options [\#32](https://github.com/feathersjs/authentication-oauth2/issues/32)
-- Implement oauth2 with graphql [\#25](https://github.com/feathersjs/authentication-oauth2/issues/25)
-
-**Merged pull requests:**
-
-- Allow a custom error Handler [\#79](https://github.com/feathersjs/authentication-oauth2/pull/79) ([mrdj07](https://github.com/mrdj07))
-- Update sinon to the latest version 🚀 [\#76](https://github.com/feathersjs/authentication-oauth2/pull/76) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#72](https://github.com/feathersjs/authentication-oauth2/pull/72) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon-chai to the latest version 🚀 [\#69](https://github.com/feathersjs/authentication-oauth2/pull/69) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update mocha to the latest version 🚀 [\#66](https://github.com/feathersjs/authentication-oauth2/pull/66) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.3](https://github.com/feathersjs/authentication-oauth2/tree/v1.0.3) (2018-01-03)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.0.2...v1.0.3)
-
-**Closed issues:**
-
-- I got Internal error when I log in with google [\#63](https://github.com/feathersjs/authentication-oauth2/issues/63)
-- how to use proxy when call authnetication provider [\#62](https://github.com/feathersjs/authentication-oauth2/issues/62)
-
-**Merged pull requests:**
-
-- Update documentation to correspond with latest release [\#65](https://github.com/feathersjs/authentication-oauth2/pull/65) ([daffl](https://github.com/daffl))
-- Update semistandard to the latest version 🚀 [\#64](https://github.com/feathersjs/authentication-oauth2/pull/64) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-memory to the latest version 🚀 [\#61](https://github.com/feathersjs/authentication-oauth2/pull/61) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.2](https://github.com/feathersjs/authentication-oauth2/tree/v1.0.2) (2017-11-28)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.0.1...v1.0.2)
-
-**Closed issues:**
-
-- Dependency @feathersjs/express not declared [\#59](https://github.com/feathersjs/authentication-oauth2/issues/59)
-
-**Merged pull requests:**
-
-- Add @feathersjs/express dependency [\#60](https://github.com/feathersjs/authentication-oauth2/pull/60) ([daffl](https://github.com/daffl))
-
-## [v1.0.1](https://github.com/feathersjs/authentication-oauth2/tree/v1.0.1) (2017-11-16)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.0.0...v1.0.1)
-
-**Closed issues:**
-
-- `facebook.profileFields` not works properly [\#57](https://github.com/feathersjs/authentication-oauth2/issues/57)
-- I get Internal server error after I auth with Google authentication [\#55](https://github.com/feathersjs/authentication-oauth2/issues/55)
-- Cannot connect with OAuth2, always getting 404 Not Found Page. [\#54](https://github.com/feathersjs/authentication-oauth2/issues/54)
-
-**Merged pull requests:**
-
-- Add default export for better ES module \(TypeScript\) compatibility [\#58](https://github.com/feathersjs/authentication-oauth2/pull/58) ([daffl](https://github.com/daffl))
-- Update @feathersjs/authentication to the latest version 🚀 [\#56](https://github.com/feathersjs/authentication-oauth2/pull/56) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v1.0.0](https://github.com/feathersjs/authentication-oauth2/tree/v1.0.0) (2017-11-01)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v1.0.0-pre.1...v1.0.0)
-
-**Merged pull requests:**
-
-- Update dependencies for release [\#52](https://github.com/feathersjs/authentication-oauth2/pull/52) ([daffl](https://github.com/daffl))
-
-## [v1.0.0-pre.1](https://github.com/feathersjs/authentication-oauth2/tree/v1.0.0-pre.1) (2017-10-25)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.3.2...v1.0.0-pre.1)
-
-**Merged pull requests:**
-
-- Update to Feathers v3 [\#51](https://github.com/feathersjs/authentication-oauth2/pull/51) ([daffl](https://github.com/daffl))
-- Rename repository and use npm scope [\#50](https://github.com/feathersjs/authentication-oauth2/pull/50) ([daffl](https://github.com/daffl))
-- Update to new plugin infrastructure [\#49](https://github.com/feathersjs/authentication-oauth2/pull/49) ([daffl](https://github.com/daffl))
-
-## [v0.3.2](https://github.com/feathersjs/authentication-oauth2/tree/v0.3.2) (2017-10-15)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.3.1...v0.3.2)
-
-**Closed issues:**
-
-- Examples are no longer working due to missing email property [\#40](https://github.com/feathersjs/authentication-oauth2/issues/40)
-- Log a warning if this.service.id is undefined or null [\#21](https://github.com/feathersjs/authentication-oauth2/issues/21)
-
-**Merged pull requests:**
-
-- Erroring if service.id is undefined. Closes \#21. [\#47](https://github.com/feathersjs/authentication-oauth2/pull/47) ([ekryski](https://github.com/ekryski))
-- Update mocha to the latest version 🚀 [\#46](https://github.com/feathersjs/authentication-oauth2/pull/46) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update examples to work with latest OAuth payload [\#45](https://github.com/feathersjs/authentication-oauth2/pull/45) ([teddy-error](https://github.com/teddy-error))
-- Update sinon to the latest version 🚀 [\#43](https://github.com/feathersjs/authentication-oauth2/pull/43) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.3.1](https://github.com/feathersjs/authentication-oauth2/tree/v0.3.1) (2017-09-27)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.3.0...v0.3.1)
-
-**Closed issues:**
-
-- Google "hostedDomain" not working [\#13](https://github.com/feathersjs/authentication-oauth2/issues/13)
-
-**Merged pull requests:**
-
-- Simplified redirectOptions \(\#42\) [\#44](https://github.com/feathersjs/authentication-oauth2/pull/44) ([nsainaney](https://github.com/nsainaney))
-
-## [v0.3.0](https://github.com/feathersjs/authentication-oauth2/tree/v0.3.0) (2017-09-25)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.6...v0.3.0)
-
-**Closed issues:**
-
-- Missing params on OAuth redirect creation [\#41](https://github.com/feathersjs/authentication-oauth2/issues/41)
-- how to custom callback page after authetication [\#39](https://github.com/feathersjs/authentication-oauth2/issues/39)
-- profileUrl is undefined [\#38](https://github.com/feathersjs/authentication-oauth2/issues/38)
-- Use patch to update the entity instead of update [\#31](https://github.com/feathersjs/authentication-oauth2/issues/31)
-- Update existing user in verifier will change the user password hash from an already hashed password. [\#19](https://github.com/feathersjs/authentication-oauth2/issues/19)
-
-**Merged pull requests:**
-
-- Added support for redirect options on strategy options \(\#41\) [\#42](https://github.com/feathersjs/authentication-oauth2/pull/42) ([nsainaney](https://github.com/nsainaney))
-- \#19 Fix: using patch to update entity in verifier [\#20](https://github.com/feathersjs/authentication-oauth2/pull/20) ([skinnyworm](https://github.com/skinnyworm))
-
-## [v0.2.6](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.6) (2017-09-12)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.5...v0.2.6)
-
-**Closed issues:**
-
-- 0.2.5 introduced breaking changes [\#36](https://github.com/feathersjs/authentication-oauth2/issues/36)
-- An in-range update of feathers is breaking the build 🚨 [\#34](https://github.com/feathersjs/authentication-oauth2/issues/34)
-
-**Merged pull requests:**
-
-- Fix old property name fallback for backwards compatibility [\#37](https://github.com/feathersjs/authentication-oauth2/pull/37) ([daffl](https://github.com/daffl))
-- Include babel-polyfill and package-lock.json [\#35](https://github.com/feathersjs/authentication-oauth2/pull/35) ([daffl](https://github.com/daffl))
-- Update debug to the latest version 🚀 [\#30](https://github.com/feathersjs/authentication-oauth2/pull/30) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update sinon to the latest version 🚀 [\#29](https://github.com/feathersjs/authentication-oauth2/pull/29) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.2.5](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.5) (2017-06-21)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.4...v0.2.5)
-
-**Closed issues:**
-
-- Module is using the wrong default config key [\#23](https://github.com/feathersjs/authentication-oauth2/issues/23)
-- Generated default.json is missing scope for Google OAuth [\#14](https://github.com/feathersjs/authentication-oauth2/issues/14)
-- Cookie not getting set [\#12](https://github.com/feathersjs/authentication-oauth2/issues/12)
-
-**Merged pull requests:**
-
-- Greenkeeper/chai 4.0.2 [\#28](https://github.com/feathersjs/authentication-oauth2/pull/28) ([daffl](https://github.com/daffl))
-- using correct auth key. Closes 23. [\#24](https://github.com/feathersjs/authentication-oauth2/pull/24) ([ekryski](https://github.com/ekryski))
-- Update semistandard to the latest version 🚀 [\#17](https://github.com/feathersjs/authentication-oauth2/pull/17) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update feathers-hooks to the latest version 🚀 [\#16](https://github.com/feathersjs/authentication-oauth2/pull/16) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-- Update dependencies to enable Greenkeeper 🌴 [\#15](https://github.com/feathersjs/authentication-oauth2/pull/15) ([greenkeeper[bot]](https://github.com/apps/greenkeeper))
-
-## [v0.2.4](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.4) (2017-03-24)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.3...v0.2.4)
-
-**Closed issues:**
-
-- Support oauth2 endpoint within sub-app or reverse proxy [\#9](https://github.com/feathersjs/authentication-oauth2/issues/9)
-- OAuth JWT user data [\#4](https://github.com/feathersjs/authentication-oauth2/issues/4)
-
-**Merged pull requests:**
-
-- Follow failureRedirect on oauth2 authentication failure [\#11](https://github.com/feathersjs/authentication-oauth2/pull/11) ([buske](https://github.com/buske))
-- Issue \#9: Add callbackPath option to support oauth2 endpoint within sub-app or server proxy [\#10](https://github.com/feathersjs/authentication-oauth2/pull/10) ([buske](https://github.com/buske))
-
-## [v0.2.3](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.3) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.2...v0.2.3)
-
-## [v0.2.2](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.2) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.1...v0.2.2)
-
-**Merged pull requests:**
-
-- fixing success and failure redirects [\#6](https://github.com/feathersjs/authentication-oauth2/pull/6) ([ekryski](https://github.com/ekryski))
-
-## [v0.2.1](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.1) (2016-12-14)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.2.0...v0.2.1)
-
-## [v0.2.0](https://github.com/feathersjs/authentication-oauth2/tree/v0.2.0) (2016-11-23)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.1.2...v0.2.0)
-
-**Merged pull requests:**
-
-- adding compatibility with payload generation [\#3](https://github.com/feathersjs/authentication-oauth2/pull/3) ([ekryski](https://github.com/ekryski))
-
-## [v0.1.2](https://github.com/feathersjs/authentication-oauth2/tree/v0.1.2) (2016-11-20)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.1.1...v0.1.2)
-
-**Merged pull requests:**
-
-- include feathers-commons and use omit, pick, and makeUrl from that. [\#2](https://github.com/feathersjs/authentication-oauth2/pull/2) ([ekryski](https://github.com/ekryski))
-
-## [v0.1.1](https://github.com/feathersjs/authentication-oauth2/tree/v0.1.1) (2016-11-19)
-[Full Changelog](https://github.com/feathersjs/authentication-oauth2/compare/v0.1.0...v0.1.1)
-
-**Merged pull requests:**
-
-- pulling options for strategy from global auth config [\#1](https://github.com/feathersjs/authentication-oauth2/pull/1) ([ekryski](https://github.com/ekryski))
-
-## [v0.1.0](https://github.com/feathersjs/authentication-oauth2/tree/v0.1.0) (2016-11-16)
-
-
-\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
diff --git a/packages/authentication-oauth2/LICENSE b/packages/authentication-oauth2/LICENSE
deleted file mode 100644
index 6bfc0adefc..0000000000
--- a/packages/authentication-oauth2/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2018 Feathers
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
diff --git a/packages/authentication-oauth2/README.md b/packages/authentication-oauth2/README.md
deleted file mode 100644
index 11689091d8..0000000000
--- a/packages/authentication-oauth2/README.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# @feathersjs/authentication-oauth2
-
-[![Build Status](https://travis-ci.org/feathersjs/feathers.png?branch=master)](https://travis-ci.org/feathersjs/feathers)
-[![Dependency Status](https://img.shields.io/david/feathersjs/feathers.svg?style=flat-square&path=packages/authentication-oauth2)](https://david-dm.org/feathersjs/feathers?path=packages/authentication-oauth2)
-[![Download Status](https://img.shields.io/npm/dm/@feathersjs/authentication-oauth2.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/authentication-oauth2)
-
-> An OAuth2 authentication strategy for feathers-authentication using Passport
-
-## Installation
-
-```
-npm install @feathersjs/authentication-oauth2 --save
-```
-
-**Note:** This is only compatibile with `feathers-authentication@1.x` and above.
-
-## Quick example
-
-```js
-const feathers = require('@feathersjs/feathers');
-const authentication = require('feathers-authentication');
-const jwt = require('feathers-authentication-jwt');
-const oauth2 = require('@feathersjs/authentication-oauth2');
-const FacebookStrategy = require('passport-facebook').Strategy;
-const app = feathers();
-
-// Setup authentication
-app.configure(authentication(settings));
-app.configure(jwt());
-app.configure(oauth2({
- name: 'facebook',
- Strategy: FacebookStrategy,
- clientID: '',
- clientSecret: '',
- scope: ['public_profile', 'email']
-}));
-
-// Setup a hook to only allow valid JWTs to authenticate
-// and get new JWT access tokens
-app.service('authentication').hooks({
- before: {
- create: [
- authentication.hooks.authenticate(['jwt'])
- ]
- }
-});
-```
-
-## Documentation
-
-Please refer to the [@feathersjs/authentication-oauth2 API documentation](https://docs.feathersjs.com/api/authentication/oauth2.html) for more details.
-
-## License
-
-Copyright (c) 2018
-
-Licensed under the [MIT license](LICENSE).
diff --git a/packages/authentication-oauth2/lib/express/error-handler.js b/packages/authentication-oauth2/lib/express/error-handler.js
deleted file mode 100644
index cdb6211806..0000000000
--- a/packages/authentication-oauth2/lib/express/error-handler.js
+++ /dev/null
@@ -1,11 +0,0 @@
-module.exports = function OAuthErrorHandler (options = {}) {
- return function (err, req, res, next) {
- // Set __redirect so that later middleware (e.g., auth.express.failureRedirect) can redirect accordingly
- if (options.failureRedirect) {
- res.hook = { data: {} };
- Object.defineProperty(res.hook.data, '__redirect', { value: { status: 302, url: options.failureRedirect } });
- }
-
- next(err);
- };
-};
diff --git a/packages/authentication-oauth2/lib/express/handler.js b/packages/authentication-oauth2/lib/express/handler.js
deleted file mode 100644
index e4f1a1254b..0000000000
--- a/packages/authentication-oauth2/lib/express/handler.js
+++ /dev/null
@@ -1,34 +0,0 @@
-const Debug = require('debug');
-
-const debug = Debug('@feathersjs/authentication-oauth2:handler');
-
-module.exports = function OAuthHandler (options = {}) {
- return function (req, res, next) {
- const app = req.app;
- const authSettings = app.get('auth') || app.get('authentication') || {};
- const entity = req[options.entity];
- const payload = req.payload;
- const params = {
- authenticated: true,
- [options.entity]: entity,
- payload
- };
- const data = {
- [options.entity]: entity,
- payload
- };
-
- debug(`Executing '${options.name}' OAuth Callback`);
- debug(`Calling create on '${authSettings.path}' service with`, entity);
- app.service(authSettings.path).create(data, params).then(result => {
- res.data = result;
-
- if (options.successRedirect) {
- res.hook = { data: {} };
- Object.defineProperty(res.hook.data, '__redirect', { value: { status: 302, url: options.successRedirect } });
- }
-
- next();
- }).catch(next);
- };
-};
diff --git a/packages/authentication-oauth2/lib/index.js b/packages/authentication-oauth2/lib/index.js
deleted file mode 100644
index 32bc420935..0000000000
--- a/packages/authentication-oauth2/lib/index.js
+++ /dev/null
@@ -1,125 +0,0 @@
-const Debug = require('debug');
-const auth = require('@feathersjs/authentication');
-
-const { rest } = require('@feathersjs/express');
-const { _, makeUrl } = require('@feathersjs/commons');
-
-const merge = require('lodash.merge');
-const defaultHandler = require('./express/handler');
-const defaultErrorHandler = require('./express/error-handler');
-const DefaultVerifier = require('./verifier');
-
-const debug = Debug('@feathersjs/authentication-oauth2');
-
-const { omit, pick } = _;
-const INCLUDE_KEYS = [
- 'entity',
- 'service',
- 'passReqToCallback',
- 'session'
-];
-
-const EXCLUDE_KEYS = ['Verifier', 'Strategy', 'formatter'];
-
-// When the OAuth callback is called, req.user will always be null
-// The following extracts the user from the jwt cookie if present
-// This ensures that the social link happens on an existing user
-function _callbackAuthenticator (config) {
- return function (req, res, next) {
- auth.express.authenticate('jwt', config)(req, res, () => {
- // We have to mark this as unauthenticated even though req.user may be set
- // because we still need the OAuth strategy to run in next()
- req.authenticated = false;
- next();
- });
- };
-}
-
-function init (options = {}) {
- return function oauth2Auth () {
- const app = this;
- const _super = app.setup;
-
- if (!app.passport) {
- throw new Error(`Can not find app.passport. Did you initialize feathers-authentication before @feathersjs/authentication-oauth2?`);
- }
-
- let { name, Strategy } = options;
-
- if (!name) {
- throw new Error(`You must provide a strategy 'name'.`);
- }
-
- if (!Strategy) {
- throw new Error(`You must provide a passport 'Strategy' instance.`);
- }
-
- const authSettings = app.get('auth') || app.get('authentication') || {};
-
- // Attempt to pull options from the global auth config
- // for this provider.
- const providerSettings = authSettings[name] || {};
- const oauth2Settings = merge({
- idField: `${name}Id`,
- path: `/auth/${name}`,
- __oauth: true
- }, pick(authSettings, ...INCLUDE_KEYS), providerSettings, omit(options, ...EXCLUDE_KEYS));
-
- // Set callback defaults based on provided path
- oauth2Settings.callbackPath = oauth2Settings.callbackPath || `${oauth2Settings.path}/callback`;
- oauth2Settings.callbackURL = oauth2Settings.callbackURL || makeUrl(oauth2Settings.callbackPath, app);
-
- if (!oauth2Settings.clientID) {
- throw new Error(`You must provide a 'clientID' in your authentication configuration or pass one explicitly`);
- }
-
- if (!oauth2Settings.clientSecret) {
- throw new Error(`You must provide a 'clientSecret' in your authentication configuration or pass one explicitly`);
- }
-
- const Verifier = options.Verifier || DefaultVerifier;
- const formatter = options.formatter || rest.formatter;
- const handler = options.handler || defaultHandler(oauth2Settings);
- const errorHandler = typeof options.errorHandler === 'function' ? options.errorHandler(oauth2Settings) : defaultErrorHandler(oauth2Settings);
-
- // register OAuth middleware
- debug(`Registering '${name}' Express OAuth middleware`);
- app.get(oauth2Settings.path, auth.express.authenticate(name, omit(oauth2Settings, 'state')));
- app.get(
- oauth2Settings.callbackPath,
- _callbackAuthenticator(authSettings),
- auth.express.authenticate(name, omit(oauth2Settings, 'state')),
- handler,
- errorHandler,
- auth.express.emitEvents(authSettings, app),
- auth.express.setCookie(authSettings),
- auth.express.successRedirect(),
- auth.express.failureRedirect(authSettings),
- formatter
- );
-
- app.setup = function () {
- let result = _super.apply(this, arguments);
- let verifier = new Verifier(app, oauth2Settings);
-
- if (!verifier.verify) {
- throw new Error(`Your verifier must implement a 'verify' function. It should have the same signature as a oauth2 passport verify callback.`);
- }
-
- // Register 'oauth2' strategy with passport
- debug('Registering oauth2 authentication strategy with options:', oauth2Settings);
- app.passport.use(name, new Strategy(oauth2Settings, verifier.verify.bind(verifier)));
- app.passport.options(name, oauth2Settings);
-
- return result;
- };
- };
-}
-
-module.exports = init;
-
-// Exposed Modules
-Object.assign(module.exports, {
- default: init,
- Verifier: DefaultVerifier
-});
diff --git a/packages/authentication-oauth2/lib/verifier.js b/packages/authentication-oauth2/lib/verifier.js
deleted file mode 100644
index 710cf7651a..0000000000
--- a/packages/authentication-oauth2/lib/verifier.js
+++ /dev/null
@@ -1,122 +0,0 @@
-const Debug = require('debug');
-
-const debug = Debug('@feathersjs/authentication-oauth2:verify');
-
-class OAuth2Verifier {
- constructor (app, options = {}) {
- this.app = app;
- this.options = options;
- this.service = typeof options.service === 'string' ? app.service(options.service) : options.service;
-
- options.makeQuery = options.makeQuery || function (profile, options) {
- return { [options.idField]: profile.id }; // facebookId: profile.id
- };
-
- if (!this.service) {
- throw new Error(`options.service does not exist.\n\tMake sure you are passing a valid service path or service instance and it is initialized before @feathersjs/authentication-oauth2.`);
- }
-
- this._createEntity = this._createEntity.bind(this);
- this._updateEntity = this._updateEntity.bind(this);
- this._normalizeResult = this._normalizeResult.bind(this);
- this.verify = this.verify.bind(this);
- }
-
- _normalizeResult (results) {
- // Paginated services return the array of results in the data attribute.
- let entities = results.data ? results.data : results;
- let entity = entities[0];
-
- // Handle entity not found.
- if (!entity) {
- return Promise.resolve(null);
- }
-
- // Handle updating mongoose models
- if (typeof entity.toObject === 'function') {
- entity = entity.toObject();
- } else if (typeof entity.toJSON === 'function') {
- // Handle updating Sequelize models
- entity = entity.toJSON();
- }
-
- debug(`${this.options.entity} found`);
- return Promise.resolve(entity);
- }
-
- _updateEntity (entity, data) {
- const options = this.options;
- const name = options.name;
- const id = entity[this.service.id];
- debug(`Updating ${options.entity}: ${id}`);
-
- const newData = {
- [options.idField]: data.profile.id,
- [name]: data
- };
-
- return this.service.patch(id, newData, { oauth: { provider: name } });
- }
-
- _createEntity (data) {
- const options = this.options;
- const name = options.name;
- const entity = {
- [options.idField]: data.profile.id,
- [name]: data
- };
-
- const id = entity[options.idField];
- debug(`Creating new ${options.entity} with ${options.idField}: ${id}`);
-
- return this.service.create(entity, { oauth: { provider: name } });
- }
-
- _setPayloadAndDone (entity, done) {
- const id = entity[this.service.id];
- const payload = { [`${this.options.entity}Id`]: id };
- done(null, entity, payload);
- }
-
- verify (req, accessToken, refreshToken, profile, done) {
- debug('Checking credentials');
- const options = this.options;
- const query = Object.assign({}, options.makeQuery(profile, options), { $limit: 1 });
- const data = { profile, accessToken, refreshToken };
- let existing;
-
- if (this.service.id === null || this.service.id === undefined) {
- debug('failed: the service.id was not set');
- return done(new Error('the `id` property must be set on the entity service for authentication'));
- }
-
- // Check request object for an existing entity
- if (req && req[options.entity]) {
- existing = req[options.entity];
- }
-
- // Check the request that came from a hook for an existing entity
- if (!existing && req && req.params && req.params[options.entity]) {
- existing = req.params[options.entity];
- }
-
- // If there is already an entity on the request object (ie. they are
- // already authenticated) attach the profile to the existing entity
- // because they are likely "linking" social accounts/profiles.
- if (existing) {
- return this._updateEntity(existing, data)
- .then(entity => this._setPayloadAndDone(entity, done))
- .catch(error => error ? done(error) : done(null, error));
- }
-
- // Find or create the user since they could have signed up via facebook.
- this.service
- .find({ query })
- .then(this._normalizeResult)
- .then(entity => entity ? this._updateEntity(entity, data) : this._createEntity(data))
- .then(entity => this._setPayloadAndDone(entity, done))
- .catch(error => error ? done(error) : done(null, error));
- }
-}
-
-module.exports = OAuth2Verifier;
diff --git a/packages/authentication-oauth2/package.json b/packages/authentication-oauth2/package.json
deleted file mode 100644
index d93c8bb101..0000000000
--- a/packages/authentication-oauth2/package.json
+++ /dev/null
@@ -1,56 +0,0 @@
-{
- "name": "@feathersjs/authentication-oauth2",
- "description": "An OAuth2 authentication strategy for feathers-authentication using Passport",
- "version": "1.3.1",
- "homepage": "https://feathersjs.com",
- "main": "lib/",
- "keywords": [
- "feathers",
- "feathers-plugin"
- ],
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git://github.com/feathersjs/feathers.git"
- },
- "author": {
- "name": "Feathers contributors",
- "email": "hello@feathersjs.com",
- "url": "https://feathersjs.com"
- },
- "contributors": [],
- "bugs": {
- "url": "https://github.com/feathersjs/feathers/issues"
- },
- "engines": {
- "node": ">= 6"
- },
- "scripts": {
- "test": "mocha --opts ../../mocha.opts"
- },
- "directories": {
- "lib": "lib"
- },
- "publishConfig": {
- "access": "public"
- },
- "dependencies": {
- "@feathersjs/commons": "^4.0.0",
- "@feathersjs/errors": "^3.3.6",
- "@feathersjs/express": "^1.3.1",
- "debug": "^4.1.1",
- "lodash.merge": "^4.6.1"
- },
- "devDependencies": {
- "@feathersjs/authentication": "^2.1.16",
- "@feathersjs/feathers": "^3.3.1",
- "body-parser": "^1.18.3",
- "chai": "^4.2.0",
- "feathers-memory": "^3.0.2",
- "mocha": "^5.2.0",
- "passport-github": "^1.1.0",
- "passport-strategy": "^1.0.0",
- "sinon": "^7.2.3",
- "sinon-chai": "^3.3.0"
- }
-}
diff --git a/packages/authentication-oauth2/test/express/error-handler.test.js b/packages/authentication-oauth2/test/express/error-handler.test.js
deleted file mode 100644
index c7a9fe35ac..0000000000
--- a/packages/authentication-oauth2/test/express/error-handler.test.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const { expect } = require('chai');
-
-const errorHandler = require('../../lib/express/error-handler');
-
-describe('express:error-handler', () => {
- let req;
- let res;
- let error;
- let options;
-
- beforeEach(() => {
- req = {};
- options = {};
- res = {
- hook: {
- data: {
- __redirect: {
- url: '/app'
- }
- }
- }
- };
- error = new Error('Authentication Error');
- });
-
- describe('when failureRedirect is set', () => {
- it('sets the redirect object on the response', done => {
- options.failureRedirect = '/login';
- errorHandler(options)(error, req, res, () => {
- expect(res.hook.data.__redirect).to.deep.equal({ status: 302, url: options.failureRedirect });
- done();
- });
- });
-
- it('calls next with error', done => {
- delete res.hook;
- errorHandler(options)(error, req, res, e => {
- expect(e).to.equal(error);
- done();
- });
- });
- });
-
- describe('when failureRedirect is not set', done => {
- it('calls next with error', done => {
- delete res.hook;
- errorHandler(options)(error, req, res, e => {
- expect(e).to.equal(error);
- done();
- });
- });
- });
-});
diff --git a/packages/authentication-oauth2/test/express/handler.test.js b/packages/authentication-oauth2/test/express/handler.test.js
deleted file mode 100644
index 665a25b563..0000000000
--- a/packages/authentication-oauth2/test/express/handler.test.js
+++ /dev/null
@@ -1,96 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-const handler = require('../../lib/express/handler');
-
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('express:handler', () => {
- let req;
- let res;
- let service;
- let options;
- let user;
- let payload;
- let accessToken = 'access';
-
- beforeEach(() => {
- payload = { userId: 1 };
- user = { name: 'Bob' };
- options = { entity: 'user', name: 'github' };
- service = {
- create: sinon.stub().returns(Promise.resolve({ accessToken }))
- };
-
- req = {
- user,
- payload,
- app: {
- get: sinon.stub().returns({ path: '/authentication' }),
- service: sinon.stub().returns(service)
- }
- };
- res = {};
- });
-
- afterEach(() => {
- req.app.service.reset();
- service.create.reset();
- });
-
- it('calls create on the authentication service', done => {
- const params = {
- authenticated: true,
- payload,
- user
- };
-
- handler(options)(req, res, () => {
- expect(req.app.service).to.have.been.calledOnce;
- expect(req.app.service).to.have.been.calledWith('/authentication');
- expect(service.create).to.have.been.calledOnce;
- expect(service.create).to.have.been.calledWith({ user, payload }, params);
- done();
- });
- });
-
- describe('when create succeeds', () => {
- it('sets res.data', done => {
- handler(options)(req, res, () => {
- expect(res.data).to.deep.equal({ accessToken });
- done();
- });
- });
-
- it('calls next', done => {
- handler(options)(req, res, done);
- });
-
- describe('when successRedirect is set', () => {
- it('sets the redirect object on the request', done => {
- options.successRedirect = '/app';
- handler(options)(req, res, () => {
- expect(res.hook.data.__redirect).to.deep.equal({ status: 302, url: options.successRedirect });
- done();
- });
- });
- });
- });
-
- describe('when create fails', () => {
- beforeEach(() => {
- service.create = sinon.stub().returns(Promise.reject(new Error('Auth Error')));
- req.app.service = sinon.stub().returns(service);
- });
-
- it('calls next with an error', done => {
- handler(options)(req, res, error => {
- expect(error).to.not.equal(undefined);
- done();
- });
- });
- });
-});
diff --git a/packages/authentication-oauth2/test/fixtures/strategy.js b/packages/authentication-oauth2/test/fixtures/strategy.js
deleted file mode 100644
index 58142848d4..0000000000
--- a/packages/authentication-oauth2/test/fixtures/strategy.js
+++ /dev/null
@@ -1,41 +0,0 @@
-const passport = require('passport-strategy');
-const util = require('util');
-
-function Strategy (options, verify) {
- passport.Strategy.call(this);
- this.name = 'mock';
- this._options = options;
- this._verify = verify;
-}
-
-util.inherits(Strategy, passport.Strategy);
-
-Strategy.prototype.authenticate = function (req, options) {
- const accessToken = 'mockAccessToken';
- const refreshToken = 'mockRefreshToken';
- const profile = { name: 'Mocky Mockerson' };
-
- const callback = function (error, user, info) {
- if (error) {
- return this.error(error);
- }
-
- if (info && info.pass) {
- return this.pass();
- }
-
- if (info && info.url) {
- return this.redirect(info.url, info.status);
- }
-
- if (!user) {
- return this.fail(info.challenge, info.status);
- }
-
- return this.success(user, info);
- }.bind(this);
-
- this._verify(req, accessToken, refreshToken, profile, callback);
-};
-
-module.exports = Strategy;
diff --git a/packages/authentication-oauth2/test/index.test.js b/packages/authentication-oauth2/test/index.test.js
deleted file mode 100644
index 9343675973..0000000000
--- a/packages/authentication-oauth2/test/index.test.js
+++ /dev/null
@@ -1,291 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-const memory = require('feathers-memory');
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const oauth2 = require('../lib');
-const Strategy = require('./fixtures/strategy');
-
-const { Verifier } = oauth2;
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('@feathersjs/authentication-oauth2', () => {
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib')).to.equal('function');
- });
-
- it('exports default', () => {
- expect(oauth2.default).to.equal(oauth2);
- });
-
- it('basic functionality', () => {
- expect(typeof oauth2).to.equal('function');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- expect(typeof oauth2.Verifier).to.equal('function');
- });
-
- describe('initialization', () => {
- let app;
- let config;
- let globalConfig;
-
- beforeEach(() => {
- config = {
- name: 'github',
- Strategy,
- clientID: '1234',
- clientSecret: 'secret'
- };
-
- globalConfig = {
- secret: 'supersecret',
- github: {
- clientID: '1234',
- clientSecret: 'secret',
- scope: ['user']
- }
- };
-
- app = expressify(feathers());
- app.set('host', 'localhost');
- app.set('port', 3030);
- app.use('/users', memory());
- app.configure(authentication(globalConfig));
- });
-
- it('throws an error if passport has not been registered', () => {
- expect(() => {
- expressify(feathers()).configure(oauth2());
- }).to.throw();
- });
-
- it('throws an error if strategy name is missing', () => {
- expect(() => {
- delete config.name;
- app.configure(oauth2(config));
- }).to.throw();
- });
-
- it('throws an error if Strategy is missing', () => {
- expect(() => {
- delete config.Strategy;
- app.configure(oauth2(config));
- }).to.throw();
- });
-
- it('throws an error if clientID is missing', () => {
- expect(() => {
- delete config.clientID;
- delete globalConfig.github.clientID;
- expressify(feathers()).configure(authentication(globalConfig)).configure(oauth2(config));
- }).to.throw();
- });
-
- it('throws an error if clientSecret is missing', () => {
- expect(() => {
- delete config.clientSecret;
- delete globalConfig.github.clientSecret;
- expressify(feathers()).configure(authentication(globalConfig)).configure(oauth2(config));
- }).to.throw();
- });
-
- it('registers the oauth2 passport strategy', () => {
- sinon.spy(app.passport, 'use');
- sinon.spy(config, 'Strategy');
- app.configure(oauth2(config));
- app.setup();
-
- expect(config.Strategy).to.have.been.calledOnce;
- expect(app.passport.use).to.have.been.calledWith(config.name);
-
- app.passport.use.restore();
- config.Strategy.restore();
- });
-
- it('registers the strategy options', () => {
- sinon.spy(app.passport, 'options');
- app.configure(oauth2(config));
- app.setup();
-
- expect(app.passport.options).to.have.been.calledOnce;
-
- app.passport.options.restore();
- });
-
- it('registers the redirect options on strategy options', () => {
- sinon.spy(authentication.express, 'authenticate');
-
- const mergedOptions = Object.assign({}, config, globalConfig);
- app.configure(oauth2(mergedOptions));
- app.setup();
-
- delete mergedOptions.Strategy;
- expect(authentication.express.authenticate).to.have.been.calledWith(config.name, sinon.match(mergedOptions));
-
- authentication.express.authenticate.restore();
- });
-
- describe('passport strategy options', () => {
- let authOptions;
- let args;
-
- beforeEach(() => {
- config.custom = true;
- sinon.spy(config, 'Strategy');
- app.configure(oauth2(config));
- app.setup();
- authOptions = app.get('authentication');
- args = config.Strategy.getCall(0).args[0];
- });
-
- afterEach(() => {
- config.Strategy.restore();
- });
-
- it('sets path', () => {
- expect(args.path).to.equal(`/auth/${config.name}`);
- });
-
- it('sets callbackPath', () => {
- expect(args.callbackPath).to.equal(`/auth/${config.name}/callback`);
- });
-
- it('sets callbackURL', () => {
- expect(args.callbackURL).to.equal(`http://localhost:3030/auth/${config.name}/callback`);
- });
-
- it('sets idField', () => {
- expect(args.idField).to.equal(`${config.name}Id`);
- });
-
- it('sets entity', () => {
- expect(args.entity).to.equal(authOptions.entity);
- });
-
- it('sets service', () => {
- expect(args.service).to.equal(authOptions.service);
- });
-
- it('sets session', () => {
- expect(args.session).to.equal(authOptions.session);
- });
-
- it('sets passReqToCallback', () => {
- expect(args.passReqToCallback).to.equal(authOptions.passReqToCallback);
- });
-
- it('supports setting custom options', () => {
- expect(args.custom).to.equal(true);
- });
- });
-
- it('mixes in global config for strategy', () => {
- delete config.clientID;
- delete config.clientSecret;
- sinon.spy(config, 'Strategy');
-
- app.configure(oauth2(config));
- app.setup();
-
- expect(config.Strategy.getCall(0).args[0].scope).to.deep.equal(['user']);
-
- config.Strategy.restore();
- });
-
- it('supports overriding default options', () => {
- sinon.spy(config, 'Strategy');
- config.entity = 'organization';
- app.configure(oauth2(config));
- app.setup();
-
- expect(config.Strategy.getCall(0).args[0].entity).to.equal('organization');
-
- config.Strategy.restore();
- });
-
- it('registers express get route', () => {
- sinon.spy(app, 'get');
- app.configure(oauth2(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(`/auth/${config.name}`);
-
- app.get.restore();
- });
-
- it('registers express callback route', () => {
- sinon.spy(app, 'get');
- app.configure(oauth2(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(`/auth/${config.name}/callback`);
-
- app.get.restore();
- });
-
- it('registers custom express callback route', () => {
- sinon.spy(app, 'get');
- config.callbackPath = `/v1/api/auth/${config.name}/callback`;
- app.configure(oauth2(config));
- app.setup();
-
- expect(app.get).to.have.been.calledWith(config.callbackPath);
-
- app.get.restore();
- });
-
- describe('custom Verifier', () => {
- it('throws an error if a verify function is missing', () => {
- expect(() => {
- class CustomVerifier {
- constructor (app) {
- this.app = app;
- }
- }
- config.Verifier = CustomVerifier;
- app.configure(oauth2(config));
- app.setup();
- }).to.throw();
- });
-
- it('verifies through custom verify function', () => {
- const User = {
- email: 'admin@feathersjs.com',
- password: 'password'
- };
-
- const req = {
- query: {},
- body: Object.assign({}, User),
- headers: {},
- cookies: {}
- };
- class CustomVerifier extends Verifier {
- verify (req, accessToken, refreshToken, profile, done) {
- expect(accessToken).to.equal('mockAccessToken');
- expect(refreshToken).to.equal('mockRefreshToken');
- expect(profile).to.deep.equal({ name: 'Mocky Mockerson' });
- done(null, User);
- }
- }
-
- config.Verifier = CustomVerifier;
- app.configure(oauth2(config));
- app.setup();
-
- return app.authenticate('github')(req).then(result => {
- expect(result.data.user).to.deep.equal(User);
- });
- });
- });
- });
-});
diff --git a/packages/authentication-oauth2/test/verifier.test.js b/packages/authentication-oauth2/test/verifier.test.js
deleted file mode 100644
index 102ede1ba7..0000000000
--- a/packages/authentication-oauth2/test/verifier.test.js
+++ /dev/null
@@ -1,331 +0,0 @@
-/* eslint-disable no-unused-expressions */
-const feathers = require('@feathersjs/feathers');
-const expressify = require('@feathersjs/express');
-const authentication = require('@feathersjs/authentication');
-
-const { Verifier } = require('../lib');
-
-const chai = require('chai');
-const sinon = require('sinon');
-const sinonChai = require('sinon-chai');
-
-const { expect } = chai;
-
-chai.use(sinonChai);
-
-describe('Verifier', () => {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- user = { id: 1, email: 'admin@feathersjs.com' };
- service = {
- id: 'id',
- find: sinon.stub().returns(Promise.resolve([user])),
- create: sinon.stub().returns(Promise.resolve(user)),
- patch: sinon.stub().returns(Promise.resolve(user))
- };
-
- app = expressify(feathers());
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = app.get('authentication');
- options.name = 'github';
- options.idField = 'githubId';
-
- verifier = new Verifier(app, options);
- });
-
- it('is CommonJS compatible', () => {
- expect(typeof require('../lib/verifier')).to.equal('function');
- });
-
- it('exposes the Verifier class', () => {
- expect(typeof Verifier).to.equal('function');
- });
-
- describe('constructor', () => {
- it('retains an app reference', () => {
- expect(verifier.app).to.deep.equal(app);
- });
-
- it('sets options', () => {
- expect(verifier.options).to.deep.equal(options);
- });
-
- it('sets service using service path', () => {
- expect(verifier.service).to.deep.equal(app.service('users'));
- });
-
- it('sets a passed in service instance', () => {
- options.service = service;
- expect(new Verifier(app, options).service).to.deep.equal(service);
- });
-
- describe('when service is undefined', () => {
- it('throws an error', () => {
- expect(() => {
- new Verifier(app, {}); // eslint-disable-line
- }).to.throw();
- });
- });
- });
-
- describe('_updateEntity', () => {
- let entity;
- let data;
- let args;
-
- beforeEach(() => {
- entity = { id: 1, name: 'Admin' };
- data = {
- accessToken: 'access',
- refreshToken: 'refresh',
- profile: {
- id: 1234,
- name: 'Admin'
- }
- };
- return verifier._updateEntity(entity, data).then(() => {
- args = service.patch.getCall(0).args;
- });
- });
-
- it('calls patch on passed in service', () => {
- expect(service.patch).to.have.been.calledOnce;
- });
-
- it('passes id', () => {
- expect(args[0]).to.equal(entity.id);
- });
-
- it('passes patch data', () => {
- expect(args[1].githubId).to.equal(data.profile.id);
- expect(args[1].github).to.deep.equal(data);
- });
-
- it('passes oauth provider via params', () => {
- expect(args[2]).to.deep.equal({ oauth: { provider: 'github' } });
- });
- });
-
- describe('_createEntity', () => {
- let data;
- let args;
-
- beforeEach(() => {
- data = {
- accessToken: 'access',
- refreshToken: 'refresh',
- profile: {
- id: 1234,
- name: 'Admin'
- }
- };
- return verifier._createEntity(data).then(() => {
- args = service.create.getCall(0).args;
- });
- });
-
- it('calls create on passed in service', () => {
- expect(service.create).to.have.been.calledOnce;
- });
-
- it('passes entity', () => {
- expect(args[0].githubId).to.equal(data.profile.id);
- expect(args[0].github).to.deep.equal(data);
- });
-
- it('passes oauth provider via params', () => {
- expect(args[1]).to.deep.equal({ oauth: { provider: 'github' } });
- });
- });
-
- describe('_normalizeResult', () => {
- describe('when has results', () => {
- it('returns entity when paginated', () => {
- return verifier._normalizeResult({ data: [user] }).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('returns entity when not paginated', () => {
- return verifier._normalizeResult([user]).then(result => {
- expect(result).to.deep.equal(user);
- });
- });
-
- it('calls toObject on entity when present', () => {
- user.toObject = sinon.spy();
- return verifier._normalizeResult({ data: [user] }).then(() => {
- expect(user.toObject).to.have.been.calledOnce;
- });
- });
-
- it('calls toJSON on entity when present', () => {
- user.toJSON = sinon.spy();
- return verifier._normalizeResult({ data: [user] }).then(() => {
- expect(user.toJSON).to.have.been.calledOnce;
- });
- });
- });
-
- describe('when no results', () => {
- it('rejects with false when paginated', () => {
- return verifier._normalizeResult({ data: [] }).catch(error => {
- expect(error).to.equal(false);
- });
- });
-
- it('rejects with false when not paginated', () => {
- return verifier._normalizeResult([]).catch(error => {
- expect(error).to.equal(false);
- });
- });
- });
- });
-
- describe('verify', () => {
- it('calls find on the provided service', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- const query = { githubId: 1234, $limit: 1 };
- expect(service.find).to.have.been.calledOnce;
- expect(service.find).to.have.been.calledWith({ query });
- done();
- });
- });
-
- it('calls with query from makeQuery', done => {
- options = Object.assign({}, options, { makeQuery: sinon.stub().returns({ key: 'value' }) });
- verifier = new Verifier(app, options);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- const query = { key: 'value', $limit: 1 };
- expect(options.makeQuery).to.have.been.calledOnce;
- expect(service.find).to.have.been.calledWith({ query });
- done();
- });
- });
-
- it('calls _normalizeResult', done => {
- sinon.spy(verifier, '_normalizeResult');
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._normalizeResult).to.have.been.calledOnce;
- verifier._normalizeResult.restore();
- done();
- });
- });
-
- describe('when entity exists on request object', () => {
- it('calls _updateEntity', done => {
- sinon.spy(verifier, '_updateEntity');
- const req = { 'user': { name: 'Admin' } };
- verifier.verify(req, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
- });
-
- describe('when entity exists on request.params object', () => {
- it('calls _updateEntity', done => {
- sinon.spy(verifier, '_updateEntity');
- const req = {
- params: {
- 'user': { name: 'Admin' }
- }
- };
- verifier.verify(req, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
- });
-
- it('calls _createEntity when entity not found', done => {
- sinon.spy(verifier, '_createEntity');
- verifier._normalizeResult = () => Promise.resolve(null);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._createEntity).to.have.been.calledOnce;
- verifier._createEntity.restore();
- done();
- });
- });
-
- it('calls _updateEntity when entity is found', done => {
- sinon.spy(verifier, '_updateEntity');
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, () => {
- expect(verifier._updateEntity).to.have.been.calledOnce;
- verifier._updateEntity.restore();
- done();
- });
- });
-
- it('returns the entity', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.deep.equal(user);
- done();
- });
- });
-
- it('handles false rejections in promise chain', done => {
- verifier._updateEntity = () => Promise.reject(false); // eslint-disable-line
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(null);
- expect(entity).to.equal(false);
- done();
- });
- });
-
- it('returns errors', done => {
- const authError = new Error('An error');
- verifier._normalizeResult = () => Promise.reject(authError);
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error).to.equal(authError);
- expect(entity).to.equal(undefined);
- done();
- });
- });
- });
-});
-
-describe('Verifier without service.id', function () {
- let service;
- let app;
- let options;
- let verifier;
- let user;
-
- beforeEach(() => {
- user = { id: 1, email: 'admin@feathersjs.com' };
- service = {
- find: sinon.stub().returns(Promise.resolve([user])),
- create: sinon.stub().returns(Promise.resolve(user)),
- patch: sinon.stub().returns(Promise.resolve(user))
- };
-
- app = expressify(feathers());
- app.use('users', service)
- .configure(authentication({ secret: 'supersecret' }));
-
- options = app.get('authentication');
- options.name = 'github';
- options.idField = 'githubId';
-
- verifier = new Verifier(app, options);
- });
-
- it('throws an error when service.id is not set', done => {
- verifier.verify({}, 'access', 'refresh', { id: 1234 }, (error, entity) => {
- expect(error.message.includes('the `id` property must be set')).to.equal(true);
- expect(entity).to.equal(undefined);
- done();
- });
- });
-});
diff --git a/packages/authentication/lib/core.js b/packages/authentication/lib/core.js
index 4d7fc0c544..26e39a1324 100644
--- a/packages/authentication/lib/core.js
+++ b/packages/authentication/lib/core.js
@@ -20,12 +20,12 @@ module.exports = class AuthenticationBase {
this.configKey = configKey;
app.set('defaultAuthentication', app.get('defaultAuthentication') || configKey);
- app.set(configKey, getOptions(options, app.get(configKey)));
+ app.set(configKey, merge({}, app.get(configKey), options));
}
get configuration () {
// Always returns a copy of the authentication configuration
- return merge({}, this.app.get(this.configKey));
+ return getOptions(this.app.get(this.configKey));
}
get strategyNames () {
@@ -46,6 +46,10 @@ module.exports = class AuthenticationBase {
strategy.setAuthentication(this);
}
+ if (typeof strategy.verifyConfiguration === 'function') {
+ strategy.verifyConfiguration();
+ }
+
// Register strategy as name
this.strategies[name] = strategy;
}
diff --git a/packages/authentication/lib/index.js b/packages/authentication/lib/index.js
index a060ab2f71..5c34cd5db3 100644
--- a/packages/authentication/lib/index.js
+++ b/packages/authentication/lib/index.js
@@ -1,10 +1,8 @@
const AuthenticationCore = require('./core');
const AuthenticationService = require('./service');
-const BaseStrategy = require('./strategy');
const JWTStrategy = require('./jwt');
const hooks = require('./hooks');
-exports.BaseStrategy = BaseStrategy;
exports.JWTStrategy = JWTStrategy;
exports.AuthenticationCore = AuthenticationCore;
exports.AuthenticationService = AuthenticationService;
diff --git a/packages/authentication/lib/jwt.js b/packages/authentication/lib/jwt.js
index 426b6e1931..d7d92aa5e0 100644
--- a/packages/authentication/lib/jwt.js
+++ b/packages/authentication/lib/jwt.js
@@ -1,10 +1,33 @@
const { NotAuthenticated } = require('@feathersjs/errors');
-const BaseStrategy = require('./strategy');
const SPLIT_HEADER = /(\S+)\s+(\S+)/;
-module.exports = class JWTStrategy extends BaseStrategy {
+module.exports = class JWTStrategy {
+ setAuthentication (auth) {
+ this.authentication = auth;
+ }
+
+ setApplication (app) {
+ this.app = app;
+ }
+
+ setName (name) {
+ this.name = name;
+ }
+
+ get configuration () {
+ const authConfig = this.authentication.configuration;
+ const config = authConfig[this.name];
+
+ return Object.assign({
+ entity: authConfig.entity,
+ service: authConfig.service,
+ header: 'Authorization',
+ schemes: [ 'Bearer', 'JWT' ]
+ }, config);
+ }
+
getEntity (id, params) {
- const { service } = this.authentication.configuration;
+ const { service } = this.configuration;
const entityService = this.app.service(service);
if (!entityService) {
@@ -18,7 +41,7 @@ module.exports = class JWTStrategy extends BaseStrategy {
authenticate (authentication, params) {
const { accessToken, strategy } = authentication;
- const { entity } = this.authentication.configuration;
+ const { entity } = this.configuration;
if (!accessToken || (strategy && strategy !== this.name)) {
return Promise.reject(new NotAuthenticated('Not authenticated'));
@@ -59,6 +82,10 @@ module.exports = class JWTStrategy extends BaseStrategy {
current => new RegExp(current, 'i').test(scheme)
);
+ if (scheme && !hasScheme) {
+ return null;
+ }
+
return Object.assign(result, {
accessToken: hasScheme ? schemeValue : headerValue
});
diff --git a/packages/authentication/lib/options.js b/packages/authentication/lib/options.js
index a3f6cad566..1c0b8afcc7 100644
--- a/packages/authentication/lib/options.js
+++ b/packages/authentication/lib/options.js
@@ -3,17 +3,13 @@ const { merge } = require('lodash');
const defaults = {
entity: 'user',
service: 'users',
- allowStrategies: [],
+ strategies: [],
jwtOptions: {
header: { typ: 'access' }, // by default is an access token but can be any type
audience: 'https://yourdomain.com', // The resource server where the token is processed
issuer: 'feathers', // The issuing server, application or resource
algorithm: 'HS256',
expiresIn: '1d'
- },
- jwt: {
- header: 'authorization',
- schemes: [ 'Bearer', 'JWT' ]
}
};
diff --git a/packages/authentication/lib/strategy.js b/packages/authentication/lib/strategy.js
deleted file mode 100644
index 56027a3495..0000000000
--- a/packages/authentication/lib/strategy.js
+++ /dev/null
@@ -1,17 +0,0 @@
-module.exports = class BaseStrategy {
- setAuthentication (auth) {
- this.authentication = auth;
- }
-
- setApplication (app) {
- this.app = app;
- }
-
- setName (name) {
- this.name = name;
- }
-
- get configuration () {
- return this.authentication.configuration[this.name];
- }
-};
diff --git a/packages/authentication/test/core.test.js b/packages/authentication/test/core.test.js
index d809916576..8e836396e2 100644
--- a/packages/authentication/test/core.test.js
+++ b/packages/authentication/test/core.test.js
@@ -41,15 +41,15 @@ describe('authentication/core', () => {
it('sets configKey and defaultAuthentication', () => {
assert.strictEqual(app.get('defaultAuthentication'), 'authentication');
- assert.deepStrictEqual(app.get('authentication'), auth.configuration);
});
it('uses default configKey', () => {
const otherApp = feathers();
const otherAuth = new AuthenticationCore(otherApp);
+ assert.ok(otherAuth);
assert.strictEqual(otherApp.get('defaultAuthentication'), 'authentication');
- assert.deepStrictEqual(otherApp.get('authentication'), otherAuth.configuration);
+ assert.deepStrictEqual(otherApp.get('authentication'), {});
});
});
diff --git a/packages/authentication/test/jwt.test.js b/packages/authentication/test/jwt.test.js
index 3d38fc6cc2..99bf0d8114 100644
--- a/packages/authentication/test/jwt.test.js
+++ b/packages/authentication/test/jwt.test.js
@@ -185,5 +185,15 @@ describe('authentication/jwt', () => {
});
});
});
+
+ it('return null when scheme does not match', () => {
+ return app.service('authentication').parse({
+ headers: {
+ authorization: ` Basic something`
+ }
+ }, {}, 'jwt').then(result => {
+ assert.strictEqual(result, null);
+ });
+ });
});
});