mirror of
https://github.com/hanzoai/passport-jwt.git
synced 2026-09-24 17:01:40 +00:00
Initial check in
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
var passport = require('passport-strategy')
|
||||
,util = require('util');
|
||||
|
||||
|
||||
/**
|
||||
* Strategy constructor
|
||||
*
|
||||
* @param secretOrKey - string or buffer containing the secret or PEM-encoded public key
|
||||
* @param options
|
||||
* issuer: If defined issuer will be verified against this value
|
||||
* audience: If defined audience will be verified against this value
|
||||
* tokenBodyField: Field in request body containing token. Default is auth_token.
|
||||
* tokenHeader: HTTP Header containing jwt. Default is 'x-auth_token'.
|
||||
* @param verify - Verify callback with args (jwt_payload, done_callback)
|
||||
*/
|
||||
function JwtStrategy(secretOrKey, options, verify) {
|
||||
if (typeof options == 'function') {
|
||||
verify = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!verify) {
|
||||
throw new TypeError('JwtStrategy requires a verify callback');
|
||||
}
|
||||
|
||||
if (!secretOrKey) {
|
||||
throw new TypeError('JwtStrategy requires a secret or key');
|
||||
}
|
||||
|
||||
passport.Strategy.call(this);
|
||||
this.name = 'jwt';
|
||||
this._verify = verify;
|
||||
this._passReqToCallback = options.passReqToCallback;
|
||||
this._tokenHeader = options.tokenHeader;
|
||||
this._tokenBodyField = options.tokenBodyField;
|
||||
this._verifOpts = {}
|
||||
|
||||
if (options.issuer) {
|
||||
this._verifOpts.issuer = options.issues;
|
||||
}
|
||||
|
||||
if (options.audience) {
|
||||
this._verifOpts.audience = options.audience;
|
||||
}
|
||||
|
||||
};
|
||||
util.inherits(JwtStrategy, passport.Strategy);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allow for injection of JWT Verifier.
|
||||
*
|
||||
* This improves testability by allowing tests to cleanly isolate failures in the JWT Verification
|
||||
* process from failures in the passport related mechanics of authentication.
|
||||
*
|
||||
* Note that this should only be replaced in tests.
|
||||
*/
|
||||
JwtStrategy.JwtVerifier = require('./verify_jwt');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Authenticate request based on JWT obtained from header or post body
|
||||
*/
|
||||
JwtStrategy.prototype.authenticate = function(req, options) {
|
||||
var self = this;
|
||||
|
||||
// Extract the jwt from the request
|
||||
// Try the header first
|
||||
var token = req.headers['x-auth_token'];
|
||||
// If not in the header try the body
|
||||
if (!token && req.body) {
|
||||
token = req.body.auth_token;
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
return self.fail(new Error("No auth token"));
|
||||
}
|
||||
|
||||
// Verify the JWT
|
||||
JwtStrategy.JwtVerifier(token, self._sercretOrKey, this._verifOpts, function(jwt_err, payload) {
|
||||
if (jwt_err) {
|
||||
return self.fail(jwt_err);
|
||||
} else {
|
||||
// Pass the parsed token to the user
|
||||
var verified = function(err, user, info) {
|
||||
if(err) {
|
||||
return self.error(err);
|
||||
} else if (!user) {
|
||||
return self.fail(info);
|
||||
} else {
|
||||
return self.success(user, info);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
self._verify(payload, verified);
|
||||
} catch(ex) {
|
||||
self.error(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Export the Jwt Strategy
|
||||
*/
|
||||
module.exports = JwtStrategy;
|
||||
@@ -0,0 +1,5 @@
|
||||
var jwt = require('jsonwebtoken');
|
||||
|
||||
module.exports = function(token, secretOrKey, options, callback) {
|
||||
return jwt.verify(token, secretOrKey, options, callback);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "passport-jwt",
|
||||
"version": "0.0.1",
|
||||
"description": "Passport authentication strategy using JSON Web Tokens",
|
||||
"main": "./lib",
|
||||
"scripts": {
|
||||
"test": "./node_modules/.bin/mocha --reporter spec --require test/bootstrap test/*test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/themikenicholson/passport-jwt.git"
|
||||
},
|
||||
"keywords": [
|
||||
"Passport",
|
||||
"Strategy",
|
||||
"JSON",
|
||||
"Web",
|
||||
"Token",
|
||||
"JWT"
|
||||
],
|
||||
"author": "Mike Nicholson",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/themikenicholson/passport-jwt/issues"
|
||||
},
|
||||
"homepage": "https://github.com/themikenicholson/passport-jwt",
|
||||
"devDependencies": {
|
||||
"chai": "^1.9.2",
|
||||
"chai-passport-strategy": "^0.2.0",
|
||||
"mocha": "^2.0.1",
|
||||
"sinon": "^1.11.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^1.1.2",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
var chai = require('chai');
|
||||
chai.use(require('chai-passport-strategy'));
|
||||
global.expect = chai.expect;
|
||||
@@ -0,0 +1,23 @@
|
||||
var Strategy = require('../lib/strategy');
|
||||
|
||||
describe('Strategy', function() {
|
||||
var strategy = new Strategy('secret', function() {});
|
||||
|
||||
it('should be named jwt', function() {
|
||||
expect(strategy.name).to.equal('jwt');
|
||||
});
|
||||
|
||||
|
||||
it('should throw if constructed without a verify callback', function() {
|
||||
expect(function() {
|
||||
var s = new Strategy('secret');
|
||||
}).to.throw(TypeError, "JwtStrategy requires a verify callback");
|
||||
});
|
||||
|
||||
|
||||
it('should throw if constructed without a secretOrKey arg', function() {
|
||||
expect(function() {
|
||||
var s = new Strategy(null, function() {});
|
||||
}).to.throw(TypeError, 'JwtStrategy requires a secret or key');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
var Strategy = require('../lib/strategy')
|
||||
, chai = require('chai')
|
||||
, sinon = require('sinon')
|
||||
, test_data= require('./testdata');
|
||||
|
||||
|
||||
describe('Strategy', function() {
|
||||
|
||||
var mockVerifier = null;
|
||||
|
||||
before(function() {
|
||||
// Replace the JWT Verfier with a stub to capture the value
|
||||
// extracted from the request
|
||||
mockVerifier = sinon.stub();
|
||||
mockVerifier.callsArgWith(3, null, test_data.valid_jwt.payload);
|
||||
Strategy.JwtVerifier = mockVerifier;
|
||||
});
|
||||
|
||||
describe('handling request with JWT in header', function() {
|
||||
var strategy;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
// Return values aren't important in this case
|
||||
return next(null, {}, {});
|
||||
});
|
||||
|
||||
mockVerifier.reset();
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.success(function(u, i) {
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
it("verifies the right jwt", function() {
|
||||
sinon.assert.calledOnce(mockVerifier);
|
||||
expect(mockVerifier.args[0][0]).to.equal(test_data.valid_jwt.token);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('handling request with JWT in body', function() {
|
||||
var strategy;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
// Return values aren't important in this case
|
||||
return next(null, {}, {});
|
||||
});
|
||||
|
||||
mockVerifier.reset();
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.success(function(u, i) {
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.body = {}
|
||||
req.body.auth_token = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it("verifies the right jwt", function() {
|
||||
sinon.assert.calledOnce(mockVerifier);
|
||||
expect(mockVerifier.args[0][0]).to.equal(test_data.valid_jwt.token);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('handling request with NO JWT', function() {
|
||||
|
||||
var info;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
// Return values aren't important in this case
|
||||
return next(null, {}, {});
|
||||
});
|
||||
|
||||
mockVerifier.reset();
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.fail(function(i) {
|
||||
info = i
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.body = {}
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it("should fail authentication", function() {
|
||||
expect(info).to.be.an.object;
|
||||
expect(info.message).to.equal("No auth token");
|
||||
});
|
||||
|
||||
|
||||
it('Should not try to verify anything', function() {
|
||||
sinon.assert.notCalled(mockVerifier);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
var Strategy = require('../lib/strategy')
|
||||
, chai = require('chai')
|
||||
, test_data = require('./testdata')
|
||||
, sinon = require('sinon');
|
||||
|
||||
describe('Strategy', function() {
|
||||
|
||||
|
||||
describe('handling valid jwt', function() {
|
||||
var strategy, payload;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
payload = jwt_payload;
|
||||
next(null, {}, {});
|
||||
});
|
||||
|
||||
// Mock successful verification
|
||||
Strategy.JwtVerifier = sinon.stub();
|
||||
Strategy.JwtVerifier.callsArgWith(3, null, test_data.valid_jwt.payload);
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.success(function(u, i) {
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should call verify with the correct payload', function() {
|
||||
expect(payload).to.deep.equal(test_data.valid_jwt.payload);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('handling failing jwt', function() {
|
||||
var strategy, info;
|
||||
var verify_spy = sinon.spy();
|
||||
|
||||
before(function(done) {
|
||||
|
||||
strategy = new Strategy('secret', verify_spy);
|
||||
|
||||
// Mock errored verification
|
||||
Strategy.JwtVerifier = sinon.stub();
|
||||
Strategy.JwtVerifier.callsArgWith(3, new Error("jwt expired"), false);
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.fail(function(i) {
|
||||
info = i;
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should not call verify', function() {
|
||||
sinon.assert.notCalled(verify_spy);
|
||||
});
|
||||
|
||||
|
||||
it('should fail with error message.', function() {
|
||||
expect(info).to.be.an.object;
|
||||
expect(info.message).to.equal('jwt expired');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
var chai = require('chai')
|
||||
, Strategy = require('../lib/strategy')
|
||||
, test_data = require('./testdata')
|
||||
, sinon = require('sinon');
|
||||
|
||||
|
||||
describe('Strategy', function() {
|
||||
|
||||
before(function() {
|
||||
Strategy.JwtVerifier = sinon.stub();
|
||||
Strategy.JwtVerifier.callsArgWith(3, null, test_data.valid_jwt.payload);
|
||||
});
|
||||
|
||||
describe('Handling a request with a valid JWT and succesful verification', function() {
|
||||
|
||||
var strategy, user, info;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_paylod, next) {
|
||||
return next(null, {user_id: 1234567890}, {foo:'bar'});
|
||||
});
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.success(function(u, i) {
|
||||
user = u;
|
||||
info = i;
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should provide a user', function() {
|
||||
expect(user).to.be.an.object;
|
||||
expect(user.user_id).to.equal(1234567890);
|
||||
});
|
||||
|
||||
|
||||
it('should forward info', function() {
|
||||
expect(info).to.be.an.object;
|
||||
expect(info.foo).to.equal('bar');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('handling a request with valid jwt and failed verification', function() {
|
||||
|
||||
var strategy, info;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
return next(null, false, {message: 'invalid user'});
|
||||
});
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.fail(function(i) {
|
||||
info = i;
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should fail with info', function() {
|
||||
expect(info).to.be.an.object;
|
||||
expect(info.message).to.equal('invalid user');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('handling a request with a valid jwt and an error during verification', function() {
|
||||
|
||||
var strategy, err;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secrety', function(jwt_payload, next) {
|
||||
return next(new Error("ERROR"), false, {message: 'invalid user'});
|
||||
});
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.error(function(e) {
|
||||
err = e;
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should error', function() {
|
||||
expect(err).to.be.an.instanceof(Error);
|
||||
expect(err.message).to.equal('ERROR');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('hanlding a request with a valid jwt and an exception during verification', function() {
|
||||
var strategy, err;
|
||||
|
||||
before(function(done) {
|
||||
strategy = new Strategy('secret', function(jwt_payload, next) {
|
||||
throw new Error("EXCEPTION");
|
||||
});
|
||||
|
||||
chai.passport.use(strategy)
|
||||
.error(function(e) {
|
||||
err = e;
|
||||
done();
|
||||
})
|
||||
.req(function(req) {
|
||||
req.headers['x-auth_token'] = test_data.valid_jwt.token;
|
||||
})
|
||||
.authenticate();
|
||||
});
|
||||
|
||||
|
||||
it('should error', function() {
|
||||
expect(err).to.be.an.instanceof(Error);
|
||||
expect(err.message).to.equal('EXCEPTION');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
// Note, this JWT will expire sometime in 2286. If unit tests are failing around this time try
|
||||
// generating a new, valid token :)
|
||||
valid_jwt : {
|
||||
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEyMzQ1Njc4OTAsIm5hbWUiOiJKb2huIERvZSIsImlzcyI6ImV4YW1wbGVzb2Z0LmNvbSIsImV4cCI6Ijk5OTk5OTk5OTkifQ.CbpI0TNI-FYXe6p3PgM__jwlz6aCT1qpUBsTVCfWuBM",
|
||||
payload : {
|
||||
"sub": "1234567890",
|
||||
"name": "John Doe",
|
||||
"iss": "examplesoft.com",
|
||||
"exp": "9999999999"
|
||||
},
|
||||
secret: 'secret'
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user