From 1ab6ab329d6fd2d3b464c392a0576630ccc2f21e Mon Sep 17 00:00:00 2001
From: Rowan Wookey <admin@rwky.net>
Date: Wed, 11 Dec 2019 13:42:25 +0000
Subject: [PATCH] Feature: Pass instantiated strategy to authenticate.

Merged from upstream https://github.com/jaredhanson/passport/pull/749/files
---
 lib/middleware/authenticate.js        | 12 +++++---
 test/authenticator.middleware.test.js | 44 +++++++++++++++++++++++++++
 2 files changed, 52 insertions(+), 4 deletions(-)

diff --git a/lib/middleware/authenticate.js b/lib/middleware/authenticate.js
index 5ceb5c8b..964c6625 100644
--- a/lib/middleware/authenticate.js
+++ b/lib/middleware/authenticate.js
@@ -177,6 +177,7 @@ module.exports = function authenticate(passport, name, options, callback) {
 
     // eslint-disable-next-line consistent-return
     (function attempt(i) {
+      let strategy;
       const layer = name[i];
       // If no more strategies exist in the chain, authentication has failed.
       if (!layer) { return allFailed(); }
@@ -184,11 +185,14 @@ module.exports = function authenticate(passport, name, options, callback) {
       // Get the strategy, which will be used as prototype from which to create
       // a new instance.  Action functions will then be bound to the strategy
       // within the context of the HTTP request/response pair.
-      const prototype = passport._strategy(layer);
-      if (!prototype) { return next(new Error(`Unknown authentication strategy "${layer}"`)); }
-
-      const strategy = Object.create(prototype);
+      if (typeof layer.authenticate === 'function') {
+        strategy = layer;
+      } else {
+        const prototype = passport._strategy(layer);
+        if (!prototype) { return next(new Error(`Unknown authentication strategy "${layer}"`)); }
 
+        strategy = Object.create(prototype);
+      }
 
       // ----- BEGIN STRATEGY AUGMENTATION -----
       // Augment the new strategy instance with action functions.  These action
diff --git a/test/authenticator.middleware.test.js b/test/authenticator.middleware.test.js
index 519ca5db..12247a43 100644
--- a/test/authenticator.middleware.test.js
+++ b/test/authenticator.middleware.test.js
@@ -151,6 +151,50 @@ describe('Authenticator', () => {
         expect(request.user.username).to.equal('jaredhanson');
       });
 
+      it('should set authInfo', () => {
+        expect(request.authInfo).to.be.an('object');
+        expect(Object.keys(request.authInfo)).to.have.length(0);
+      });
+    });
+    describe('handling a request with instantiated strategy', () => {
+      function Strategy() {
+      }
+      Strategy.prototype.authenticate = function authenticate() {
+        const user = { id: '1', username: 'jaredhanson' };
+        this.success(user);
+      };
+
+      const passport = new Authenticator();
+
+      let request, error;
+
+      before((done) => {
+        chai.connect.use(passport.authenticate(new Strategy())).req((req) => {
+          request = req;
+
+          req.logIn = function logIn(user, options, done) {
+            this.user = user;
+            done();
+          };
+        })
+          .next((err) => {
+            error = err;
+            done();
+          })
+          .dispatch();
+      });
+
+      it('should not error', () => {
+        // eslint-disable-next-line no-unused-expressions
+        expect(error).to.be.undefined;
+      });
+
+      it('should set user', () => {
+        expect(request.user).to.be.an('object');
+        expect(request.user.id).to.equal('1');
+        expect(request.user.username).to.equal('jaredhanson');
+      });
+
       it('should set authInfo', () => {
         expect(request.authInfo).to.be.an('object');
         expect(Object.keys(request.authInfo)).to.have.length(0);