forked from joaquimserafim/json-web-token
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
152 lines (120 loc) · 3.2 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
'use strict'
const crypto = require('crypto')
const b64url = require('base64-url')
const inherits = require('util').inherits
//
// supported algorithms
//
const algorithms = {
HS256: {hash: 'sha256', type: 'hmac'},
HS384: {hash: 'sha384', type: 'hmac'},
HS512: {hash: 'sha512', type: 'hmac'},
RS256: {hash: 'RSA-SHA256', type: 'sign'}
}
//
// JSON Web Token
//
const jwt = module.exports
jwt.JWTError = JWTError
jwt.getAlgorithms = getAlgorithms
jwt.encode = encode
jwt.decode = decode
function getAlgorithms () {
return Object.keys(algorithms)
}
function encode (key, payload, algorithm, cb) {
if (paramIsValid(algorithm, 'function')) {
cb = algorithm
algorithm = 'HS256'
}
var validationError = encodeValidations(key, payload, algorithm)
if (validationError) {
return prcResult(validationError, null, cb)
}
var parts = b64url.encode(JSON.stringify({typ: 'JWT', alg: algorithm})) +
'.' +
b64url.encode(JSON.stringify(payload))
return prcResult(
null,
parts + '.' + sign(algorithms[algorithm], key, parts),
cb
)
}
function decode (key, token, cb) {
if (paramsAreFalsy(key, token)) {
return prcResult('The key and token are mandatory!', null, cb)
}
var parts = token.split('.')
// check all parts're present
if (parts.length !== 3) {
return prcResult(
'The JWT should consist of three parts!',
null,
cb
)
}
// base64 decode and parse JSON
var header = JSON.parse(b64url.decode(parts[0]))
var payload = JSON.parse(b64url.decode(parts[1]))
// get algorithm hash and type and check if is valid
var algorithm = algorithms[header.alg]
if (!algorithm) {
return prcResult('The algorithm is not supported!', null, cb)
}
// verify the signature
var res = verify(
algorithm,
key,
parts.slice(0, 2).join('.'),
parts[2]
)
return prcResult(!res && 'Invalid key!' || null, payload, cb)
}
function encodeValidations (key, payload, algorithm) {
return paramsAreFalsy(key, payload) ?
'The key and payload are mandatory!' :
!Object.keys(payload).length ?
'The payload is an empty object!' :
!algorithms[algorithm] && 'The algorithm is not supported!'
}
//
// JWT token error
//
function JWTError (message) {
Error.call(this)
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = message
}
inherits(JWTError, Error)
//
// Utils methods
//
function sign (alg, key, input) {
return 'hmac' === alg.type ?
b64url.escape(crypto.createHmac(alg.hash, key)
.update(input)
.digest('base64')) :
b64url.escape(crypto.createSign(alg.hash)
.update(input)
.sign(key, 'base64'))
}
function verify (alg, key, input, signVar) {
return 'hmac' === alg.type ?
signVar === sign(alg, key, input) :
crypto.createVerify(alg.hash)
.update(input)
.verify(key, b64url.unescape(signVar), 'base64')
}
function prcResult (err, res, cb) {
err = err && new JWTError(err)
return cb ?
cb(err, res) :
{error: err, value: res}
}
function paramIsValid (param, type) {
return !param || typeof param === type
}
function paramsAreFalsy (param1, param2) {
return !param1 || !param2
}