Skip to content

Commit

Permalink
Apply style and comment changes
Browse files Browse the repository at this point in the history
  • Loading branch information
tristanlee85 committed Feb 1, 2024
1 parent 7b83a90 commit ead248b
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 109 deletions.
74 changes: 39 additions & 35 deletions examples/v7-jwt-verification/edge-functions/JWT.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,58 @@
import { Buffer } from 'buffer'
import * as Base64 from 'crypto-js/enc-base64url'
import { HmacSHA256, HmacSHA384, HmacSHA512 } from 'crypto-js'
import { Buffer } from 'buffer';
import * as Base64 from 'crypto-js/enc-base64url';
import { HmacSHA256, HmacSHA384, HmacSHA512 } from 'crypto-js';

const base64decode = (str) => Buffer.from(str, 'base64').toString()
// Function to decode base64 strings
const base64decode = (str) => Buffer.from(str, 'base64').toString();

// Hashing functions mapped to JWT algorithms
const hashLibraries = {
HS256: HmacSHA256,
HS384: HmacSHA384,
HS512: HmacSHA512,
}
};

export class JWT {
// JWT validation process:
// 1. Split the token by '.' to get the header (json), payload (json), and signature (string).
// 2. Calculate a signature using the algorithm in the header (hardcoded here) to join the header and payload with a
// '.', and hash it using a secret value
// 3. Compare the calculated signature with the one from the token. If they match, the token is valid. If not, the
// token has been tampered with.

constructor(token, secret) {
const [ header_base64, payload_base64, origSignature ] = token.split('.')

this.header_base64 = header_base64
this.payload_base64 = payload_base64

this.header = JSON.parse(base64decode(header_base64))
this.payload = JSON.parse(base64decode(payload_base64))

this.origSignature = origSignature

this.hasher = hashLibraries[this.header.alg]
this.secret = secret
const [header_base64, payload_base64, origSignature] = token.split('.');

this.header_base64 = header_base64;
this.payload_base64 = payload_base64;

try {
// Decode header and payload from base64
this.header = JSON.parse(base64decode(header_base64));
this.payload = JSON.parse(base64decode(payload_base64));
} catch (e) {
// Invalid payload or header, initialize empty objects
this.header = {};
this.payload = {};
}

this.origSignature = origSignature;
this.hasher = hashLibraries[this.header.alg];
this.secret = secret;
}

// Validates the JWT token
validate() {
console.log(`validating token using ${this.header.alg} algorithm.`)
const calculatedSignature = Base64.stringify(
this.hasher(
`${this.header_base64}.${this.payload_base64}`,
this.secret
)
)
return calculatedSignature === this.origSignature
try {
const calculatedSignature = Base64.stringify(
this.hasher(`${this.header_base64}.${this.payload_base64}`, this.secret)
);
return calculatedSignature === this.origSignature;
} catch (e) {
return false;
}
}

// Returns the payload object
payloadObject() {
return this.payload
return this.payload;
}

// Returns the algorithm used in JWT
algUsed() {
return this.header.alg
return this.header.alg;
}
}
}
45 changes: 31 additions & 14 deletions examples/v7-jwt-verification/edge-functions/validate.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
import { JWT } from './JWT.js'
import { JWT } from './JWT.js';

/**
* Handle an HTTP request to validate a JWT.
*
* @param {Request} request - The incoming HTTP request.
* @param {any} context - Context providing runtime information.
* @returns {Response} HTTP response with validation result.
*/
export async function handleHttpRequest(request, context) {
const token = await request.text()
const secret = context.environmentVars['JWT_SECRET'] || ''
const resp = {
valid: false
}
// Extract the JWT token from the request body
const { token } = await request.json();

// Retrieve the secret key from environment variables
const secret = context.environmentVars['JWT_SECRET'] || 'your-256-bit-secret';

// Initialize response structure
const resp = { valid: false };

// Create JWT instance with the token and secret
const jwt = new JWT(token, secret);

// Validate the JWT
const isValid = jwt.validate();

const jwt = new JWT(token, secret)
const isValid = jwt.validate()
// If valid, update response with additional JWT info
if (isValid) {
resp.valid = true
resp.payload = jwt.payloadObject()
resp.alg = jwt.algUsed()
resp.valid = true;
resp.payload = jwt.payloadObject(); // Extract payload
resp.alg = jwt.algUsed(); // Extract algorithm used
}

// Return the response with appropriate HTTP status code
return new Response(JSON.stringify(resp), {
status: isValid ? 200 : 403
})
}
status: isValid ? 200 : 403, // 200 OK for valid token, 403 Forbidden for invalid
headers: { 'Content-Type': 'application/json' }, // Set response content type
});
}
10 changes: 5 additions & 5 deletions examples/v7-jwt-verification/edgio.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Learn more about this file at https://docs.edg.io/guides/edgio_config
module.exports = {
// The name of the site in Edgio to which this app should be deployed.
name: "ef-jwt-validate",
name: 'ef-jwt-verification',

// The name of the organization in Edgio to which this app should be deployed.
// organization: 'my-organization-name',
Expand All @@ -19,22 +19,22 @@ module.exports = {
origins: [
{
// The name of the backend origin
name: "origin",
name: 'origin',

// Use the following to override the host header sent from the browser when connecting to the origin
override_host_header: "httpbin.org",
override_host_header: 'httpbin.org',

// The list of origin hosts to which to connect
hosts: [
{
// The domain name or IP address of the origin server
location: "httpbin.org",
location: 'httpbin.org',
},
],

tls_verify: {
use_sni: true,
sni_hint_and_strict_san_check: "httpbin.org",
sni_hint_and_strict_san_check: 'httpbin.org',
},

// Uncomment the following to configure a shield
Expand Down
2 changes: 1 addition & 1 deletion examples/v7-jwt-verification/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Router, edgioRoutes } from '@edgio/core';

export default new Router()
.use(edgioRoutes)
.get('/validate', ({ serveStatic }) => {
.get('/', ({ serveStatic }) => {
serveStatic('static/validate.html');
})
.post('/jwt', {
Expand Down
176 changes: 122 additions & 54 deletions examples/v7-jwt-verification/static/validate.html
Original file line number Diff line number Diff line change
@@ -1,58 +1,126 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JWT Validator</title>
<style type="text/css">
.wrapper {
max-width: 640px;
margin: 1rem auto;
}
fieldset {
margin-bottom: 1rem;
}
</style>
</head>
<body>
<div class="wrapper">
<form action="#" id="validator">
<fieldset>
<legend for="token">Your token:</legend>
<textarea name="token" id="token" cols="70" rows="10"></textarea>
</fieldset>
<button type="submit">Validate</button>
</form>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JWT Validator</title>
<style type="text/css">
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
color: #333;
}
.wrapper {
max-width: 640px;
margin: 2rem auto;
padding: 1rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
fieldset {
border: 1px solid #ddd;
padding: 0.5rem;
}
textarea {
width: calc(100% - 1rem); /* Adjust width to prevent overflow */
padding: 0.25rem;
border: 1px solid #ddd;
border-radius: 4px;
margin-top: 0.5rem;
box-sizing: border-box; /* Include padding and border in the element's total width and height */
}
button {
padding: 0.5rem 1rem;
color: white;
background-color: #007bff;
border: none;
border-radius: 4px;
cursor: pointer;
margin-top: 1rem;
}
button:hover {
background-color: #0056b3;
}
#results {
margin-top: 1rem;
padding: 1rem;
background-color: #eee;
border-radius: 4px;
}
</style>
</head>
<body>
<div class="wrapper">
<h1>JWT Validator</h1>
<form id="validator">
<fieldset>
<legend>Enter Your Token:</legend>
<textarea
name="token"
id="token"
rows="10"
placeholder="Paste your JWT here..."
>
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJjb21tZW50IjoiRWRnZSBmdW5jdGlvbnMgYXJlIHN3ZWV0ISJ9.hc0nVeZWhE6MEf-CHJwljTY3uo6qqm8q_V_0zwm34tFALrjJDAa0CL3oUEehMRNdt3PQdcuWBgzMUqKVUWMRVQ</textarea
>
</fieldset>
<button type="submit">Validate</button>
</form>

<div id="results">
<p>Click "Validate" above.</p>
<p>
You can generate a test token using the default signing key
<code>your-256-bit-secret</code> at
<a href="https://jwt.io" target="_blank" rel="noopener noreferrer"
>jwt.io</a
>. Valid algorithms for this example include <code>HS256</code>,
<code>HS384</code>, and <code>HS512</code>.
</p>

<div id="results">
<p>Click "Validate" to verify your token.</p>
</div>
</div>
</div>

<script type="text/javascript">
const form = document.getElementById('validator')
form.addEventListener('submit', async (e) => {
e.preventDefault()
const token = form.querySelector('textarea[name="token"]').value
let resultString = ''
try {
const resp = await fetch('/jwt', {
method: 'POST',
body: token
})
const data = await resp.json()
console.log(data)
if (data.valid) {
resultString = `<p>Token successfully validated using ${data.alg}. Refresh the page to try another token.</p><p>Payload:</p><pre>${JSON.stringify(data.payload)}</pre>`
} else {
resultString = '<p>An error occurred validating the token.</p>'
}
} catch (e) {
resultString = '<p>An error occurred validating the token.</p>'
console.error(e)
} finally {
document.getElementById('results').innerHTML = resultString
}
})
</script>
</body>
</html>
<script type="text/javascript">
document
.getElementById('validator')
.addEventListener('submit', async (e) => {
e.preventDefault();
const token = document.getElementById('token').value;
let resultString = '';

try {
const response = await fetch('/jwt', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token }),
});

if (!response.ok) {
throw new Error(`Server error occurred. Response code: ${response.status}, Response body: ${JSON.stringify(await response.json())}`);
}

const data = await response.json();

if (data.valid) {
resultString = `<p>Token successfully validated using ${
data.alg
}.</p><p>Payload:</p><pre>${JSON.stringify(
data.payload,
null,
2
)}</pre>`;
} else {
resultString = '<p>An error occurred validating the token.</p>';
}
} catch (error) {
resultString = `<p>${error.message}</p>`;
console.error('Error:', error);
} finally {
document.getElementById('results').innerHTML = resultString;
}
});
</script>
</body>
</html>

0 comments on commit ead248b

Please sign in to comment.