CVE-2021-41580 (published 2021-09-27T07:15Z)

The passport-oauth2 package before 1.6.1 for Node.js mishandles the error condition of failure to obtain an access token. This is exploitable in certain use cases where an OAuth identity provider uses an HTTP 200 status code for authentication-failure error reports, and an application grants authorization upon simply receiving the access token (i.e., does not try to use the token). NOTE: the passport-oauth2 vendor does not consider this a passport-oauth2 vulnerability

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/jaredhanson/passport-oauth2
commit_details: repo_url: https://github.com/jaredhanson/passport-oauth2
author: Jared Hanson
author_date: 2021-09-24 14:26:10-07:00
msg: Add test case and avoid parsing internal OAuth error when it doesn't exist.
repository: repo_url: https://github.com/jaredhanson/passport-oauth2
repo_name: jaredhanson/passport-oauth2
description: OAuth 2.0 authentication strategy for Passport and Node.js.
repo_language: JavaScript
owner: jaredhanson
file_changes: filename: strategy.js
change_type: ModificationType.MODIFY
diff: @@ -174,7 +174,8 @@ OAuth2Strategy.prototype.authenticate = function(req, options) {
 
       self._oauth2.getOAuthAccessToken(code, params,
         function(err, accessToken, refreshToken, params) {
-          if (err || !accessToken) { return self.error(self._createOAuthError('Failed to obtain access token', err)); }
+          if (err) { return self.error(self._createOAuthError('Failed to obtain access token', err)); }
+          if (!accessToken) { return self.error(new Error('Failed to obtain access token')); }
 
           self._loadUserProfile(accessToken, function(err, profile) {
             if (err) { return self.error(err); }

programming_language: JavaScript
filename: oauth2.test.js
change_type: ModificationType.MODIFY
diff: @@ -1256,6 +1256,45 @@ describe('OAuth2Strategy', function() {
       });
     }); // that errors due to token request error, in node-oauth object literal form with text body
     
+    describe('that errors due to not receiving an access token', function() {
+      var strategy = new OAuth2Strategy({
+        authorizationURL: 'https://www.example.com/oauth2/authorize',
+        tokenURL: 'https://www.example.com/oauth2/token',
+        clientID: 'ABC123',
+        clientSecret: 'secret',
+        callbackURL: 'https://www.example.net/auth/example/callback',
+      },
+      function(accessToken, refreshToken, params, profile, done) {
+        return done(new Error('something went wrong'));
+      });
+  
+      strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
+        return callback(null, undefined, undefined, undefined);
+      }
+  
+  
+      var err;
+  
+      before(function(done) {
+        chai.passport.use(strategy)
+          .error(function(e) {
+            err = e;
+            done();
+          })
+          .req(function(req) {
+            req.query = {};
+            req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
+          })
+          .authenticate();
+      });
+
+      it('should error', function() {
+        expect(err).to.be.an.instanceof(Error);
+        expect(err).to.not.be.an.instanceof(InternalOAuthError)
+        expect(err.message).to.equal('Failed to obtain access token');
+      });
+    }); // that errors due to not receiving an access token
+    
     describe('that errors due to verify callback supplying error', function() {
       var strategy = new OAuth2Strategy({
         authorizationURL: 'https://www.example.com/oauth2/authorize',

programming_language: JavaScript