Skip to content

Instantly share code, notes, and snippets.

@jawi
Created June 2, 2016 12:48
Show Gist options
  • Save jawi/6edf93db8b8ce464c31d3acf4ca1c672 to your computer and use it in GitHub Desktop.
Save jawi/6edf93db8b8ce464c31d3acf4ca1c672 to your computer and use it in GitHub Desktop.
JWT decoder
<!DOCTYPE html>
<html lang="en">
<head>
<title>JSON web token decoder</title>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="content-language" content="en-us" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="pragma" content="no-cache" />
<meta name="description" content="JWT decoder" />
<meta name="author" content="J.W. Janssen" />
<meta name="copyright" content="&copy; 2016" />
<meta name="license" content="Apache License v2" />
<style>
html {
font-family: Helvetica;
font-size: 11pt;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
}
pre {
font-family: Consolas, monospace;
font-size: 14px;
line-height: 22px;
overflow-y: scroll;
white-space: pre-wrap;
margin: 0;
padding: 0;
}
.playground {
display: flex;
margin: 0;
padding: 0;
flex-direction: row;
}
.col {
width: calc(33.333333% - 30px);
margin: 20px;
height: 40em;
}
.box {
border: solid 1px gray;
padding: 10px;
height: 100%;
}
table.inline tr {
line-height: 1.2em;
}
table.inline th {
text-align: right;
}
table.inline {
border-spacing: 4px;
}
.string { color: blue; }
.number { color: green; }
.boolean { color: orange; }
.null { color: magenta; }
.key { color: darkblue; }
.err { color: red; font-weight: bold; }
</style>
</head>
<body>
<div class="playground">
<div class="col">
<h2>JWT input</h2>
<pre class="box" id='jwt-input' contenteditable="true" placeholder="token"></pre>
</div>
<div class="col">
<h2>Decoded output</h2>
<pre class="box" id='jwt-payload'></pre>
</div>
<div class="col">
<h2>Details</h2>
<div class="box">
<table class="inline">
<tr><th>Subject</th><td id='sub'>-</td></tr>
<tr><th>Issuer</th><td id='iss'>-</td></tr>
<tr><th>Audience</th><td id='aud'>-</td></tr>
<tr><th>Issued at</th><td id='iat'>-</td></tr>
<tr><th>Not before</th><td id='nbf'>-</td></tr>
<tr><th>Not after</th><td id='exp'>-</td></tr>
<tr><th>Expired?</th><td id='isExp'>-</td></tr>
</table>
</div>
</div>
</div>
<script type="text/javascript">
'use strict';
// see: https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
function base64_unicode_decode(str) {
return decodeURIComponent(str.replace(/(.)/g, function (m, p) {
var code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
function base64_url_decode(str) {
var output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Invalid JWT token: not a valid base64url string";
}
return base64_unicode_decode(atob(output));
}
function jwt_decode(token) {
if (!token) {
return
}
var parts = token.split('.')
if (parts.length < 3) {
throw "Invalid JWT token: need three parts!"
}
return [
JSON.parse(base64_url_decode(parts[0])),
JSON.parse(base64_url_decode(parts[1])),
parts[2]
];
}
// taken from: http://stackoverflow.com/a/7220510/229140
function json_highlight(json) {
if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2);
}
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
var cls = 'number';
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key';
} else {
cls = 'string';
}
} else if (/true|false/.test(match)) {
cls = 'boolean';
} else if (/null/.test(match)) {
cls = 'null';
}
return '<span class="' + cls + '">' + match + '</span>';
});
}
function el(id) {
return document.getElementById(id)
}
function parseEpochTime(t) {
var f = new Intl.DateTimeFormat(navigator.language || 'en-US', {
weekday: 'short', year: 'numeric', month: 'long', day: 'numeric',
hour: 'numeric', minute: 'numeric', second: 'numeric',
timeZoneName: 'short', hour12: false})
return f.format(new Date(t * 1000))
}
function format(n, v) {
if (n === 'iat' || n === 'nbf' || n === 'exp') {
return parseEpochTime(v)
}
return v;
}
function decode(ev) {
var txt = ev.target.innerText;
if (!txt || txt == "") {
return false;
}
var jwt
try {
jwt = jwt_decode(txt)
el('jwt-payload').innerHTML = json_highlight(jwt[0]) + '<br>' + json_highlight(jwt[1])
var payload = jwt[1]
for (var key in payload) {
var e = el(key)
if (e) {
e.innerHTML = '<span>' + format(key, payload[key]) + '</span>'
}
}
var isExp = (new Date().getTime() > (payload['exp'] * 1000))
el('isExp').innerHTML = '<span' + (exp ? ' class="err">yes' : '>no') + '</span>';
} catch (e) {
el('jwt-payload').innerHTML = '<span class="err">' + e + '!</span>'
}
}
el('jwt-input').addEventListener("input", decode, false)
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment