atlassian-jwt
Advanced tools
Comparing version 1.0.2 to 1.0.3
@@ -37,13 +37,13 @@ import { Request as ExpressRequest } from 'express'; | ||
}; | ||
export declare const version = "1.0.3"; | ||
/** | ||
* version | ||
*/ | ||
export declare const version = "0.1.0"; | ||
/** | ||
* Decode jwt | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be HS256, HS384, or HS512. | ||
* | ||
* @param {Object} token | ||
* @param {String} key | ||
* @param {Boolean} noVerify | ||
* @return {Object} payload | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param noVerify optional, set to true to skip the result verification | ||
* | ||
* @return Decoded JWT object | ||
* | ||
* @api public | ||
@@ -53,8 +53,10 @@ */ | ||
/** | ||
* Encode jwt | ||
* Encodes JWT object to string. | ||
* | ||
* @param {Object} payload | ||
* @param {String} key | ||
* @param {String} algorithm | ||
* @return {String} token | ||
* @param payload Payload object to encode | ||
* @param key Key used to encode | ||
* @param algorithm Optional, must be HS256, HS384, or HS512; default is HS256 | ||
* | ||
* @return Encoded JWT string | ||
* | ||
* @api public | ||
@@ -61,0 +63,0 @@ */ |
"use strict"; | ||
/* | ||
* Based off jwt-simple, adds query string hash verification | ||
* Based off jwt-simple: | ||
* https://github.com/hokaccha/node-jwt-simple | ||
* | ||
* Add Atlassian query string hash verification: | ||
* https://developer.atlassian.com/cloud/jira/platform/understanding-jwt/ | ||
* | ||
* JSON Web Token encode and decode module for node.js | ||
@@ -10,2 +14,5 @@ * | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
@@ -18,13 +25,8 @@ if (mod && mod.__esModule) return mod; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** | ||
* module dependencies | ||
*/ | ||
var crypto_1 = require("crypto"); | ||
var _ = __importStar(require("lodash")); | ||
var lodash_1 = __importDefault(require("lodash")); | ||
var jsuri_1 = __importDefault(require("jsuri")); | ||
var url = __importStar(require("url")); | ||
// https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ | ||
var Algorithm; | ||
@@ -48,2 +50,10 @@ (function (Algorithm) { | ||
} | ||
/** | ||
* Supported algorithm mapping. | ||
*/ | ||
var algorithmMap = { | ||
HS256: 'sha256', | ||
HS384: 'sha384', | ||
HS512: 'sha512' | ||
}; | ||
function fromExpressRequest(eReq) { | ||
@@ -82,31 +92,23 @@ // req.originalUrl represents the full URL and req.path represents the URL from the last router | ||
/** | ||
* support algorithm mapping | ||
*/ | ||
var algorithmMap = { | ||
HS256: 'sha256', | ||
HS384: 'sha384', | ||
HS512: 'sha512' | ||
}; | ||
/** | ||
* The separator between sections of a canonical query. | ||
*/ | ||
var CANONICAL_QUERY_SEPARATOR = '&'; | ||
exports.version = '1.0.3'; | ||
/** | ||
* version | ||
*/ | ||
exports.version = '0.1.0'; | ||
/** | ||
* Decode jwt | ||
* Decodes JWT string to object. | ||
* The encoding algorithm must be HS256, HS384, or HS512. | ||
* | ||
* @param {Object} token | ||
* @param {String} key | ||
* @param {Boolean} noVerify | ||
* @return {Object} payload | ||
* @param token JWT to decode | ||
* @param key Key used to decode | ||
* @param noVerify optional, set to true to skip the result verification | ||
* | ||
* @return Decoded JWT object | ||
* | ||
* @api public | ||
*/ | ||
exports.decode = function jwt_decode(token, key, noVerify) { | ||
// check seguments | ||
// Check seguments | ||
var segments = token.split('.'); | ||
if (segments.length !== 3) { | ||
throw new Error('Not enough or too many segments'); | ||
throw new Error('Not enough or too many JWT token segments; should be 3'); | ||
} | ||
@@ -117,23 +119,11 @@ // All segment should be base64 | ||
var signatureSeg = segments[2]; | ||
// base64 decode and parse JSON | ||
// Base64 decode and parse JSON | ||
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 && !_.isArray(payload.aud)) { | ||
// Normalize 'aud' claim, the spec allows both String and Array | ||
if (payload.aud && !lodash_1.default.isArray(payload.aud)) { | ||
payload.aud = [payload.aud]; | ||
} | ||
if (!noVerify) { | ||
var alg = getAlgorithmFromString(header.alg); | ||
if (!alg) { | ||
throw new Error('Algorithm "' + header.alg + '" is not supported'); | ||
} | ||
var signingMethod = algorithmMap[alg]; | ||
if (!signingMethod) { | ||
throw new Error('Algorithm "' + header.alg + '" is not supported'); | ||
} | ||
// verify signature. `sign` will return base64 string. | ||
var signingInput = [headerSeg, payloadSeg].join('.'); | ||
if (signatureSeg !== sign(signingInput, key, signingMethod)) { | ||
throw new Error('Signature verification failed for input: ' + signingInput + ' with method ' + signingMethod); | ||
} | ||
verifySignature(headerSeg, payloadSeg, signatureSeg, key, header.alg); | ||
} | ||
@@ -143,26 +133,17 @@ return payload; | ||
/** | ||
* Encode jwt | ||
* Encodes JWT object to string. | ||
* | ||
* @param {Object} payload | ||
* @param {String} key | ||
* @param {String} algorithm | ||
* @return {String} token | ||
* @param payload Payload object to encode | ||
* @param key Key used to encode | ||
* @param algorithm Optional, must be HS256, HS384, or HS512; default is HS256 | ||
* | ||
* @return Encoded JWT string | ||
* | ||
* @api public | ||
*/ | ||
exports.encode = function jwt_encode(payload, key, algorithm) { | ||
// Check key | ||
if (!key) { | ||
throw new Error('Require key'); | ||
} | ||
// Check algorithm, default is HS256 | ||
if (!algorithm) { | ||
algorithm = Algorithm.HS256; | ||
} | ||
var signingMethod = algorithmMap[algorithm]; | ||
if (!signingMethod) { | ||
throw new Error('Algorithm "' + algorithm + '" is not supported'); | ||
} | ||
// header, typ is fixed value. | ||
var header = { typ: 'JWT', alg: algorithm }; | ||
// create segments, all segment should be base64 string | ||
var _a = validateAlgorithm(key, algorithm), signingAlgorithm = _a[0], signingMethod = _a[1]; | ||
// typ is fixed value | ||
var header = { typ: 'JWT', alg: signingAlgorithm }; | ||
// Create segments, all segment should be base64 string | ||
var segments = []; | ||
@@ -189,4 +170,29 @@ segments.push(base64urlEncode(JSON.stringify(header))); | ||
/** | ||
* private util functions | ||
* Private util functions. | ||
*/ | ||
function validateAlgorithm(key, algorithm) { | ||
// Check key | ||
if (!key) { | ||
throw new Error('Require key'); | ||
} | ||
// Check algorithm, default is HS256 | ||
var signingAlgorithm = algorithm || 'HS256'; | ||
var alg = getAlgorithmFromString(signingAlgorithm); | ||
if (!alg) { | ||
throw new Error('Algorithm "' + algorithm + '" is not supported'); | ||
} | ||
var signingMethod = algorithmMap[alg]; | ||
if (!signingMethod) { | ||
throw new Error('Algorithm "' + algorithm + '" is not supported'); | ||
} | ||
return [signingAlgorithm, signingMethod]; | ||
} | ||
function verifySignature(headerSeg, payloadSeg, signatureSeg, key, algorithm) { | ||
var _a = validateAlgorithm(key, algorithm), signingMethod = _a[1]; | ||
// Verify signature | ||
var signingInput = [headerSeg, payloadSeg].join('.'); | ||
if (signatureSeg !== sign(signingInput, key, signingMethod)) { | ||
throw new Error('Signature verification failed for input: ' + signingInput + ' with method ' + signingMethod); | ||
} | ||
} | ||
function sign(input, key, method) { | ||
@@ -226,7 +232,7 @@ var base64str = crypto_1.createHmac(method, key).update(input).digest('base64'); | ||
path = path.replace(new RegExp(CANONICAL_QUERY_SEPARATOR, 'g'), encodeRfc3986(CANONICAL_QUERY_SEPARATOR)); | ||
// prefix with / | ||
// Prefix with / | ||
if (path[0] !== '/') { | ||
path = '/' + path; | ||
} | ||
// remove trailing / | ||
// Remove trailing / | ||
if (path.length > 1 && path[path.length - 1] === '/') { | ||
@@ -238,13 +244,14 @@ path = path.substring(0, path.length - 1); | ||
function canonicalizeQueryString(req, checkBodyForParams) { | ||
var queryParams = req.query, method = req.method.toUpperCase(); | ||
var queryParams = req.query; | ||
var method = req.method.toUpperCase(); | ||
// Apache HTTP client (or something) sometimes likes to take the query string and put it into the request body | ||
// if the method is PUT or POST | ||
if (checkBodyForParams && _.isEmpty(queryParams) && (method === 'POST' || method === 'PUT')) { | ||
if (checkBodyForParams && lodash_1.default.isEmpty(queryParams) && (method === 'POST' || method === 'PUT')) { | ||
queryParams = req.body; | ||
} | ||
var sortedQueryString = new Array(), query = _.extend({}, queryParams); | ||
if (!_.isEmpty(query)) { | ||
// remove the 'jwt' query string param | ||
var sortedQueryString = new Array(), query = lodash_1.default.extend({}, queryParams); | ||
if (!lodash_1.default.isEmpty(query)) { | ||
// Remove the 'jwt' query string param | ||
delete query.jwt; | ||
_.each(_.keys(query).sort(), function (key) { | ||
lodash_1.default.each(lodash_1.default.keys(query).sort(), function (key) { | ||
// The __proto__ field can sometimes sneak in depending on what node version is being used. | ||
@@ -255,5 +262,6 @@ // Get rid of it or the qsh calculation will be wrong. | ||
} | ||
var param = query[key], paramValue = ''; | ||
var param = query[key]; | ||
var paramValue = ''; | ||
if (Array.isArray(param)) { | ||
paramValue = _.map(param.sort(), encodeRfc3986).join(','); | ||
paramValue = lodash_1.default.map(param.sort(), encodeRfc3986).join(','); | ||
} | ||
@@ -260,0 +268,0 @@ else { |
{ | ||
"name": "atlassian-jwt", | ||
"description": "JWT (JSON Web Token) implementation with custom Atlassian QSH claim verification", | ||
"version": "1.0.2", | ||
"author": "Seb Ruiz <sruiz@atlassian.com>", | ||
"version": "1.0.3", | ||
"author": "Atlassian", | ||
"contributors": [ | ||
"Robert Massaioli <rmassaioli@atlassian.com>", | ||
"Jake Furler <jfurler@atlassian.com>", | ||
"Ngoc Dao <ndao@atlassian.com>", | ||
"Ralph Whitbeck <rwhitbeck@atlassian.com>", | ||
"Parambir Singh", | ||
"RichardS", | ||
"Seb Ruiz <sruiz@atlassian.com>" | ||
], | ||
"homepage": "https://bitbucket.org/atlassian/atlassian-jwt-js", | ||
"repository": { | ||
@@ -10,19 +20,22 @@ "type": "git", | ||
}, | ||
"publishConfig": { | ||
"registry": "https://registry.npmjs.org" | ||
}, | ||
"dependencies": { | ||
"jsuri": "^1.3.1", | ||
"lodash": "^4.12.0" | ||
"lodash": "^4.17.11" | ||
}, | ||
"devDependencies": { | ||
"@types/express": "^4.16.0", | ||
"@types/express": "^4.17.0", | ||
"@types/jsuri": "^1.3.30", | ||
"@types/lodash": "^4.14.116", | ||
"@types/mocha": "^5.2.5", | ||
"@types/lodash": "^4.14.134", | ||
"@types/mocha": "^5.2.7", | ||
"@types/node": "^10.7.1", | ||
"@types/qs": "^6.5.1", | ||
"mocha": "^5.2.0", | ||
"moment": "^2.14.1", | ||
"qs": "^6.2.1", | ||
"ts-node": "^7.0.1", | ||
"tslint": "^5.11.0", | ||
"typescript": "^3.0.1" | ||
"@types/qs": "^6.5.3", | ||
"mocha": "^6.1.4", | ||
"moment": "^2.24.0", | ||
"qs": "^6.7.0", | ||
"ts-node": "^8.3.0", | ||
"tslint": "^5.17.0", | ||
"typescript": "^3.5.2" | ||
}, | ||
@@ -29,0 +42,0 @@ "scripts": { |
# atlassian-jwt | ||
![build-status](https://bitbucket-badges.atlassian.io/badge/atlassian/atlassian-jwt-js.svg) | ||
[![TypeScript](https://badges.frapsoft.com/typescript/code/typescript.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) | ||
[JWT (JSON Web Token)](http://self-issued.info/docs/draft-jones-json-web-token.html) encoding & decoding | ||
library for node.js. Built on [jwt-simple](https://github.com/hokaccha/node-jwt-simple) and adds support | ||
[JWT (JSON Web Token)](http://self-issued.info/docs/draft-jones-json-web-token.html) encoding & decoding | ||
library for node.js. Built on [jwt-simple](https://github.com/hokaccha/node-jwt-simple), it adds support | ||
for Atlassian's custom QSH (query string hash) claim. | ||
For more information on using JWT tokens with Atlassian add-ons, please read: | ||
For more information on using JWT tokens with Atlassian apps, please read: | ||
[Understanding JWT](https://developer.atlassian.com/cloud/jira/platform/understanding-jwt/). | ||
@@ -15,3 +14,5 @@ | ||
$ npm install atlassian-jwt | ||
```sh | ||
npm install atlassian-jwt | ||
``` | ||
@@ -33,5 +34,5 @@ ## Usage | ||
"iss": 'issuer-val', | ||
"iat": now.unix(), // the time the token is generated | ||
"exp": now.add(3, 'minutes').unix(), // token expiry time (recommend 3 minutes after issuing) | ||
"qsh": jwt.createQueryStringHash(req) // [Query String Hash](https://developer.atlassian.com/cloud/jira/platform/understanding-jwt/#a-name-qsh-a-creating-a-query-string-hash) | ||
"iat": now.unix(), // The time the token is generated | ||
"exp": now.add(3, 'minutes').unix(), // Token expiry time (recommend 3 minutes after issuing) | ||
"qsh": jwt.createQueryStringHash(req) // [Query String Hash](https://developer.atlassian.com/cloud/jira/platform/understanding-jwt/#a-name-qsh-a-creating-a-query-string-hash) | ||
}; | ||
@@ -47,16 +48,9 @@ | ||
```javascript | ||
/* | ||
* jwt.decode(token, secret, noVerify, algorithm) | ||
* | ||
* Decodes the JWT token and verifies the signature using the secret and algorithm. Algorithm defaults to HS256. | ||
*/ | ||
var decoded = jwt.decode(token, secret); | ||
```typescript | ||
const decoded = jwt.decode(token, secret); | ||
console.log(decoded); //=> { foo: 'bar' } | ||
/* | ||
* Decode without verifing the signature of the token. | ||
* Tokens should never be used without verifying the signature as otherwise payload trust cannot be established. | ||
*/ | ||
var decoded = jwt.decode(token, null, true); | ||
// Decode without verifing the signature of the token. | ||
// Don't do this unless that's your intention. | ||
const decoded = jwt.decode(token, null, true); | ||
console.log(decoded); //=> { foo: 'bar' } | ||
@@ -67,12 +61,17 @@ ``` | ||
- `jwt.createQueryStringHash(req, checkBodyForParams, baseUrl)` | ||
Create a QSH using the algorithm defined by [the algorithm](https://developer.atlassian.com/static/connect/docs/latest/concepts/understanding-jwt.html#qsh) . | ||
- `jwt.createCanonicalRequest(req, checkBodyForParams, baseUrl)` | ||
Creates a canonical request which is used to calculate the QSH for the JWT token. Prefer using `#createQueryStringHash()` directly. | ||
- `jwt.fromExpressRequest(expressRequest: ExpressRequest)` | ||
Converts an Express.js Request into a `Request` object that can be used with other methods in this library. | ||
- `jwt.fromMethodAndUrl(method: string, url: string)` | ||
This takes in a method and url, both as plain strings, and turns them into a `Request` object that can be used with other methods in this library. | ||
- `jwt.fromMethodAndPathAndBody` | ||
This takes in a method, a url, and some form params from a request body and turns them into a `Request` object that can be used with other methods in this library. | ||
- `jwt.createQueryStringHash(req, checkBodyForParams, baseUrl)` | ||
Creates a QSH using the algorithm defined by | ||
[the algorithm](https://developer.atlassian.com/static/connect/docs/latest/concepts/understanding-jwt.html#qsh). | ||
- `jwt.createCanonicalRequest(req, checkBodyForParams, baseUrl)` | ||
Creates a canonical request which is used to calculate the QSH for the JWT token. | ||
Prefer using `#createQueryStringHash()` directly. | ||
- `jwt.fromExpressRequest(expressRequest: ExpressRequest)` | ||
Converts an Express.js request into a `Request` object | ||
that can be used with other methods in this library. | ||
- `jwt.fromMethodAndUrl(method, url)` | ||
Takes in a method and URL, both as plain strings, | ||
and turns them into a `Request` object that can be used with other methods in this library. | ||
- `jwt.fromMethodAndPathAndBody(method, url, body)` | ||
Takes in a method, a URL, and some form params from a request body | ||
and turns them into a `Request` object that can be used with other methods in this library. | ||
@@ -83,7 +82,23 @@ ### Algorithms | ||
The supported algorithms for encoding and decoding are `HS256`, `HS384`, `HS512` and `RS256`. | ||
The supported algorithms for encoding and decoding are `HS256`, `HS384`, and `HS512`. | ||
See [Critical vulnerabilities in JSON Web Token libraries](https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/). | ||
If you use TypeScript: | ||
```typescript | ||
// Encode using HS256 (default) | ||
jwt.encode(payload, secret); | ||
// Encode using HS512 | ||
jwt.encode(payload, secret, jwt.Algorithm.HS512); | ||
``` | ||
If you use JavaScript: | ||
```javascript | ||
// encode using HS512 | ||
jwt.encode(payload, secret, 'HS512') | ||
// Encode using HS256 (default) | ||
jwt.encode(payload, secret); | ||
// Encode using HS512 | ||
jwt.encode(payload, secret, 'HS512'); | ||
``` | ||
@@ -113,11 +128,17 @@ | ||
Update `version` in package.json and lib/jwt.ts. | ||
To publish this library: | ||
npm run tsc | ||
npm publish | ||
```sh | ||
npm run tsc | ||
npm publish | ||
``` | ||
This has been combined into a single command with: | ||
npm run build-and-publish | ||
```sh | ||
npm run build-and-publish | ||
``` | ||
Only the built typescript files will be published with this library. | ||
Only the built TypeScript files will be published with this library. |
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
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
19771
347
0
139
1
Updatedlodash@^4.17.11