Introducing Edgio Applications v7Find out what's new.
Edgio
Edgio

JWT Verification

Edge functions can be used to verify the authenticity of a JSON Web Token (JWT) sent by a client. This can be useful for ensuring that the client is authorized to access a protected resource, or for verifying the identity of the client. Handling this verification at the edge can help to offload the work from the origin server, and offer a more secure and efficient way to verify the token.

Router Configuration

In the Edgio router, you can use the edge_function feature to specify the path to the edge function that will handle the JWT verification. We expect the client to send a POST request with a JSON body containing the JWT token to be verified. The edge function will then validate the token and return a response with the validation result.
JavaScriptroutes.js
1import { Router, edgioRoutes } from '@edgio/core';
2
3export default new Router()
4 .use(edgioRoutes)
5 .post('/jwt', {
6 edge_function: './edge-functions/main.js',
7 });

Edge Function

The edge function will be responsible for validating the JWT token sent by the client. The token will be extracted from the request body, and the secret key used to sign the token will be retrieved from the environment variables. The token will then be validated using the secret key, and the result will be returned in the response. This example supports the HS256, HS384, and HS512 algorithms.
The Edge Function runtime does not currently support a native crypto library, so a third-party library to generate the signature is needed. In this example, we’ll use the crypto-js library.
JavaScriptedge-functions/main.js
1import { JWT } from './JWT.js';
2
3/**
4 * Handle an HTTP request to validate a JWT.
5 *
6 * @param {Request} request - The incoming HTTP request.
7 * @param {any} context - Context providing runtime information.
8 * @returns {Response} HTTP response with validation result.
9 */
10export async function handleHttpRequest(request, context) {
11 // Extract the JWT token from the request body
12 const { token } = await request.json();
13
14 // Retrieve the secret key from environment variables
15 const secret = context.environmentVars['JWT_SECRET'] || 'your-256-bit-secret';
16
17 // Initialize response structure
18 const resp = { valid: false };
19
20 // Create JWT instance with the token and secret
21 const jwt = new JWT(token, secret);
22
23 // Validate the JWT
24 const isValid = jwt.validate();
25
26 // If valid, update response with additional JWT info
27 if (isValid) {
28 resp.valid = true;
29 resp.payload = jwt.payloadObject(); // Extract payload
30 resp.alg = jwt.algUsed(); // Extract algorithm used
31 }
32
33 // Return the response with appropriate HTTP status code
34 return new Response(JSON.stringify(resp), {
35 status: isValid ? 200 : 401, // 200 OK for valid token, 401 for invalid
36 headers: { 'Content-Type': 'application/json' }, // Set response content type
37 });
38}
JavaScriptedge-functions/JWT.js
1import { Buffer } from 'buffer';
2import * as Base64 from 'crypto-js/enc-base64url';
3import { HmacSHA256, HmacSHA384, HmacSHA512 } from 'crypto-js';
4
5// Function to decode base64 strings
6const base64decode = (str) => Buffer.from(str, 'base64').toString();
7
8// Hashing functions mapped to JWT algorithms
9const hashLibraries = {
10 HS256: HmacSHA256,
11 HS384: HmacSHA384,
12 HS512: HmacSHA512,
13};
14
15export class JWT {
16 constructor(token, secret) {
17 const [header_base64, payload_base64, origSignature] = token.split('.');
18
19 this.header_base64 = header_base64;
20 this.payload_base64 = payload_base64;
21
22 try {
23 // Decode header and payload from base64
24 this.header = JSON.parse(base64decode(header_base64));
25 this.payload = JSON.parse(base64decode(payload_base64));
26 } catch (e) {
27 // Invalid payload or header, initialize empty objects
28 this.header = {};
29 this.payload = {};
30 }
31
32 this.origSignature = origSignature;
33 this.hasher = hashLibraries[this.header.alg];
34 this.secret = secret;
35 }
36
37 // Validates the JWT token
38 validate() {
39 try {
40 const calculatedSignature = Base64.stringify(
41 this.hasher(`${this.header_base64}.${this.payload_base64}`, this.secret)
42 );
43 return calculatedSignature === this.origSignature;
44 } catch (e) {
45 return false;
46 }
47 }
48
49 // Returns the payload object
50 payloadObject() {
51 return this.payload;
52 }
53
54 // Returns the algorithm used in JWT
55 algUsed() {
56 return this.header.alg;
57 }
58}