atlassian-jwt
Advanced tools
Comparing version 1.0.3 to 2.0.0
"use strict"; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./lib/jwt")); | ||
__exportStar(require("./lib/jwt"), exports); |
import { Request as ExpressRequest } from 'express'; | ||
export declare enum Algorithm { | ||
export declare enum AsymmetricAlgorithm { | ||
RS256 = "RS256" | ||
} | ||
export declare enum SymmetricAlgorithm { | ||
HS256 = "HS256", | ||
@@ -7,2 +10,6 @@ HS384 = "HS384", | ||
} | ||
declare type Algorithm = AsymmetricAlgorithm | SymmetricAlgorithm; | ||
declare type JwtHeader = { | ||
kid: string; | ||
}; | ||
export declare function fromExpressRequest(eReq: ExpressRequest): Request; | ||
@@ -38,9 +45,10 @@ export declare function fromMethodAndUrl(method: string, rawUrl: string): Request; | ||
}; | ||
export declare const version = "1.0.3"; | ||
export declare const version = "2.0.0"; | ||
/** | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be HS256, HS384, or HS512. | ||
* The encoding algorithm must be RS256. | ||
* | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param signedAlgorithm should match the header.alg | ||
* @param noVerify optional, set to true to skip the result verification | ||
@@ -52,5 +60,21 @@ * | ||
*/ | ||
export declare const decode: (token: string, key: string, noVerify?: boolean | undefined) => any; | ||
export declare const decodeAsymmetric: (token: string, key: string, signedAlgorithm: Algorithm, noVerify?: boolean | undefined) => any; | ||
export declare function getKeyId(token: string): any; | ||
/** | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be symmetric (HS256, HS384, or HS512). | ||
* | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param signedAlgorithm should match the header.alg | ||
* @param noVerify optional, set to true to skip the result verification | ||
* | ||
* @return Decoded JWT object | ||
* | ||
* @api public | ||
*/ | ||
export declare const decodeSymmetric: (token: string, key: string, signedAlgorithm: Algorithm, noVerify?: boolean | undefined) => any; | ||
/** | ||
* Encodes JWT object to string. | ||
* The encoding algorithm must be symmetric (HS256, HS384, or HS512). | ||
* | ||
@@ -65,5 +89,16 @@ * @param payload Payload object to encode | ||
*/ | ||
export declare const encode: (payload: object, key: string, algorithm?: Algorithm | undefined) => string; | ||
export declare const encodeSymmetric: (payload: object, key: string, algorithm?: SymmetricAlgorithm | undefined) => string; | ||
/** | ||
* Encodes JWT object to string. | ||
* The encoding algorithm must be RS256 | ||
* | ||
* @param payload Payload object to encode | ||
* @param key Key used to encode | ||
* @param algorithm supports only RS256 | ||
* @returns | ||
*/ | ||
export declare const encodeAsymmetric: (payload: object, key: string, algorithm: AsymmetricAlgorithm, header?: JwtHeader | undefined) => string; | ||
export declare function createCanonicalRequest(req: Request, checkBodyForParams?: boolean, baseUrl?: string): string; | ||
export declare function createQueryStringHash(req: Request, checkBodyForParams?: boolean, baseUrl?: string): string; | ||
export {}; | ||
//# sourceMappingURL=jwt.d.ts.map |
@@ -14,13 +14,37 @@ "use strict"; | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.createQueryStringHash = exports.createCanonicalRequest = exports.encodeAsymmetric = exports.encodeSymmetric = exports.decodeSymmetric = exports.getKeyId = exports.decodeAsymmetric = exports.version = exports.fromMethodAndPathAndBody = exports.fromMethodAndUrl = exports.fromExpressRequest = exports.SymmetricAlgorithm = exports.AsymmetricAlgorithm = void 0; | ||
var crypto_1 = require("crypto"); | ||
@@ -31,16 +55,22 @@ var lodash_1 = __importDefault(require("lodash")); | ||
// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ | ||
var Algorithm; | ||
(function (Algorithm) { | ||
Algorithm["HS256"] = "HS256"; | ||
Algorithm["HS384"] = "HS384"; | ||
Algorithm["HS512"] = "HS512"; | ||
})(Algorithm = exports.Algorithm || (exports.Algorithm = {})); | ||
var AsymmetricAlgorithm; | ||
(function (AsymmetricAlgorithm) { | ||
AsymmetricAlgorithm["RS256"] = "RS256"; | ||
})(AsymmetricAlgorithm = exports.AsymmetricAlgorithm || (exports.AsymmetricAlgorithm = {})); | ||
var SymmetricAlgorithm; | ||
(function (SymmetricAlgorithm) { | ||
SymmetricAlgorithm["HS256"] = "HS256"; | ||
SymmetricAlgorithm["HS384"] = "HS384"; | ||
SymmetricAlgorithm["HS512"] = "HS512"; | ||
})(SymmetricAlgorithm = exports.SymmetricAlgorithm || (exports.SymmetricAlgorithm = {})); | ||
function getAlgorithmFromString(rawAlgorithm) { | ||
switch (rawAlgorithm) { | ||
case 'HS256': | ||
return Algorithm.HS256; | ||
return SymmetricAlgorithm.HS256; | ||
case 'HS384': | ||
return Algorithm.HS384; | ||
return SymmetricAlgorithm.HS384; | ||
case 'HS512': | ||
return Algorithm.HS512; | ||
return SymmetricAlgorithm.HS512; | ||
case 'RS256': | ||
return AsymmetricAlgorithm.RS256; | ||
default: | ||
@@ -56,3 +86,4 @@ return undefined; | ||
HS384: 'sha384', | ||
HS512: 'sha512' | ||
HS512: 'sha512', | ||
RS256: 'RSA-SHA256' | ||
}; | ||
@@ -95,9 +126,10 @@ function fromExpressRequest(eReq) { | ||
var CANONICAL_QUERY_SEPARATOR = '&'; | ||
exports.version = '1.0.3'; | ||
exports.version = '2.0.0'; | ||
/** | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be HS256, HS384, or HS512. | ||
* The encoding algorithm must be RS256. | ||
* | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param signedAlgorithm should match the header.alg | ||
* @param noVerify optional, set to true to skip the result verification | ||
@@ -109,15 +141,38 @@ * | ||
*/ | ||
exports.decode = function jwt_decode(token, key, noVerify) { | ||
// Check seguments | ||
var segments = token.split('.'); | ||
if (segments.length !== 3) { | ||
throw new Error('Not enough or too many JWT token segments; should be 3'); | ||
exports.decodeAsymmetric = function jwt_decode_asymmetric(token, key, signedAlgorithm, noVerify) { | ||
// All segment should be base64 | ||
var _a = getTokenSegments(token), headerSeg = _a[0], payloadSeg = _a[1], signatureSeg = _a[2]; | ||
// Base64 decode and parse JSON | ||
var _b = getHeaderAndPayload(headerSeg, payloadSeg), header = _b[0], payload = _b[1]; | ||
validateAsymmetricAlgorithm(header.alg, signedAlgorithm); | ||
if (!noVerify) { | ||
verifySignature(headerSeg, payloadSeg, signatureSeg, key, header.alg); | ||
} | ||
return payload; | ||
}; | ||
function getKeyId(token) { | ||
var _a = getTokenSegments(token), headerSeg = _a[0], payloadSeg = _a[1]; | ||
// Base64 decode and parse JSON | ||
var header = getHeaderAndPayload(headerSeg, payloadSeg)[0]; | ||
return header && header.kid; | ||
} | ||
exports.getKeyId = getKeyId; | ||
/** | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be symmetric (HS256, HS384, or HS512). | ||
* | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param signedAlgorithm should match the header.alg | ||
* @param noVerify optional, set to true to skip the result verification | ||
* | ||
* @return Decoded JWT object | ||
* | ||
* @api public | ||
*/ | ||
exports.decodeSymmetric = function jwt_decode_symmetric(token, key, signedAlgorithm, noVerify) { | ||
// All segment should be base64 | ||
var headerSeg = segments[0]; | ||
var payloadSeg = segments[1]; | ||
var signatureSeg = segments[2]; | ||
var _a = getTokenSegments(token), headerSeg = _a[0], payloadSeg = _a[1], signatureSeg = _a[2]; | ||
// Base64 decode and parse JSON | ||
var header = JSON.parse(base64urlDecode(headerSeg)); | ||
var payload = JSON.parse(base64urlDecode(payloadSeg)); | ||
var _b = getHeaderAndPayload(headerSeg, payloadSeg), header = _b[0], payload = _b[1]; | ||
// Normalize 'aud' claim, the spec allows both String and Array | ||
@@ -127,2 +182,3 @@ if (payload.aud && !lodash_1.default.isArray(payload.aud)) { | ||
} | ||
validateSymmetricAlgorithm(header.alg, signedAlgorithm); | ||
if (!noVerify) { | ||
@@ -135,2 +191,3 @@ verifySignature(headerSeg, payloadSeg, signatureSeg, key, header.alg); | ||
* Encodes JWT object to string. | ||
* The encoding algorithm must be symmetric (HS256, HS384, or HS512). | ||
* | ||
@@ -145,4 +202,7 @@ * @param payload Payload object to encode | ||
*/ | ||
exports.encode = function jwt_encode(payload, key, algorithm) { | ||
var _a = validateAlgorithm(key, algorithm), signingAlgorithm = _a[0], signingMethod = _a[1]; | ||
exports.encodeSymmetric = function jwt_encode_symmetric(payload, key, algorithm) { | ||
var _a = validateSupportedAlgorithm(key, algorithm), signingAlgorithm = _a[0], signingMethod = _a[1]; | ||
if (!SymmetricAlgorithm[signingAlgorithm]) { | ||
throw new Error('Algorithm "' + algorithm + '" is not symmetric'); | ||
} | ||
// typ is fixed value | ||
@@ -157,2 +217,25 @@ var header = { typ: 'JWT', alg: signingAlgorithm }; | ||
}; | ||
/** | ||
* Encodes JWT object to string. | ||
* The encoding algorithm must be RS256 | ||
* | ||
* @param payload Payload object to encode | ||
* @param key Key used to encode | ||
* @param algorithm supports only RS256 | ||
* @returns | ||
*/ | ||
exports.encodeAsymmetric = function jwt_encode_asymmetric(payload, key, algorithm, header) { | ||
var _a = validateSupportedAlgorithm(key, algorithm), signingAlgorithm = _a[0], signingMethod = _a[1]; | ||
if (!AsymmetricAlgorithm[signingAlgorithm]) { | ||
throw new Error('Algorithm "' + algorithm + '" is not asymmetric'); | ||
} | ||
// typ is fixed value | ||
var defaultHeader = { typ: 'JWT', alg: signingAlgorithm }; | ||
// Create segments, all segment should be base64 string | ||
var segments = []; | ||
segments.push(base64urlEncode(JSON.stringify(header ? __assign(__assign({}, defaultHeader), { kid: header.kid }) : defaultHeader))); | ||
segments.push(base64urlEncode(JSON.stringify(payload))); | ||
segments.push(sign(segments.join('.'), key, signingMethod)); | ||
return segments.join('.'); | ||
}; | ||
function createCanonicalRequest(req, checkBodyForParams, baseUrl) { | ||
@@ -175,3 +258,3 @@ return canonicalizeMethod(req) + | ||
*/ | ||
function validateAlgorithm(key, algorithm) { | ||
function validateSupportedAlgorithm(key, algorithm) { | ||
// Check key | ||
@@ -193,7 +276,35 @@ if (!key) { | ||
} | ||
function validateAsymmetricAlgorithm(algorithm, expectedAlgorithm) { | ||
// Asymmetric algorithm: Check if expected algorithm is defined and it matches the header | ||
if (algorithm !== expectedAlgorithm) { | ||
throw new Error('Algorithm from the header "' + algorithm + '" does not match'); | ||
} | ||
if (!AsymmetricAlgorithm[expectedAlgorithm] || | ||
!AsymmetricAlgorithm[algorithm]) { | ||
throw new Error('Algorithm from the header "' + algorithm + '" is not asymmetric'); | ||
} | ||
} | ||
function validateSymmetricAlgorithm(algorithm, expectedAlgorithm) { | ||
// Symmetric algorithm: Check if expected algorithm is defined and it matches the header | ||
if (algorithm !== expectedAlgorithm) { | ||
throw new Error('Algorithm from the header "' + algorithm + '" does not match'); | ||
} | ||
if (!SymmetricAlgorithm[expectedAlgorithm] || | ||
!SymmetricAlgorithm[algorithm]) { | ||
throw new Error('Algorithm from the header "' + algorithm + '" is not symmetric'); | ||
} | ||
} | ||
function verifySignature(headerSeg, payloadSeg, signatureSeg, key, algorithm) { | ||
var _a = validateAlgorithm(key, algorithm), signingMethod = _a[1]; | ||
var _a = validateSupportedAlgorithm(key, algorithm), signingMethod = _a[1]; | ||
// Verify signature | ||
var signingInput = [headerSeg, payloadSeg].join('.'); | ||
if (signatureSeg !== sign(signingInput, key, signingMethod)) { | ||
var verified = false; | ||
if (AsymmetricAlgorithm[algorithm]) { | ||
verified = crypto_1.createVerify(signingMethod).update(signingInput).verify(key, base64urlUnescape(signatureSeg), 'base64'); | ||
} | ||
else { | ||
var signingOutput = sign(signingInput, key, signingMethod); | ||
verified = signatureSeg === signingOutput; | ||
} | ||
if (!verified) { | ||
throw new Error('Signature verification failed for input: ' + signingInput + ' with method ' + signingMethod); | ||
@@ -203,5 +314,30 @@ } | ||
function sign(input, key, method) { | ||
var base64str = crypto_1.createHmac(method, key).update(input).digest('base64'); | ||
var base64str; | ||
if (method !== 'RSA-SHA256') { | ||
base64str = crypto_1.createHmac(method, key).update(input).digest('base64'); | ||
} | ||
else { | ||
base64str = crypto_1.createSign(method).update(input).sign(key, 'base64'); | ||
} | ||
return base64urlEscape(base64str); | ||
} | ||
function getTokenSegments(token) { | ||
var segments = token.split('.'); | ||
if (segments.length !== 3) { | ||
throw new Error('Not enough or too many JWT token segments; should be 3'); | ||
} | ||
var headerSeg = segments[0]; | ||
var payloadSeg = segments[1]; | ||
var signatureSeg = segments[2]; | ||
return [headerSeg, payloadSeg, signatureSeg]; | ||
} | ||
function getHeaderAndPayload(headerSeg, payloadSeg) { | ||
var header = JSON.parse(base64urlDecode(headerSeg)); | ||
var payload = JSON.parse(base64urlDecode(payloadSeg)); | ||
// Normalize 'aud' claim, the spec allows both String and Array | ||
if (payload.aud && !lodash_1.default.isArray(payload.aud)) { | ||
payload.aud = [payload.aud]; | ||
} | ||
return [header, payload]; | ||
} | ||
function base64urlDecode(str) { | ||
@@ -208,0 +344,0 @@ return Buffer.from(base64urlUnescape(str), 'base64').toString(); |
{ | ||
"name": "atlassian-jwt", | ||
"description": "JWT (JSON Web Token) implementation with custom Atlassian QSH claim verification", | ||
"version": "1.0.3", | ||
"version": "2.0.0", | ||
"author": "Atlassian", | ||
@@ -6,0 +6,0 @@ "contributors": [ |
@@ -40,3 +40,3 @@ # atlassian-jwt | ||
const token = jwt.encode(tokenData, secret); | ||
const token = jwt.encodeSymmetric(tokenData, secret); | ||
console.log(token); | ||
@@ -48,3 +48,3 @@ ``` | ||
```typescript | ||
const decoded = jwt.decode(token, secret); | ||
const decoded = jwt.decodeSymmetric(token, secret); | ||
console.log(decoded); //=> { foo: 'bar' } | ||
@@ -54,3 +54,3 @@ | ||
// Don't do this unless that's your intention. | ||
const decoded = jwt.decode(token, null, true); | ||
const decoded = jwt.decodeSymmetric(token, null, true); | ||
console.log(decoded); //=> { foo: 'bar' } | ||
@@ -76,2 +76,4 @@ ``` | ||
and turns them into a `Request` object that can be used with other methods in this library. | ||
- `jwt.getKeyId(token)` | ||
Extracts `kid` from a jwt token. | ||
@@ -82,3 +84,3 @@ ### Algorithms | ||
The supported algorithms for encoding and decoding are `HS256`, `HS384`, and `HS512`. | ||
The supported algorithms for encoding and decoding are `HS256`, `HS384`, `HS512`, and `RS256`. | ||
See [Critical vulnerabilities in JSON Web Token libraries](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). | ||
@@ -90,6 +92,12 @@ | ||
// Encode using HS256 (default) | ||
jwt.encode(payload, secret); | ||
jwt.encodeSymmetric(payload, secret); | ||
// Encode using HS512 | ||
jwt.encode(payload, secret, jwt.Algorithm.HS512); | ||
jwt.encodeSymmetric(payload, secret, jwt.Algorithm.HS512); | ||
// Encode using RS256, optinally accepts kid | ||
jwt.encodeAsymmetric(payload, privateKey, jwt.Algorithm.RS256, { kid: 'f50f8562-f146-4b98-9bbb-7ce9545603b6' }); | ||
// Decode using RS256 | ||
jwt.decodeAsymmetric(token, publicKey, jwt.Algorithm.RS256); | ||
``` | ||
@@ -101,6 +109,12 @@ | ||
// Encode using HS256 (default) | ||
jwt.encode(payload, secret); | ||
jwt.encodeSymmetric(payload, secret); | ||
// Encode using HS512 | ||
jwt.encode(payload, secret, 'HS512'); | ||
jwt.encodeSymmetric(payload, secret, 'HS512'); | ||
// Encode using RS256, optinally accepts kid | ||
jwt.encodeAsymmetric(payload, privateKey, 'RS256', { kid: 'f50f8562-f146-4b98-9bbb-7ce9545603b6' }); | ||
// Decode using RS256 | ||
jwt.decodeAsymmetric(token, publicKey, 'RS256'); | ||
``` | ||
@@ -126,2 +140,7 @@ | ||
### Migrating from 1.x.x to 2.x.x | ||
The `2.x.x` release supports `RS256` asymmetric algorithm which can be used with `encodeAsymmetric` and `decodeAsymmetric` functions. | ||
`encode` and `decode` functions in `1.x.x` are renamed to `encodeSymmetric` and `decodeSymmetric` which supports symmetric algorithms only. | ||
## Guides for developers | ||
@@ -128,0 +147,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
28996
526
158