-
Notifications
You must be signed in to change notification settings - Fork 74
/
verify-jwt.js
146 lines (117 loc) · 3.67 KB
/
verify-jwt.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
import crypto from 'crypto';
import cf from 'cloudfront';
// updated the original example from below URL to use KVS
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-validate-token.html
//Response when JWT is not valid.
const response401 = {
statusCode: 401,
statusDescription: 'Unauthorized'
};
// Replace the KVS_ID with your KVS ID
const kvsId = "KVS_ID";
const kvsKey = 'jwt.secret';
// set to true to enable console logging
const loggingEnabled = false;
function jwt_decode(token, key, noVerify, algorithm) {
// check token
if (!token) {
throw new Error('No token supplied');
}
// check segments
const segments = token.split('.');
if (segments.length !== 3) {
throw new Error('Not enough or too many segments');
}
// All segment should be base64
const headerSeg = segments[0];
const payloadSeg = segments[1];
const signatureSeg = segments[2];
// base64 decode and parse JSON
const payload = JSON.parse(_base64urlDecode(payloadSeg));
if (!noVerify) {
const signingMethod = 'sha256';
const signingType = 'hmac';
// Verify signature. `sign` will return base64 string.
const signingInput = [headerSeg, payloadSeg].join('.');
if (!_verify(signingInput, key, signingMethod, signingType, signatureSeg)) {
throw new Error('Signature verification failed');
}
// Support for nbf and exp claims.
// According to the RFC, they should be in seconds.
if (payload.nbf && Date.now() < payload.nbf*1000) {
throw new Error('Token not yet active');
}
if (payload.exp && Date.now() > payload.exp*1000) {
throw new Error('Token expired');
}
}
return payload;
}
//Function to ensure a constant time comparison to prevent
//timing side channels.
function _constantTimeEquals(a, b) {
if (a.length != b.length) {
return false;
}
let xor = 0;
for (let i = 0; i < a.length; i++) {
xor |= (a.charCodeAt(i) ^ b.charCodeAt(i));
}
return 0 === xor;
}
function _verify(input, key, method, type, signature) {
if(type === "hmac") {
return _constantTimeEquals(signature, _sign(input, key, method));
}
else {
throw new Error('Algorithm type not recognized');
}
}
function _sign(input, key, method) {
return crypto.createHmac(method, key).update(input).digest('base64url');
}
function _base64urlDecode(str) {
return Buffer.from(str, 'base64url')
}
async function handler(event) {
let request = event.request;
//Secret key used to verify JWT token.
//Update with your own key.
const secret_key = await getSecret()
if(!secret_key) {
return response401;
}
// If no JWT token, then generate HTTP redirect 401 response.
if(!request.querystring.jwt) {
log("Error: No JWT in the querystring");
return response401;
}
const jwtToken = request.querystring.jwt.value;
try{
jwt_decode(jwtToken, secret_key);
}
catch(e) {
log(e);
return response401;
}
//Remove the JWT from the query string if valid and return.
delete request.querystring.jwt;
log("Valid JWT token");
return request;
}
// get secret from key value store
async function getSecret() {
// initialize cloudfront kv store and get the key value
try {
const kvsHandle = cf.kvs(kvsId);
return await kvsHandle.get(kvsKey);
} catch (err) {
log(`Error reading value for key: ${kvsKey}, error: ${err}`);
return null;
}
}
function log(message) {
if (loggingEnabled) {
console.log(message);
}
}