forked from hapijs/jwt
-
Notifications
You must be signed in to change notification settings - Fork 0
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
1 parent
28d6b5d
commit 1ee06c7
Showing
18 changed files
with
2,740 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,13 @@ | ||
**/node_modules | ||
**/package-lock.json | ||
|
||
coverage.* | ||
|
||
**/.DS_Store | ||
**/._* | ||
|
||
**/*.pem | ||
|
||
**/.vs | ||
**/.vscode | ||
**/.idea |
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,22 @@ | ||
language: node_js | ||
|
||
node_js: | ||
- "8" | ||
- "10" | ||
- "12" | ||
- "node" | ||
|
||
sudo: false | ||
|
||
install: | ||
- "npm install" | ||
- "npm install hapi@$HAPI_VERSION" | ||
|
||
env: | ||
- HAPI_VERSION="17" | ||
- HAPI_VERSION="18" | ||
|
||
os: | ||
- "linux" | ||
- "osx" | ||
- "windows" |
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,3 @@ | ||
Breaking changes are documented using GitHub issues, see [issues labeled "release notes"](https://github.com/hapijs/jwt/issues?q=is%3Aissue+label%3A%22release+notes%22). | ||
|
||
If you want changes of a specific minor or patch release, you can browse the [GitHub milestones](https://github.com/hapijs/jwt/milestones?state=closed&direction=asc&sort=due_date). |
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,5 @@ | ||
COMMERCIAL LICENSE | ||
|
||
Copyright (c) 2019 Sideway Inc. | ||
|
||
This package requires a commercial license. You may not use, copy, or distribute it without first acquiring a commercial license from Sideway Inc. Using this software without a license is a violation of US and international law. To obtain a license, please contact [[email protected]](mailto:[email protected]). |
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 |
---|---|---|
|
@@ -5,3 +5,22 @@ | |
JWT (JSON Web Token) Authentication. | ||
|
||
[](https://travis-ci.org/hapijs/jwt) | ||
|
||
## License | ||
|
||
This package requires a commercial license. You may not use, copy, or distribute it without first | ||
acquiring a commercial license from Sideway Inc. Using this software without a license is a | ||
violation of US and international law. To obtain a license, please contact [[email protected]](mailto:[email protected]). | ||
|
||
## Acknowledgements | ||
|
||
Portions of this module were adapted from: | ||
|
||
- [node-jwa](https://github.com/brianloveswords/node-jwa), copyright (c) 2013 Brian J. Brennan, MIT License | ||
- [node-jws](https://github.com/brianloveswords/node-jws), copyright (c) 2013 Brian J. Brennan, MIT License | ||
- [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken), copyright (c) 2015 Auth0, Inc., MIT License | ||
- [node-jwks-rsa](https://github.com/auth0/node-jwks-rsa), copyright (c) 2016 Sandrino Di Mattia, MIT License | ||
- [hapi-auth-jwt2](https://github.com/dwyl/hapi-auth-jwt2), copyright (c) 2015-2016, dwyl ltd, ISC License | ||
- [mock-jwks](https://github.com/Levino/mock-jwks), copyright (c) 2018-2019 Levin Keller, MIT License | ||
- [Stack Overflow answer](http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js) | ||
- [node-rsa-pem-from-mod-exp](https://github.com/tracker1/node-rsa-pem-from-mod-exp), copyright (c) 2014 Michael J. Ryan, MIT License |
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,218 @@ | ||
'use strict'; | ||
|
||
const Crypto = require('crypto'); | ||
|
||
const EcdsaSigFormatter = require('ecdsa-sig-formatter'); | ||
|
||
const Utils = require('./utils'); | ||
|
||
|
||
const internals = { | ||
algorithms: { | ||
RS256: 'RSA-SHA256', | ||
RS384: 'RSA-SHA384', | ||
RS512: 'RSA-SHA512', | ||
|
||
PS256: 'RSA-SHA256', | ||
PS384: 'RSA-SHA384', | ||
PS512: 'RSA-SHA512', | ||
|
||
ES256: 'RSA-SHA256', | ||
ES384: 'RSA-SHA384', | ||
ES512: 'RSA-SHA512', | ||
|
||
HS256: 'sha256', | ||
HS384: 'sha384', | ||
HS512: 'sha512' | ||
} | ||
}; | ||
|
||
|
||
exports.generate = function (value, algorithm, key) { | ||
|
||
algorithm = algorithm.toUpperCase(); | ||
|
||
if (algorithm === 'NONE') { | ||
return ''; | ||
} | ||
|
||
const algo = internals.algorithms[algorithm]; | ||
if (!algo) { | ||
throw new Error('Unsupported algorithm'); | ||
} | ||
|
||
switch (algorithm) { | ||
case 'RS256': | ||
case 'RS384': | ||
case 'RS512': { | ||
|
||
const signer = Crypto.createSign(algo); | ||
signer.update(value); | ||
const sig = signer.sign(key, 'base64'); | ||
return internals.b64urlEncode(sig); | ||
} | ||
|
||
case 'PS256': | ||
case 'PS384': | ||
case 'PS512': { | ||
|
||
const signer = Crypto.createSign(algo); | ||
signer.update(value); | ||
const sig = signer.sign({ key, padding: Crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: Crypto.constants.RSA_PSS_SALTLEN_DIGEST }, 'base64'); | ||
return internals.b64urlEncode(sig); | ||
} | ||
|
||
case 'ES256': | ||
case 'ES384': | ||
case 'ES512': { | ||
|
||
const signer = Crypto.createSign(algo); | ||
signer.update(value); | ||
const sig = signer.sign(key, 'base64'); | ||
return EcdsaSigFormatter.derToJose(sig, algorithm); | ||
} | ||
|
||
case 'HS256': | ||
case 'HS384': | ||
case 'HS512': { | ||
|
||
const hmac = Crypto.createHmac(algo, key); | ||
hmac.update(value); | ||
const digest = hmac.digest('base64'); | ||
return internals.b64urlEncode(digest); | ||
} | ||
} | ||
}; | ||
|
||
|
||
exports.verify = function (raw, algorithm, key) { | ||
|
||
algorithm = algorithm.toUpperCase(); | ||
|
||
if (algorithm === 'NONE') { | ||
return raw.signature === ''; | ||
} | ||
|
||
const algo = internals.algorithms[algorithm]; | ||
if (!algo) { | ||
throw new Error('Unsupported algorithm'); | ||
} | ||
|
||
const value = `${raw.header}.${raw.payload}`; | ||
const signature = raw.signature; | ||
|
||
switch (algorithm) { | ||
case 'RS256': | ||
case 'RS384': | ||
case 'RS512': { | ||
|
||
const verifier = Crypto.createVerify(algo); | ||
verifier.update(value); | ||
return verifier.verify(key, internals.b64urlDecode(signature), 'base64'); | ||
} | ||
|
||
case 'PS256': | ||
case 'PS384': | ||
case 'PS512': { | ||
|
||
const verifier = Crypto.createVerify(algo); | ||
verifier.update(value); | ||
return verifier.verify({ key, padding: Crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: Crypto.constants.RSA_PSS_SALTLEN_DIGEST }, internals.b64urlDecode(signature), 'base64'); | ||
} | ||
|
||
case 'ES256': | ||
case 'ES384': | ||
case 'ES512': { | ||
|
||
const sig = EcdsaSigFormatter.joseToDer(signature, algorithm).toString('base64'); | ||
const verifier = Crypto.createVerify(algo); | ||
verifier.update(value); | ||
return verifier.verify(key, internals.b64urlDecode(sig), 'base64'); | ||
} | ||
|
||
case 'HS256': | ||
case 'HS384': | ||
case 'HS512': { | ||
|
||
const compare = exports.generate(value, algorithm, key); | ||
return Crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(compare)); | ||
} | ||
} | ||
}; | ||
|
||
|
||
internals.b64urlEncode = function (b64) { | ||
|
||
return b64.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); | ||
}; | ||
|
||
|
||
internals.b64urlDecode = function (b64url) { | ||
|
||
b64url = b64url.toString(); | ||
|
||
const padding = 4 - b64url.length % 4; | ||
if (padding !== 4) { | ||
for (let i = 0; i < padding; ++i) { | ||
b64url += '='; | ||
} | ||
} | ||
|
||
return b64url.replace(/\-/g, '+').replace(/_/g, '/'); | ||
}; | ||
|
||
|
||
exports.certToPEM = function (cert) { | ||
|
||
return `-----BEGIN CERTIFICATE-----\n${internals.chop(cert)}\n-----END CERTIFICATE-----\n`; | ||
}; | ||
|
||
|
||
exports.rsaPublicKeyToPEM = function (modulusB64, exponentB64) { | ||
|
||
const modulusHex = internals.prepadSigned(Buffer.from(modulusB64, 'base64').toString('hex')); | ||
const exponentHex = internals.prepadSigned(Buffer.from(exponentB64, 'base64').toString('hex')); | ||
|
||
const modlen = modulusHex.length / 2; | ||
const explen = exponentHex.length / 2; | ||
|
||
const encodedModlen = internals.encodeLengthHex(modlen); | ||
const encodedExplen = internals.encodeLengthHex(explen); | ||
|
||
const encodedPubkey = '30' + | ||
internals.encodeLengthHex(modlen + explen + encodedModlen.length / 2 + encodedExplen.length / 2 + 2) + | ||
'02' + encodedModlen + modulusHex + | ||
'02' + encodedExplen + exponentHex; | ||
|
||
const der = internals.chop(Buffer.from(encodedPubkey, 'hex').toString('base64')); | ||
return `-----BEGIN RSA PUBLIC KEY-----\n${der}\n-----END RSA PUBLIC KEY-----\n`; | ||
}; | ||
|
||
|
||
internals.prepadSigned = function (hexStr) { | ||
|
||
const msb = hexStr[0]; | ||
if (msb > '7') { | ||
return `00${hexStr}`; | ||
} | ||
|
||
return hexStr; | ||
}; | ||
|
||
|
||
internals.encodeLengthHex = function (n) { | ||
|
||
if (n <= 127) { | ||
return Utils.toHex(n); | ||
} | ||
|
||
const nHex = Utils.toHex(n); | ||
const lengthOfLengthByte = 128 + nHex.length / 2; | ||
return Utils.toHex(lengthOfLengthByte) + nHex; | ||
}; | ||
|
||
|
||
internals.chop = function (cert) { | ||
|
||
return cert.match(/.{1,64}/g).join('\n'); | ||
}; |
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,32 @@ | ||
'use strict'; | ||
|
||
const Crypto = require('./crypto'); | ||
const Plugin = require('./plugin'); | ||
const Token = require('./token'); | ||
const Utils = require('./utils'); | ||
|
||
|
||
const internals = {}; | ||
|
||
|
||
module.exports = { | ||
plugin: Plugin.plugin, | ||
|
||
token: { | ||
generate: Token.generate, | ||
decode: Token.decode, | ||
verify: Token.verify, | ||
verifyTime: Token.verifyTime, | ||
|
||
signature: { | ||
generate: Crypto.generate, | ||
verify: Crypto.verify | ||
} | ||
}, | ||
|
||
crypto: { | ||
rsaPublicKeyToPEM: Crypto.rsaPublicKeyToPEM | ||
}, | ||
|
||
utils: Utils | ||
}; |
Oops, something went wrong.