-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add token generator protected by Cloudflare Access (#43)
* WIP: token generator * Fix FE JS * Add token to DOM, take input from JSON * Make token preformatted * Added secure area index * Added verification functionality * Add missing await * Added brackets * Move token-verify function * Add heading for token verification form
- Loading branch information
1 parent
ac1219b
commit f7996fa
Showing
5 changed files
with
161 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
const encoder = new TextEncoder(); | ||
|
||
async function generateHash(input) { | ||
return [ | ||
...new Uint8Array( | ||
await crypto.subtle.digest( | ||
{ name: 'SHA-256' }, | ||
encoder.encode(input) | ||
) | ||
) | ||
].map(x => x.toString(16).padStart(2, '0')).join('') | ||
} | ||
|
||
async function generateSecureToken({ | ||
expiry, | ||
name, | ||
secret, | ||
}) { | ||
const hash = (await generateHash(`${name}/${expiry}/${secret}`)).substring(0, 8); | ||
return `${name}/${expiry}/${hash}`; | ||
} | ||
|
||
async function verifySecureToken({ | ||
secret, | ||
token, | ||
}) { | ||
const [name, expiry, hash] = token.split("/"); | ||
|
||
const expiryDate = new Date(expiry).addDays(1); | ||
if (expiryDate < new Date()) { | ||
return false; | ||
} | ||
|
||
const expectedHash = (await generateHash(`${name}/${expiry}/${secret}`)).substring(0, 8); | ||
if (hash != expectedHash) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
Date.prototype.addDays = function(days) { | ||
var date = new Date(this.valueOf()); | ||
date.setDate(date.getDate() + days); | ||
return date; | ||
} | ||
|
||
export { | ||
generateSecureToken, | ||
verifySecureToken, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { verifySecureToken } from "../../functions-src/secure-token.js" | ||
|
||
export async function onRequest(context) { | ||
if (context.request.method !== "POST") { | ||
return new Response("Invalid request method", { status: 405 }); | ||
} | ||
|
||
const json = await context.request.json(); | ||
const secret = context.env.TOKEN_GENERATOR_SECRET; | ||
const ok = await verifySecureToken({ | ||
token: json.token, | ||
secret, | ||
}); | ||
return Response.json({ ok }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { generateSecureToken } from "../../../functions-src/secure-token.js" | ||
|
||
export async function onRequest(context) { | ||
if (context.request.method !== "POST") { | ||
return new Response("Invalid request method", { status: 405 }); | ||
} | ||
|
||
const json = await context.request.json(); | ||
const secret = context.env.TOKEN_GENERATOR_SECRET; | ||
const token = await generateSecureToken({ | ||
name: json.name, | ||
expiry: json.expiry, | ||
secret, | ||
}); | ||
return Response.json({ token }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
layout: default | ||
title: "Secure area: index" | ||
--- | ||
|
||
- [Token generator (for urgent contact form)](token-generator) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
--- | ||
layout: default | ||
title: Token Generator | ||
--- | ||
|
||
Use this form to generate a token for the urgent contact form. | ||
|
||
<form id="token-generation-form"> | ||
<fieldset style="margin-bottom:1em"> | ||
<label for="name" style="display:inline-block; margin-bottom:0.5em">Token name:</label> | ||
<input type="text" name="name" id="name" placeholder="Token name" style="box-sizing:border-box; width:100%; max-width:20em" required> | ||
</fieldset> | ||
<fieldset style="margin-bottom:1em"> | ||
<label for="expiry" style="display:inline-block; margin-bottom:0.5em">Expiry date:</label> | ||
<input type="date" name="expiry" id="expiry" style="box-sizing:border-box; width:100%; max-width:20em" required> | ||
</fieldset> | ||
<button type="submit" style="margin-bottom:1em">Generate Token</button> | ||
</form> | ||
<p>Your token is:</p> | ||
<pre style="display:inline" id="token-value">...</pre> | ||
|
||
## Token Verifier | ||
|
||
<form id="token-verification-form"> | ||
<fieldset style="margin-bottom:1em"> | ||
<label for="name" style="display:inline-block; margin-bottom:0.5em">Token:</label> | ||
<input type="text" name="token" id="token" placeholder="Token" style="box-sizing:border-box; width:100%; max-width:20em" required> | ||
</fieldset> | ||
<button type="submit" style="margin-bottom:1em">Verify Token</button> | ||
</form> | ||
<p>Token status: <span id="token-verify-output">?</span></p> | ||
|
||
<script> | ||
document.getElementById("token-generation-form").addEventListener("submit", event => { | ||
event.preventDefault() | ||
const formData = new FormData(event.target); | ||
const name = formData.get("name"); | ||
const expiry = formData.get("expiry"); | ||
fetch('/secure/api/token-generate', { | ||
method: 'POST', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ name, expiry }) | ||
}) | ||
.then(res => res.json()) | ||
.then(res => document.getElementById("token-value").innerHTML = res.token); | ||
}); | ||
|
||
document.getElementById("token-verification-form").addEventListener("submit", event => { | ||
event.preventDefault() | ||
const formData = new FormData(event.target); | ||
const token = formData.get("token"); | ||
fetch('/api/token-verify', { | ||
method: 'POST', | ||
headers: { | ||
'Accept': 'application/json', | ||
'Content-Type': 'application/json' | ||
}, | ||
body: JSON.stringify({ token }) | ||
}) | ||
.then(res => res.json()) | ||
.then(res => { | ||
const element = document.getElementById("token-verify-output"); | ||
if (res.ok) { | ||
element.innerHTML = "ok"; | ||
} else { | ||
element.innerHTML = "BAD"; | ||
} | ||
}); | ||
}); | ||
</script> |