├── .gitignore ├── index.js ├── package.json ├── README.md └── src └── jwt.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/jwt'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-jwt", 3 | "version": "1.0.0", 4 | "description": "React native compatible JSON web token utility", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "postinstall": "rn-nodeify --install --hack" 9 | }, 10 | "repository": "https://github.com/StanScates/react-native-jwt.git", 11 | "keywords": [ 12 | "react", 13 | "react", 14 | "native", 15 | "jwt", 16 | "json", 17 | "web", 18 | "token", 19 | "react-native" 20 | ], 21 | "author": "Stan Scates ", 22 | "license": "MIT", 23 | "dependencies": { 24 | "base-64": "^0.1.0", 25 | "react-native-crypto": "^2.0.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ##react-native-jwt 2 | ###React Native compatible JSON Web Token encoder / decoder 3 | 4 | 5 | This package is essentially just [node-jwt-simple](https://github.com/hokaccha/node-jwt-simple) modified to use [react-native-crypto](https://github.com/mvayngrib/react-native-crypto) and [base-64](https://github.com/mathiasbynens/base64). 6 | 7 | ## Install 8 | 1. First, install [rn-nodeify](https://www.npmjs.com/package/rn-nodeify): `npm install -g rn-nodeify` 9 | 10 | 2. Then, install [react-native-randombytes](https://github.com/mvayngrib/react-native-randombytes) into your React Native project. Ensure that you import and link it properly as per the installation instructions. 11 | 12 | 3. Install react-native-jwt itself via `npm install react-native-jwt` 13 | 14 | 4. After the installation completes, the postinstall script will run `rn-nodeify --install --hack`, which should update your package.json with the necessary browserify shims. 15 | 16 | 5. Restart your React Native packager process, and you should be able to `require('react-native-jwt')`. Refer to [node-jwt-simple](https://github.com/hokaccha/node-jwt-simple) for further usage instructions. 17 | 18 | ## License 19 | MIT -------------------------------------------------------------------------------- /src/jwt.js: -------------------------------------------------------------------------------- 1 | /** 2 | * module dependencies 3 | */ 4 | var crypto = require('react-native-crypto'); 5 | var base64 = require('base-64'); 6 | 7 | /** 8 | * support algorithm mapping 9 | */ 10 | var algorithmMap = { 11 | HS256: 'sha256', 12 | HS384: 'sha384', 13 | HS512: 'sha512', 14 | RS256: 'RSA-SHA256' 15 | }; 16 | 17 | /** 18 | * Map algorithm to hmac or sign type, to determine which crypto function to use 19 | */ 20 | var typeMap = { 21 | HS256: 'hmac', 22 | HS384: 'hmac', 23 | HS512: 'hmac', 24 | RS256: 'sign' 25 | }; 26 | 27 | /** 28 | * expose object 29 | */ 30 | var jwt = module.exports; 31 | 32 | /** 33 | * Decode jwt 34 | * 35 | * @param {Object} token 36 | * @param {String} key 37 | * @param {Boolean} noVerify 38 | * @param {String} algorithm 39 | * @return {Object} payload 40 | * @api public 41 | */ 42 | jwt.decode = function jwt_decode(token, key, noVerify, algorithm) { 43 | // check token 44 | if (!token) { 45 | throw new Error('No token supplied'); 46 | } 47 | 48 | // check segments 49 | var segments = token.split('.'); 50 | if (segments.length !== 3) { 51 | throw new Error('Not enough or too many segments'); 52 | } 53 | 54 | // All segment should be base64 55 | var headerSeg = segments[0]; 56 | var payloadSeg = segments[1]; 57 | var signatureSeg = segments[2]; 58 | 59 | // base64 decode and parse JSON 60 | var header = JSON.parse(base64urlDecode(headerSeg)); 61 | var payload = JSON.parse(base64urlDecode(payloadSeg)); 62 | 63 | if (!noVerify) { 64 | var signingMethod = algorithmMap[algorithm || header.alg]; 65 | var signingType = typeMap[algorithm || header.alg]; 66 | if (!signingMethod || !signingType) { 67 | throw new Error('Algorithm not supported'); 68 | } 69 | 70 | // verify signature. `sign` will return base64 string. 71 | var signingInput = [headerSeg, payloadSeg].join('.'); 72 | if (!verify(signingInput, key, signingMethod, signingType, signatureSeg)) { 73 | throw new Error('Signature verification failed'); 74 | } 75 | } 76 | 77 | return payload; 78 | }; 79 | 80 | /** 81 | * Encode jwt 82 | * 83 | * @param {Object} payload 84 | * @param {String} key 85 | * @param {String} algorithm 86 | * @param {Object} options 87 | * @return {String} token 88 | * @api public 89 | */ 90 | jwt.encode = function jwt_encode(payload, key, algorithm, options) { 91 | // Check key 92 | if (!key) { 93 | throw new Error('Require key'); 94 | } 95 | 96 | // Check algorithm, default is HS256 97 | algorithm = algorithm || 'HS256'; 98 | 99 | var signingMethod = algorithmMap[algorithm]; 100 | var signingType = typeMap[algorithm]; 101 | if (!signingMethod || !signingType) { 102 | throw new Error('Algorithm not supported'); 103 | } 104 | 105 | // header, typ is fixed value. 106 | var header = { typ: 'JWT', alg: algorithm }; 107 | if (options && options.header) { 108 | assignProperties(header, options.header); 109 | } 110 | 111 | // create segments, all segments should be base64 string 112 | var segments = []; 113 | segments.push(base64urlEncode(JSON.stringify(header))); 114 | segments.push(base64urlEncode(JSON.stringify(payload))); 115 | segments.push(sign(segments.join('.'), key, signingMethod, signingType)); 116 | 117 | return segments.join('.'); 118 | }; 119 | 120 | /** 121 | * private util functions 122 | */ 123 | function assignProperties(dest, source) { 124 | for (var attr in source) { 125 | if (source.hasOwnProperty(attr)) { 126 | dest[attr] = source[attr]; 127 | } 128 | } 129 | } 130 | 131 | function verify(input, key, method, type, signature) { 132 | if(type === "hmac") { 133 | return (signature === sign(input, key, method, type)); 134 | } 135 | 136 | else if(type == "sign") { 137 | return crypto.createVerify(method) 138 | .update(input) 139 | .verify(key, base64urlUnescape(signature), 'base64'); 140 | } 141 | 142 | else { 143 | throw new Error('Algorithm type not recognized'); 144 | } 145 | } 146 | 147 | function sign(input, key, method, type) { 148 | var base64str; 149 | if(type === "hmac") { 150 | base64str = crypto.createHmac(method, key).update(input).digest('base64'); 151 | } 152 | 153 | else if(type == "sign") { 154 | base64str = crypto.createSign(method).update(input).sign(key, 'base64'); 155 | } 156 | 157 | else { 158 | throw new Error('Algorithm type not recognized'); 159 | } 160 | 161 | return base64urlEscape(base64str); 162 | } 163 | 164 | function base64urlDecode(str) { 165 | return base64.decode(str); 166 | } 167 | 168 | function base64urlUnescape(str) { 169 | str += new Array(5 - str.length % 4).join('='); 170 | return str.replace(/\-/g, '+').replace(/_/g, '/'); 171 | } 172 | 173 | function base64urlEncode(str) { 174 | return base64.encode(str); 175 | } 176 | 177 | function base64urlEscape(str) { 178 | return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, ''); 179 | } 180 | --------------------------------------------------------------------------------