Skip to content

Commit 59f63b2

Browse files
committed
[update] how-to guide: jwt debugger more algos
1 parent 2dc123c commit 59f63b2

File tree

2 files changed

+215
-123
lines changed

2 files changed

+215
-123
lines changed

docs/assets/js/jwt-debugger.js

Lines changed: 114 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,139 @@
11

2-
// JWT Debugger
2+
// // JWT Debugger
3+
34

45
document.addEventListener('DOMContentLoaded', function () {
56
const jwtInput = document.getElementById('jwtInput');
67
const headerInput = document.getElementById('jwtHeaderInput');
78
const payloadInput = document.getElementById('jwtPayloadInput');
89
const secretInput = document.getElementById('jwtSecretInput');
9-
const encodedOutput = document.getElementById('jwtEncoded');
10+
const publicKeyInput = document.getElementById('jwtPublicKeyInput');
11+
const privateKeyInput = document.getElementById('jwtPrivateKeyInput');
1012
const encodingSelect = document.getElementById('secretEncoding');
13+
const algorithmSelect = document.getElementById('jwtAlgorithm');
14+
const encodedOutput = document.getElementById('jwtEncoded');
1115
const verifyStatus = document.getElementById('jwtVerifyStatus');
12-
16+
1317
document.getElementById('decode-jwt')?.addEventListener('click', decodeJWT);
1418
document.getElementById('verify-jwt')?.addEventListener('click', verifyJWT);
1519
document.getElementById('encode-jwt')?.addEventListener('click', encodeJWT);
16-
20+
21+
function getAlgorithm() {
22+
return algorithmSelect?.value || 'HS256';
23+
}
24+
25+
function getKeyForSign(alg) {
26+
if (alg.startsWith('HS')) {
27+
const secret = secretInput.value.trim();
28+
const encoding = encodingSelect.value;
29+
const keyObj = {};
30+
keyObj[encoding] = secret;
31+
return keyObj;
32+
} else if (alg.startsWith('RS') || alg.startsWith('PS') || alg.startsWith('ES')) {
33+
return privateKeyInput.value.trim();
34+
} else {
35+
throw new Error(`Unsupported algorithm for signing: ${alg}`);
36+
}
37+
}
38+
39+
function getKeyForVerify(alg) {
40+
if (alg.startsWith('HS')) {
41+
const secret = secretInput.value.trim();
42+
const encoding = encodingSelect.value;
43+
const keyObj = {};
44+
keyObj[encoding] = secret;
45+
return keyObj;
46+
} else if (alg.startsWith('RS') || alg.startsWith('PS') || alg.startsWith('ES')) {
47+
return publicKeyInput.value.trim();
48+
} else {
49+
throw new Error(`Unsupported algorithm for verification: ${alg}`);
50+
}
51+
}
52+
1753
function decodeJWT() {
18-
const jwt = jwtInput.value.trim();
19-
const parts = jwt.split('.');
20-
if (parts.length !== 3) {
21-
return showError('Invalid JWT format (expected 3 parts)');
22-
}
23-
24-
try {
25-
const header = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(parts[0]));
26-
const payload = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(parts[1]));
27-
28-
headerInput.value = JSON.stringify(header, null, 2);
29-
payloadInput.value = JSON.stringify(payload, null, 2);
30-
showStatus('', '✅ Decoded successfully (not verified)');
31-
} catch (err) {
32-
showError('Failed to decode JWT: ' + err.message);
33-
}
54+
const jwt = jwtInput.value.trim();
55+
const parts = jwt.split('.');
56+
if (parts.length !== 3) {
57+
return showError('Invalid JWT format (expected 3 parts)');
58+
}
59+
60+
try {
61+
const header = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(parts[0]));
62+
const payload = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(parts[1]));
63+
64+
headerInput.value = JSON.stringify(header, null, 2);
65+
payloadInput.value = JSON.stringify(payload, null, 2);
66+
showStatus('', '✅ Decoded successfully (not verified)');
67+
} catch (err) {
68+
showError('Failed to decode JWT: ' + err.message);
69+
}
3470
}
35-
71+
3672
function verifyJWT() {
37-
try {
38-
const jwt = jwtInput.value.trim();
39-
const secret = secretInput.value.trim();
40-
const encoding = encodingSelect.value;
41-
if (!jwt || !secret) {
42-
return showError('JWT and secret are required.');
43-
}
44-
45-
const keyObj = {};
46-
keyObj[encoding] = secret;
47-
48-
const isValid = KJUR.jws.JWS.verify(jwt, keyObj, ['HS256']);
49-
50-
if (isValid) {
51-
const [headerB64, payloadB64] = jwt.split('.');
52-
const header = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(headerB64));
53-
const payload = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(payloadB64));
54-
55-
headerInput.value = JSON.stringify(header, null, 2);
56-
payloadInput.value = JSON.stringify(payload, null, 2);
57-
showStatus(true, '✅ Signature is valid');
58-
} else {
59-
showStatus(false, '❌ Signature is invalid');
60-
}
61-
} catch (err) {
62-
showError('Verification failed: ' + err.message);
73+
try {
74+
const jwt = jwtInput.value.trim();
75+
const key = getKeyForVerify(getAlgorithm());
76+
const alg = getAlgorithm();
77+
78+
const isValid = KJUR.jws.JWS.verify(jwt, key, [alg]);
79+
80+
if (isValid) {
81+
const [headerB64, payloadB64] = jwt.split('.');
82+
const header = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(headerB64));
83+
const payload = KJUR.jws.JWS.readSafeJSONString(b64urlDecode(payloadB64));
84+
85+
headerInput.value = JSON.stringify(header, null, 2);
86+
payloadInput.value = JSON.stringify(payload, null, 2);
87+
showStatus(true, '✅ Signature is valid');
88+
} else {
89+
showStatus(false, '❌ Signature is invalid');
6390
}
91+
} catch (err) {
92+
showError('Verification failed: ' + err.message);
93+
}
6494
}
65-
95+
6696
function encodeJWT() {
67-
try {
68-
const header = JSON.parse(headerInput.value.trim());
69-
const payload = JSON.parse(payloadInput.value.trim());
70-
const secret = secretInput.value.trim();
71-
const encoding = encodingSelect.value;
72-
73-
const sHeader = JSON.stringify(header);
74-
const sPayload = JSON.stringify(payload);
75-
76-
const keyObj = {};
77-
keyObj[encoding] = secret;
78-
79-
const jwt = KJUR.jws.JWS.sign(header.alg || 'HS256', sHeader, sPayload, keyObj);
80-
81-
jwtInput.value = jwt;
82-
showStatus('', '✅ JWT encoded successfully');
83-
} catch (err) {
84-
showError('Encoding failed: ' + err.message);
85-
}
97+
try {
98+
const header = JSON.parse(headerInput.value.trim());
99+
const payload = JSON.parse(payloadInput.value.trim());
100+
const alg = getAlgorithm();
101+
102+
header.alg = alg; // force-match selected alg
103+
const sHeader = JSON.stringify(header);
104+
const sPayload = JSON.stringify(payload);
105+
const key = getKeyForSign(alg);
106+
107+
const jwt = KJUR.jws.JWS.sign(alg, sHeader, sPayload, key);
108+
109+
jwtInput.value = jwt;
110+
if (encodedOutput) encodedOutput.innerText = jwt;
111+
showStatus('', '✅ JWT encoded successfully');
112+
} catch (err) {
113+
showError('Encoding failed: ' + err.message);
114+
}
86115
}
87-
116+
88117
function b64urlDecode(str) {
89-
str = str.replace(/-/g, '+').replace(/_/g, '/');
90-
while (str.length % 4 !== 0) str += '=';
91-
return atob(str);
118+
str = str.replace(/-/g, '+').replace(/_/g, '/');
119+
while (str.length % 4 !== 0) str += '=';
120+
return atob(str);
92121
}
93-
122+
94123
function showError(msg) {
95-
verifyStatus.innerText = '❌ ' + msg;
96-
verifyStatus.className = 'text-danger font-weight-bold';
124+
verifyStatus.innerText = '❌ ' + msg;
125+
verifyStatus.className = 'text-danger font-weight-bold';
97126
}
98-
127+
99128
function showStatus(valid, msg) {
100-
verifyStatus.innerText = valid === true
101-
? '✅ Signature is valid'
102-
: valid === false
103-
? '❌ Signature is invalid'
104-
: msg || '';
105-
verifyStatus.className = valid === true
106-
? 'text-success font-weight-bold'
107-
: 'text-danger font-weight-bold';
129+
verifyStatus.innerText = valid === true
130+
? '✅ Signature is valid'
131+
: valid === false
132+
? '❌ Signature is invalid'
133+
: msg || '';
134+
verifyStatus.className = valid === true
135+
? 'text-success font-weight-bold'
136+
: 'text-danger font-weight-bold';
108137
}
109-
});
138+
});
139+

docs/pages/how-to-jwt-debugger.md

Lines changed: 101 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ layout: page
33
title: JWT Debugger
44
parent: How-To Guides
55
permalink: /how-to/jwt-debugger
6-
description: Inspect, verify, and create JSON Web Tokens securely in your browser — no server required.
6+
description: Inspect, verify, and create JSON Web Tokens securely using all the following algorithms RSA, RSAPSS, HMAC, EC. Multi-algo, security-grade JWT debugger.
77
nav_order: 12
88
---
99

@@ -19,6 +19,17 @@ Or, create a new one by editing the header and payload and providing a secret.
1919
{: .highlight }
2020
> **Private and Secure:** Fully client-side — nothing leaves your browser !
2121
22+
---
23+
24+
## **Token & Key Format Summary**
25+
{: .fs-5 }
26+
27+
| **Format** | **Name** | **Purpose** | **Used For** | **Format Type** | **Popularity** |
28+
|:-----------|:----------------------|:----------------------------------------------------|:-----------------------------------------------|:-----------------------------|:----------------|
29+
| `JWT` | JSON Web Token | Signed (and optionally encrypted) claims | ID tokens, access tokens, stateless sessions | Compact, signed data format | very high |
30+
| `JWE` | JSON Web Encryption | Encrypted JWT (confidential claims) | Privacy-sensitive data, secure token transport | Encrypted token format | niche |
31+
| `JWKS` | JSON Web Key Set | Public key distribution format | RS256/PS256 token verification | JSON container for keys | medium |
32+
2233

2334
---
2435

@@ -30,53 +41,104 @@ Or, create a new one by editing the header and payload and providing a secret.
3041
</tr>
3142
</thead>
3243
<tbody>
33-
<tr>
34-
<td style="vertical-align: top;">
35-
<textarea id="jwtInput"
36-
style="width: 100%; height: 100%; min-height: 460px; font-family: monospace; color: #789"
37-
class="mb-2"
38-
placeholder="Paste or generate JWT..."></textarea>
39-
</td>
40-
<td style="vertical-align: top; height: 100%;">
41-
<div style="display: flex; flex-direction: column; height: 100%;">
42-
<label class="mb-1 font-weight-bold">Header</label>
43-
<textarea id="jwtHeaderInput"
44-
class="w-100 mb-2"
45-
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
46-
placeholder='{"alg":"HS256","typ":"JWT"}'></textarea>
47-
48-
<label class="mb-1 font-weight-bold">Payload</label>
49-
<textarea id="jwtPayloadInput"
50-
class="w-100 mb-2"
51-
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
52-
placeholder='{"sub":"1234567890","name":"John Doe"}'>
53-
</textarea>
44+
<tr>
45+
<td style="vertical-align: top;">
46+
<textarea id="jwtInput"
47+
style="width: 100%; height: 100%; min-height: 460px; font-family: monospace; color: #789"
48+
class="mb-2"
49+
placeholder="Paste or generate JWT...">
50+
</textarea>
51+
</td>
52+
<td style="vertical-align: top; height: 100%;">
53+
<div style="display: flex; flex-direction: column; height: 100%;">
54+
<label class="mb-1 font-weight-bold">Header</label>
55+
<textarea id="jwtHeaderInput"
56+
class="w-100 mb-2"
57+
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
58+
placeholder='{"alg":"HS256","typ":"JWT"}'></textarea>
5459

55-
<label class="mb-1 font-weight-bold">Secret (for HS256)</label>
56-
<input id="jwtSecretInput"
57-
type="text"
58-
class="w-100 mb-2"
59-
style="font-family: monospace; color: #789" placeholder="your-secret-key" />
60+
<label class="mb-1 font-weight-bold">Payload</label>
61+
<textarea id="jwtPayloadInput"
62+
class="w-100 mb-2"
63+
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
64+
placeholder='{"sub":"1234567890","name":"John Doe"}'>
65+
</textarea>
66+
</div>
67+
</td>
68+
</tr>
69+
<tr>
70+
<td style="vertical-align: top; height: 100%;">
71+
<div style="display: flex; flex-direction: column; height: 100%;">
72+
<label class="mb-1 font-weight-bold">Algorithm</label>
73+
<select id="jwtAlgorithm" class="w-100 mb-2">
74+
<option value="HS256" selected>HS256 [HMAC+SHA-256]</option>
75+
<option value="HS384">HS384 [HMAC+SHA-384]</option>
76+
<option value="HS512">HS512 [HMAC+SHA-512]</option>
77+
<option value="RS256">RS256 [RSA+SHA-256]</option>
78+
<option value="RS384">RS384 [RSA+SHA-384]</option>
79+
<option value="RS512">RS512 [RSA+SHA-512]</option>
80+
<option value="ES256">ES256 [ECDSA+SHA-256)</option>
81+
<option value="ES384">ES384 [ECDSA+SHA-384]</option>
82+
<option value="ES512">ES512 [ECDSA+SHA-512]</option>
83+
<option value="PS256">PS256 [RSAPSS+SHA-256]</option>
84+
<option value="PS384">PS384 [RSAPSS+SHA-384]</option>
85+
<option value="PS512">PS512 [RSAPSS+SHA-512]</option>
86+
</select>
87+
</div>
88+
</td>
89+
<td style="vertical-align: bottom; height: 100%;">
90+
<div style="display: flex; flex-direction: column; height: 100%;">
91+
<label class="mb-1 font-weight-bold">Secret (for HS)</label>
92+
<input id="jwtSecretInput"
93+
type="text"
94+
class="w-100 mb-2"
95+
style="font-family: monospace; color: #789" placeholder="your-secret-key" />
6096

61-
<label class="mb-1 font-weight-bold">Secret Encoding</label>
62-
<select id="secretEncoding" class="mb-2">
63-
<option value="utf8">UTF-8</option>
64-
<option value="b64">Base64</option>
65-
<option value="hex">Hex</option>
66-
</select>
67-
</div>
68-
</td>
69-
</tr>
97+
<label class="mb-1 font-weight-bold">Secret Encoding</label>
98+
<select id="secretEncoding" class="mb-2">
99+
<option value="utf8">UTF-8</option>
100+
<option value="b64">Base64</option>
101+
<option value="hex">Hex</option>
102+
</select>
103+
</div>
104+
</td>
105+
</tr>
106+
<tr>
107+
<td style="vertical-align: bottom; height: 100%;">
108+
<div style="display: flex; flex-direction: column; height: 100%;">
109+
<label class="mb-1 font-weight-bold">Public Key (RS|ES|PS)</label>
110+
<textarea id="jwtPublicKeyInput"
111+
class="w-100 mb-2"
112+
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
113+
placeholder='{"alg":"HS256","typ":"JWT"}'>
114+
</textarea>
115+
</div>
116+
</td>
117+
<td style="vertical-align: bottom; height: 100%;">
118+
<div style="display: flex; flex-direction: column; height: 100%;">
119+
<label class="mb-1 font-weight-bold">Private Key (RS|ES|PS)</label>
120+
<textarea id="jwtPrivateKeyInput"
121+
class="w-100 mb-2"
122+
style="flex: 1; min-height: 200px; resize: vertical; font-family: monospace; color: #789;"
123+
placeholder='{"sub":"1234567890","name":"John Doe"}'>
124+
</textarea>
125+
</div>
126+
</td>
127+
128+
</tr>
129+
<tr>
130+
<td><label class="mb-1 font-weight-bold">* Public and Private keys must be in PEM format.</label></td>
131+
</tr>
70132
<tr>
71133
<td colspan="2">
72134
<div class="mt-2">
73135
<button id="decode-jwt" class="btn btn-blue mr-2">Decode</button>
74-
<button id="verify-jwt" class="btn btn-yellow mr-2">Verify Signature</button>
75-
<button id="encode-jwt" class="btn btn-green mr-2">Encode JWT</button>
136+
<button id="verify-jwt" class="btn btn-purple mr-2">Verify</button>
137+
<button id="encode-jwt" class="btn btn-green mr-2">Encode</button>
76138
</div>
77139
</td>
78140
</tr>
79-
<tr>
141+
<tr>
80142
<td colspan="2">
81143
<div id="jwtVerifyStatus" class="font-weight-bold mt-3 text-sm"></div>
82144
</td>

0 commit comments

Comments
 (0)