forked from auth0/node-jsonwebtoken
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 07d163b
Showing
10 changed files
with
434 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- 0.8 | ||
- 0.10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# node-jws [![Build Status](https://secure.travis-ci.org/auth0/node-jsonwebtoken.png)](http://travis-ci.org/auth0/node-jsonwebtoken) | ||
|
||
|
||
An implementation of [JSON Web Tokens](http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html). | ||
|
||
This was developed against `draft-ietf-oauth-json-web-token-08`. It makes use of [node-jws](https://github.com/brianloveswords/node-jws) | ||
|
||
# Install | ||
|
||
```bash | ||
$ npm install jsonwebtoken | ||
``` | ||
|
||
# Usage | ||
|
||
## jwt.sign(payload, secretOrPrivateKey, options) | ||
|
||
(Synchronous) Returns the JsonWebToken as string | ||
|
||
`payload` could be an literal, buffer or string | ||
|
||
`secretOrPrivateKey` is a string or buffer containing either the secret for HMAC algorithms, or the PEM | ||
encoded private key for RSA and ECDSA. | ||
|
||
`options`: | ||
|
||
* `algorithm` (default: `HS256`) | ||
* `expiresInMinutes` | ||
* `audience` | ||
* `subject` | ||
* `issuer` | ||
|
||
If `payload` is not a buffer or a string, it will be coerced into a string | ||
using `JSON.stringify`. | ||
|
||
If any `expiresInMinutes`, `audience`, `subject`, `issuer` are not provided, there is no default. The jwt generated won't include those properties in the payload. | ||
|
||
Example | ||
|
||
```js | ||
// sign with default (HMAC SHA256) | ||
var token = jwt.sign({ foo: 'bar' }, 'shhhhh'); | ||
|
||
// sign with RSA SHA256 | ||
var cert = fs.readFileSync('private.key'); // get private key | ||
var token = jwt.sign({ foo: 'bar' }, cert, { algorithm: 'RS256'}); | ||
``` | ||
|
||
## jwt.verify(token, secretOrPublicKey, options, callback) | ||
|
||
(Synchronous with callback) Returns the payload decoded if the signature (and optionally expiration, audience, issuer) are valid. If not, it will return the error. | ||
|
||
`token` is the JsonWebToken string | ||
|
||
`secretOrPublicKey` is a string or buffer containing either the secret for HMAC algorithms, or the PEM | ||
encoded public key for RSA and ECDSA. | ||
|
||
`options` | ||
|
||
* `audience`: if you want to check audience (`aud`), provide a value here | ||
* `issuer`: if you want to check issuer (`iss`), provide a value here | ||
|
||
```js | ||
// verify a token symmetric | ||
jwt.verify(token, 'shhhhh', function(err, decoded) { | ||
console.log(decoded.foo) // bar | ||
}); | ||
|
||
// invalid token | ||
jwt.verify(token, 'wrong-secret', function(err, decoded) { | ||
// err | ||
// decoded undefined | ||
}); | ||
|
||
// verify a token asymmetric | ||
var cert = fs.readFileSync('public.pem'); // get public key | ||
jwt.verify(token, cert, function(err, decoded) { | ||
console.log(decoded.foo) // bar | ||
}); | ||
|
||
// verify audience | ||
var cert = fs.readFileSync('public.pem'); // get public key | ||
jwt.verify(token, cert, { audience: 'urn:foo' }, function(err, decoded) { | ||
// if audience mismatch, err == invalid audience | ||
}); | ||
|
||
// verify issuer | ||
var cert = fs.readFileSync('public.pem'); // get public key | ||
jwt.verify(token, cert, { audience: 'urn:foo', issuer: 'urn:issuer' }, function(err, decoded) { | ||
// if issuer mismatch, err == invalid issuer | ||
}); | ||
|
||
``` | ||
|
||
## Algorithms supported | ||
|
||
Array of supported algorithms. The following algorithms are currently supported. | ||
|
||
alg Parameter Value | Digital Signature or MAC Algorithm | ||
----------------|---------------------------- | ||
HS256 | HMAC using SHA-256 hash algorithm | ||
HS384 | HMAC using SHA-384 hash algorithm | ||
HS512 | HMAC using SHA-512 hash algorithm | ||
RS256 | RSASSA using SHA-256 hash algorithm | ||
RS384 | RSASSA using SHA-384 hash algorithm | ||
RS512 | RSASSA using SHA-512 hash algorithm | ||
ES256 | ECDSA using P-256 curve and SHA-256 hash algorithm | ||
ES384 | ECDSA using P-384 curve and SHA-384 hash algorithm | ||
ES512 | ECDSA using P-521 curve and SHA-512 hash algorithm | ||
none | No digital signature or MAC value included | ||
|
||
|
||
|
||
# TODO | ||
|
||
* X.509 certificate chain is not checked | ||
|
||
# License | ||
|
||
MIT | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
var jws = require('jws'); | ||
var moment = require('moment'); | ||
|
||
module.exports.sign = function(payload, secretOrPrivateKey, options) { | ||
options = options || {}; | ||
|
||
var header = {typ: 'JWT', alg: options.algorithm || 'HS256'}; | ||
if (options.expiresInMinutes) | ||
payload.exp = moment().add('minutes', options.expiresInMinutes).utc().unix(); | ||
|
||
if (options.audience) | ||
payload.aud = options.audience; | ||
|
||
if (options.issuer) | ||
payload.iss = options.issuer; | ||
|
||
if (options.subject) | ||
payload.sub = options.subject; | ||
|
||
payload.iat = moment().utc().unix(); | ||
|
||
var signed = jws.sign({header: header, payload: payload, secret: secretOrPrivateKey}); | ||
|
||
return signed; | ||
}; | ||
|
||
module.exports.verify = function(jwtString, secretOrPublicKey, options, callback) { | ||
if ((typeof options === 'function') && !callback) callback = options; | ||
if (!options) options = {}; | ||
|
||
var valid; | ||
try { | ||
valid = jws.verify(jwtString, secretOrPublicKey); | ||
} | ||
catch (e) { | ||
return callback(e); | ||
} | ||
|
||
if (!valid) | ||
return callback(new Error('invalid signature')); | ||
|
||
var jwt = jws.decode(jwtString); | ||
|
||
if (jwt.payload.exp) { | ||
if (moment().utc().unix() >= jwt.payload.exp) | ||
return callback(new Error('jwt expired')); | ||
} | ||
|
||
if (jwt.payload.aud && options.audience) { | ||
if (jwt.payload.aud !== options.audience) | ||
return callback(new Error('jwt audience invalid. expected: ' + jwt.payload.aud)); | ||
} | ||
|
||
if (jwt.payload.iss && options.issuer) { | ||
if (jwt.payload.iss !== options.issuer) | ||
return callback(new Error('jwt issuer invalid. expected: ' + jwt.payload.iss)); | ||
} | ||
|
||
callback(null, jwt.payload); | ||
}; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "node-jsonwebtoken", | ||
"version": "0.1.0", | ||
"description": "JSON Web Token implementation (symmetric and asymmetric)", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/auth0/node-jsonwebtoken" | ||
}, | ||
"keywords": [ | ||
"jwt" | ||
], | ||
"author": "auth0", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/auth0/node-jsonwebtoken/issues" | ||
}, | ||
"dependencies": { | ||
"jws": "~0.2.2" | ||
}, | ||
"devDependencies": { | ||
"chai": "*" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
-----BEGIN CERTIFICATE----- | ||
MIIDJjCCAg6gAwIBAgIJAMyz3mSPlaW4MA0GCSqGSIb3DQEBBQUAMBYxFDASBgNV | ||
BAMUCyouYXV0aDAuY29tMB4XDTEzMDQxODE3MDE1MFoXDTI2MTIyNjE3MDE1MFow | ||
FjEUMBIGA1UEAxQLKi5hdXRoMC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw | ||
ggEKAoIBAQDZq1Ua0/BGm+TaBFoftKWeYMWrQG9Fx3g7ikErxljmyOvlwqkiat3q | ||
ixX+Dxw9TFb5gbBjNJ+L3nt4YefJgLsYvsHqkOUxWsB+HM/ulJRVnVrZm1tI3Nbg | ||
xO1BQ7DrGfBpq2KCxtQCaQFRlQJw1+qS5LwrdIvihB7Kc142VElCFFHJ6+09eMUy | ||
jy00Z5pfQr4Am6W6eEOS9ObDbNs4XgKOcWe5khWXj3UStou+VgbAg40XcYht2IbY | ||
gMfKF+VUZOy3+e+aRTqPOBU3MAeb0tvCCPUQJbNAUHgSKVhAvNf8mRwttVsOLT70 | ||
anjjeCOd7RKS8fVKBwc2KtgNkghYdPY9AgMBAAGjdzB1MB0GA1UdDgQWBBSi4+X0 | ||
+MvCKDdd375mDhx/ZBbJ4DBGBgNVHSMEPzA9gBSi4+X0+MvCKDdd375mDhx/ZBbJ | ||
4KEapBgwFjEUMBIGA1UEAxQLKi5hdXRoMC5jb22CCQDMs95kj5WluDAMBgNVHRME | ||
BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQBi0qPe0DzlPSufq+Gdk2Fwf1pGEtjA | ||
D34IxxJ9SX6r1DS/NIP7IOLUnNU8cP8BQWl7i413v29jJsNV457pjdmqf8J7OE9O | ||
eF5Yz1x91gY/27561Iga/TQeIVOlFQAgx66eLfUFFoAig3hz2srZo5TzYBixMJsS | ||
fYMXHPiU7KoLUqYXvpSXIllstQCu51KCC6t9H7wZ92lTES1v76hFY4edQ30sftPo | ||
kjAYWGEhMjPo/r4THcdSMqKXoRtCGEun4pTXid7MJcTgdGDrAJddLWi6SxKecEVB | ||
MhMu4XfUCdxCwqQPjHeJ+zE49A1CUdBB2FN3BNLbmTTwEBgmuwyGRzhj | ||
-----END CERTIFICATE----- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
var jwt = require('../index'); | ||
|
||
var expect = require('chai').expect; | ||
var assert = require('chai').assert; | ||
|
||
describe('HS256', function() { | ||
|
||
describe('when signing a token', function() { | ||
var secret = 'shhhhhh'; | ||
|
||
var token = jwt.sign({ foo: 'bar' }, secret, { algorithm: 'HS256' }); | ||
|
||
it('should be syntactically valid', function() { | ||
expect(token).to.be.a('string'); | ||
expect(token.split('.')).to.have.length(3); | ||
}); | ||
|
||
it('should validate with secret', function(done) { | ||
jwt.verify(token, secret, function(err, decoded) { | ||
assert.ok(decoded.foo); | ||
assert.equal('bar', decoded.foo); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should throw with invalid secret', function(done) { | ||
jwt.verify(token, 'invalid secret', function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
done(); | ||
}); | ||
}); | ||
|
||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
var jwt = require('../index'); | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
|
||
var expect = require('chai').expect; | ||
var assert = require('chai').assert; | ||
|
||
describe('RS256', function() { | ||
var pub = fs.readFileSync(path.join(__dirname, 'pub.pem')); | ||
var priv = fs.readFileSync(path.join(__dirname, 'priv.pem')); | ||
var invalid_pub = fs.readFileSync(path.join(__dirname, 'invalid_pub.pem')); | ||
|
||
describe('when signing a token', function() { | ||
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256' }); | ||
|
||
it('should be syntactically valid', function() { | ||
expect(token).to.be.a('string'); | ||
expect(token.split('.')).to.have.length(3); | ||
}); | ||
|
||
it('should validate with public key', function(done) { | ||
jwt.verify(token, pub, function(err, decoded) { | ||
assert.ok(decoded.foo); | ||
assert.equal('bar', decoded.foo); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should throw with invalid public key', function(done) { | ||
jwt.verify(token, invalid_pub, function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
done(); | ||
}); | ||
|
||
}); | ||
|
||
}); | ||
|
||
|
||
describe('when signing a token with expiration', function() { | ||
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', expiresInMinutes: 10 }); | ||
|
||
it('should be valid expiration', function(done) { | ||
jwt.verify(token, pub, function(err, decoded) { | ||
assert.isNotNull(decoded); | ||
assert.isNull(err); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should be invalid', function(done) { | ||
// expired token | ||
token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', expiresInMinutes: -10 }); | ||
|
||
jwt.verify(token, pub, function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
done(); | ||
}); | ||
}); | ||
|
||
}); | ||
|
||
describe('when signing a token with audience', function() { | ||
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', audience: 'urn:foo' }); | ||
|
||
it('should check audience', function(done) { | ||
jwt.verify(token, pub, function(err, decoded) { | ||
assert.isNotNull(decoded); | ||
assert.isNull(err); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should throw when invalid audience', function(done) { | ||
jwt.verify(token, pub, { audience: 'urn:wrong' }, function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
done(); | ||
}); | ||
}); | ||
|
||
}); | ||
|
||
describe('when signing a token with issuer', function() { | ||
var token = jwt.sign({ foo: 'bar' }, priv, { algorithm: 'RS256', issuer: 'urn:foo' }); | ||
|
||
it('should check issuer', function() { | ||
jwt.verify(token, pub, { issuer: 'urn:foo' }, function(err, decoded) { | ||
assert.isNotNull(decoded); | ||
assert.isNull(err); | ||
}); | ||
}); | ||
|
||
it('should throw when invalid issuer', function() { | ||
jwt.verify(token, pub, { issuer: 'urn:wrong' }, function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('when verifying a malformed token', function() { | ||
it('should throw', function(done) { | ||
jwt.verify('fruit.fruit.fruit', pub, function(err, decoded) { | ||
assert.isUndefined(decoded); | ||
assert.isNotNull(err); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
|
||
|
||
}); |
Oops, something went wrong.