├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## How to contribute to azure-jwt0verify 2 | 3 | #### **Did you find a bug?** 4 | 5 | * **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/99xt/azure-jwt-verify/issues). 6 | 7 | * If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/99xt/azure-jwt-verify/issues/new). Be sure to include a **title and clear description**, as much relevant information as possible, and a **code sample** or an **executable test case** demonstrating the expected behavior that is not occurring. 8 | 9 | #### **Did you write a patch that fixes a bug?** 10 | 11 | * Open a new GitHub pull request with the patch. 12 | 13 | * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. 14 | 15 | #### **Do you intend to add a new feature or change an existing one?** 16 | 17 | * Suggest your change in the issue list and start writing code. 18 | 19 | 20 |
21 | 22 | azure-jwt-verify is a volunteer effort. We encourage you to pitch in and [join the team](https://github.com/99xt/azure-jwt-verify/graphs/contributors)! 23 | 24 | Thanks! :heart: :heart: :heart: 25 | 26 | azure-jwt-verify Team 27 | 28 | CONTRIBUTING file inspired/based on [rails contrubiting](https://github.com/rails/rails/blob/master/CONTRIBUTING.md) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 99X Technology 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | azure-jwt-verify 2 | ================================= 3 | 4 | [![npm version](https://badge.fury.io/js/azure-jwt-verify.svg)](https://www.npmjs.com/package/azure-jwt-verify) 5 | [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) 6 | 7 | ## This Plugin Requires 8 | * NodeJS Runtime 9 | 10 | ## Features 11 | * Verify JWT Token issued by Azure Active Directory B2C 12 | * Automatically use the rotated public key from Azure Public Keys URL 13 | 14 | ## Install Plugin 15 | `npm install --save azure-jwt-verify` 16 | 17 | ## Using Azure JWT Verify in your code 18 | You need to define the following constants based on your Azure Active Directory B2C application configurations 19 | 20 | ### Initialize module 21 | ```javascript 22 | var azureJWT = require('azure-jwt-verify'); 23 | 24 | ``` 25 | 26 | ### Configuration and the JWT to verify 27 | ```javascript 28 | var jwtToken = "YOUR_JWT_TOKEN_TO_VERIFY"; // You can find this url in Azure Active Directory B2C Section 29 | const config = { 30 | JWK_URI: "", 31 | ISS: "", 32 | AUD: "" 33 | }; 34 | ``` 35 | * JWK_URI and the ISS(Issuer) can be obtained from the metadata endpoint of the policies created in the B2C tenant. 36 | * AUD(Audience) is the Client ID of the application accessing the tenant. 37 | 38 | ### Verify Function 39 | ```javascript 40 | azureJWT.verify(jwtToken, config).then(function(decoded){ 41 | // success callback 42 | 43 | }, function(error){ 44 | // error callback 45 | 46 | }) 47 | ``` 48 | 49 | ## Response Examples 50 | 51 | #### For valid and authorize JWT token. 52 | 53 | Response body: 54 | ```JSON 55 | { 56 | "status": "success", 57 | "message": { 58 | "exp": 1486111778, 59 | "nbf": 1486108178, 60 | "ver": "1.0", 61 | "iss": "https://login.microsoftonline.com/9434db1e-33cd-4607-b350-9d947127a5de/v2.0/", 62 | "sub": "ff8490cb-2bda-4cc8-b801-4a22a8620c59", 63 | "aud": "dfc54797-efe3-4db3-a1e1-422b4ecf27d7", 64 | "nonce": "defaultNonce", 65 | "iat": 1486108178, 66 | "auth_time": 1486108178, 67 | "oid": "ff8490cb-2bda-4cc8-b801-4a22a8620c59", 68 | "name": "jon snow", 69 | "extension_NIC": "933132325V", 70 | "emails": [ 71 | "hellodishan@gmail.com" 72 | ], 73 | "tfp": "B2C_1_b2c_1_sign_in" 74 | } 75 | } 76 | ``` 77 | 78 | #### For valid but expired JWT token. 79 | 80 | Response body: 81 | ```JSON 82 | { 83 | "status": "error", 84 | "message": { 85 | "name": "TokenExpiredError", 86 | "message": "jwt expired", 87 | "expiredAt": "2017-02-02T12:30:11.000Z" 88 | } 89 | } 90 | ``` 91 | 92 | #### For invalid JWT token. 93 | 94 | Response body: 95 | ```JSON 96 | { 97 | "status": "error", 98 | "message": "Error Decoding JWT Token" 99 | } 100 | ``` 101 | 102 | 103 | 104 | 105 | ## Links 106 | * [Azure Active Directory B2C](https://azure.microsoft.com/en-us/services/active-directory-b2c/) 107 | * [Contact Us](mailto:ashanf@99x.lk) 108 | * [NPM Registry](https://www.npmjs.com/package/azure-jwt-verify) 109 | 110 | ## License 111 | [MIT](LICENSE) 112 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const BbPromise = require('bluebird'); 2 | const jwt = require('jsonwebtoken'); 3 | const request = require('request'); 4 | const _ = require('lodash'); 5 | const getPem = require('rsa-pem-from-mod-exp'); 6 | 7 | const publicKeys = {}; 8 | 9 | // Validate the jwt Token with the audience and the issuer 10 | const verifyJwt = function verifyJwt(jwtToken, publicKey, aud, iss) { 11 | return new BbPromise(function (resolve, reject) { 12 | jwt.verify(jwtToken, publicKey, { algorithms: ['RS256'], audience: aud, issuer: iss }, 13 | function (error, decoded) { 14 | if (!error) { 15 | resolve(decoded); 16 | } else { 17 | reject(error); 18 | } 19 | }); 20 | }); 21 | }; 22 | 23 | // fetch publicKeys (mod and exp) from jwks_uri if there are no current kid matching 24 | const getPublicKeys = function getPublicKeys(JWK_URI, jwtKid) { 25 | if (hasPublicKey(jwtKid)) { 26 | return new BbPromise(function (resolve, reject) { 27 | resolve(publicKeys); 28 | }); 29 | } else { 30 | return new BbPromise(function (resolve, reject) { 31 | request(JWK_URI, function (error, response, body) { 32 | if (!error && response.statusCode == 200) { 33 | let keys = JSON.parse(body).keys; 34 | updatePublicKeys(keys); 35 | resolve(publicKeys); 36 | } else { 37 | reject(error) 38 | } 39 | }); 40 | }); 41 | } 42 | }; 43 | 44 | // generate and cache the rsa public key from modulus exponent 45 | const updatePublicKeys = function (b2cKeys) { 46 | _.forEach(b2cKeys, function (value) { 47 | publicKeys[value.kid] = getPem(value.n, value.e) 48 | }); 49 | }; 50 | 51 | // retunrs the public key for the given kid from the cached keys 52 | const getPublicKey = function (jwtKid) { 53 | if (publicKeys.hasOwnProperty(jwtKid)) { 54 | return publicKeys[jwtKid]; 55 | } else { 56 | return false; 57 | } 58 | }; 59 | 60 | // check if the kid has a public key 61 | const hasPublicKey = function (jwtKid) { 62 | return publicKeys.hasOwnProperty(jwtKid); 63 | }; 64 | 65 | // verify the jwtToken against the given configuration 66 | exports.verify = function (jwtToken, config) { 67 | return new BbPromise(function (resolve, reject) { 68 | let decoded = jwt.decode(jwtToken, { complete: true }); 69 | if (!decoded) { 70 | reject('{ "status":"error", "message":"Error Decoding JWT Token" }'); 71 | } else { 72 | let jwtKid = decoded.header.kid; 73 | if (!jwtKid) { 74 | reject('{ "status":"error", "message":"Invalid JWT Token" }'); 75 | } else { 76 | getPublicKeys(config.JWK_URI, jwtKid).then(function (response) { 77 | if (hasPublicKey(jwtKid)) { 78 | let publicKey = getPublicKey(jwtKid); 79 | return verifyJwt(jwtToken, publicKey, config.AUD, config.ISS).then(function (response) { 80 | resolve(JSON.stringify({ "status": "success", "message": response })); 81 | }).catch(function (error) { 82 | reject(JSON.stringify({ "status": "error", "message": error })); 83 | }); 84 | } else { 85 | reject('{ "status":"error", "message":"Invalid jwt kid" }'); 86 | } 87 | }).catch(function (error) { 88 | reject('{ "status":"error", "message":"Cannot fetch data from JWK_URI" }'); 89 | }) 90 | } 91 | } 92 | }); 93 | }; 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-jwt-verify", 3 | "version": "1.0.0", 4 | "description": "Verify JWT Token issued by Azure Active Directory B2C", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/99xt/azure-jwt-verify.git" 12 | }, 13 | "keywords": [ 14 | "azure", 15 | "b2c", 16 | "jwt", 17 | "verify" 18 | ], 19 | "author": "99xtechnology.com", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/99xt/azure-jwt-verify/issues" 23 | }, 24 | "homepage": "https://github.com/99xt/azure-jwt-verify#readme", 25 | "dependencies": { 26 | "bluebird": "^3.4.7", 27 | "jsonwebtoken": "^8.5.1", 28 | "lodash": "^4.17.4", 29 | "request": "^2.79.0", 30 | "rsa-pem-from-mod-exp": "^0.8.4" 31 | } 32 | } 33 | --------------------------------------------------------------------------------