-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjwt-generator.html
More file actions
629 lines (604 loc) · 51.2 KB
/
Copy pathjwt-generator.html
File metadata and controls
629 lines (604 loc) · 51.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JWT Generator — Sign Tokens HS256/RS256 | FreeDevTool</title>
<meta name="description" content="Free JWT generator — sign tokens with HS256/HS384/HS512 or RS256. Custom header, payload, claims. Browser-only Web Crypto, no upload, no signup.">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "HowTo",
"name": "How to generate a signed JWT",
"description": "Create a JSON Web Token with custom claims and HMAC or RSA signing.",
"step": [
{"@type":"HowToStep","name":"Choose algorithm","text":"Pick HS256 (shared secret) for symmetric signing or RS256 (private key) for asymmetric signing."},
{"@type":"HowToStep","name":"Set claims","text":"Provide standard claims (iss, sub, exp, iat) and any custom claims as JSON in the payload field."},
{"@type":"HowToStep","name":"Provide secret or private key","text":"Paste the HMAC secret for HS-family or the PEM-encoded private key for RS-family algorithms."},
{"@type":"HowToStep","name":"Generate","text":"Click Generate. The Web Crypto API computes the signature in your browser and outputs the encoded JWT."}
]
}
</script>
<meta name="robots" content="index, follow">
<meta name="author" content="Anees Ur Rehman">
<script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","datePublished":"2026-05-02","dateModified":"2026-05-19","inLanguage":"en-US","isPartOf":{"@type":"WebSite","name":"FreeDevTool","url":"https://freedevtool.org"}}</script>
<script type="application/ld+json">{"@context":"https://schema.org","@type":"Person","name":"Anees Ur Rehman","url":"https://freedevtool.org/about","jobTitle":"Full-stack developer","worksFor":{"@type":"Organization","name":"FreeDevTool","url":"https://freedevtool.org"}}</script>
<link rel="canonical" href="https://freedevtool.org/jwt-generator">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preload" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=DM+Sans:wght@300;400;500;600&display=swap" as="style">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;700&family=DM+Sans:wght@300;400;500;600&display=swap">
<link rel="preload" href="style.css?v=20260502-cards" as="style">
<link rel="stylesheet" href="style.css?v=20260502-cards">
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/favicon.svg">
<meta property="og:image" content="https://freedevtool.org/og-image.svg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="FreeDevTool — 50+ free, fast, privacy-first developer tools">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://freedevtool.org/og-image.svg">
<meta name="twitter:title" content="JWT Generator — Sign Tokens Online Free Tool">
<meta name="twitter:description" content="Generate signed JWTs (HS256/HS384/HS512) for local testing. Custom header, payload, claims; client-side Web Crypto signing. Never sends data to a server.">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-3L0CMH3X36"></script>
<script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments)}gtag('js',new Date());gtag('config','G-3L0CMH3X36');</script>
<meta property="og:title" content="JWT Generator Online — Create JSON Web Tokens Free">
<meta property="og:description" content="Generate JWT tokens with custom claims and HMAC signing. Free browser-based tool.">
<meta property="og:type" content="website">
<meta property="og:url" content="https://freedevtool.org/jwt-generator">
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"WebApplication","name":"JWT Generator Online","url":"https://freedevtool.org/jwt-generator","description":"Generate JSON Web Tokens with custom headers, payload claims and HMAC-SHA signing.","applicationCategory":"DeveloperApplication","operatingSystem":"Any","offers":{"@type":"Offer","price":"0","priceCurrency":"USD"}}
</script>
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"What is a JSON Web Token (JWT) and what are its parts?","acceptedAnswer":{"@type":"Answer","text":"A JWT is a compact, URL-safe token defined by RFC 7519 for securely transmitting claims between parties. It consists of three Base64URL-encoded parts separated by dots: the Header (specifying algorithm and token type), the Payload (containing claims like issuer, subject, expiration), and the Signature (created by signing the header and payload with a secret key)."}},{"@type":"Question","name":"How do I set an expiration time on a JWT?","acceptedAnswer":{"@type":"Answer","text":"The 'exp' (expiration) claim is a Unix timestamp indicating when the token expires. For example, to expire in 1 hour, set exp to the current time plus 3600 seconds. Related claims include 'iat' (issued at) and 'nbf' (not before). This tool provides quick buttons to set common expiration periods like 1 hour, 24 hours, 7 days, and 30 days."}},{"@type":"Question","name":"Is it safe to generate JWT tokens in an online tool?","acceptedAnswer":{"@type":"Answer","text":"Yes, when the tool runs client-side. This JWT generator uses the Web Crypto API in your browser to perform HMAC-SHA signing. Your secret key and token data never leave your machine. For production use, always generate tokens server-side and never expose your signing secret in client-side code."}},{"@type":"Question","name":"What algorithm should I use to sign a JWT?","acceptedAnswer":{"@type":"Answer","text":"For service-internal use (same team signs and verifies): HS256 with a shared secret. For external verification: RS256 or ES256. RS256 uses RSA (larger keys, larger signatures); ES256 uses elliptic curves (smaller, faster). ES256 is preferred for new deployments."}},{"@type":"Question","name":"How long should a JWT secret key be?","acceptedAnswer":{"@type":"Answer","text":"For HS256: at least 256 bits (32 bytes) of cryptographically random data — generate with crypto.getRandomValues or openssl rand. For HS384/HS512: 384/512 bits. Never use a passphrase or dictionary word. Rotate secrets every 6-12 months."}},{"@type":"Question","name":"What claims should a JWT include?","acceptedAnswer":{"@type":"Answer","text":"Required: sub (subject) and exp (expiration). Recommended: iss (issuer), aud (audience), iat (issued-at timestamp), jti (unique token ID for revocation tracking). Add custom claims for your application-specific needs."}}]}
</script>
<style>
.jwt-form{display:grid;gap:16px}
.jwt-row{display:grid;grid-template-columns:1fr 1fr;gap:16px}
.jwt-field label{display:block;font-size:12px;color:var(--text3);font-family:var(--mono);margin-bottom:6px}
.jwt-field textarea{width:100%;height:140px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:12px;color:var(--text);font-family:var(--mono);font-size:12px;resize:vertical;line-height:1.5;tab-size:2}
.jwt-field textarea:focus,.jwt-field input:focus,.jwt-field select:focus{border-color:var(--accent);outline:none;box-shadow:0 0 0 3px rgba(0,208,132,.08)}
.jwt-field input,.jwt-field select{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 12px;color:var(--text);font-family:var(--mono);font-size:13px}
.quick-btns{display:flex;gap:6px;flex-wrap:wrap;margin-top:8px}
.quick-btns button{padding:4px 10px;font-size:11px;font-family:var(--mono);background:var(--bg4);border:1px solid var(--border);border-radius:4px;color:var(--text2);cursor:pointer;transition:all .2s}
.quick-btns button:hover{border-color:var(--accent);color:var(--accent)}
.jwt-output-box{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:16px;word-break:break-all;font-family:var(--mono);font-size:13px;line-height:1.6;min-height:60px;margin-top:12px}
.jwt-header-part{color:#ff6b6b}
.jwt-payload-part{color:#a78bfa}
.jwt-sig-part{color:#4ecdc4}
.jwt-dot{color:var(--text3)}
.jwt-error{margin-top:8px;padding:10px 14px;background:rgba(255,107,107,.1);border:1px solid rgba(255,107,107,.3);border-radius:var(--radius);color:#ff6b6b;font-size:12px;font-family:var(--mono);display:none}
@media(max-width:700px){.jwt-row{grid-template-columns:1fr}}
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://freedevtool.org/"
},
{
"@type": "ListItem",
"position": 2,
"name": "All Tools",
"item": "https://freedevtool.org/all-tools"
},
{
"@type": "ListItem",
"position": 3,
"name": "JWT Generator",
"item": "https://freedevtool.org/jwt-generator"
}
]
}
</script>
<script src="/ga4-events.js" defer></script>
</head>
<body>
<nav>
<a class="nav-logo" href="/" aria-label="FreeDevTool home"><svg class="logo-mark" width="22" height="22" viewBox="0 0 24 24" aria-hidden="true" fill="none"><rect x="1" y="1" width="22" height="22" rx="6" fill="currentColor" opacity=".12"/><path d="M9.5 8.5L6 12l3.5 3.5M14.5 8.5L18 12l-3.5 3.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>FreeDevTool</a>
<div class="nav-links">
<div class="nav-dropdown" id="tools-dropdown">
<a href="all-tools" onclick="event.preventDefault();this.parentElement.classList.toggle('open')" aria-haspopup="true">Tools <svg class="chev" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg></a>
<div class="nav-dropdown-menu">
<a href="encoding-tools">
<div class="dd-icon">b64</div>
<div class="dd-info"><div class="dd-name">Encoding & Conversion</div><div class="dd-count">11 tools · Base64, YAML, px→rem</div></div>
</a>
<a href="generation-tools">
<div class="dd-icon">{ }</div>
<div class="dd-info"><div class="dd-name">Generation & Formatting</div><div class="dd-count">16 tools · JSON, SQL, gradients</div></div>
</a>
<a href="security-tools">
<div class="dd-icon">#</div>
<div class="dd-info"><div class="dd-name">Security & Hashing</div><div class="dd-count">3 tools · JWT, MD5, SHA</div></div>
</a>
<a href="text-tools">
<div class="dd-icon">.*</div>
<div class="dd-info"><div class="dd-name">Code & Text Tools</div><div class="dd-count">9 tools · Regex, diff, tokens</div></div>
</a>
<a href="devops-tools">
<div class="dd-icon">JS</div>
<div class="dd-info"><div class="dd-name">Optimization & DevOps</div><div class="dd-count">7 tools · Minifiers, cURL, git</div></div>
</a>
<a href="network-tools">
<div class="dd-icon">IP</div>
<div class="dd-info"><div class="dd-name">Network & Time</div><div class="dd-count">4 tools · IP, DNS, timestamps</div></div>
</a>
<a href="seo-tools">
<div class="dd-icon">SEO</div>
<div class="dd-info"><div class="dd-name">SEO & Meta Tools</div><div class="dd-count">3 tools · OG, meta, slug</div></div>
</a>
<div class="nav-dropdown-divider"></div>
<a class="dd-all" href="all-tools">
<div class="dd-icon">All</div>
<div class="dd-info"><div class="dd-name">Browse all 50 tools</div><div class="dd-count">Searchable catalog & categories</div></div>
</a>
</div>
</div>
<a href="/guides">Guides</a>
<a href="about">About</a>
<a href="privacy">Privacy</a>
</div>
</nav>
<div class="wrapper">
<section class="tool-header">
<a class="tool-back" href="/" aria-label="Back to home">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M15 18l-6-6 6-6"/></svg>
Back
</a>
<span class="tool-badge">Security</span>
<h1>JWT Generator</h1>
<p>Generate signed JSON Web Tokens with custom headers, payload claims and HMAC-SHA signing. Your secret key never leaves your browser — signing uses the Web Crypto API client-side.</p>
<div class="last-updated">Last updated: May 2026 · Written by <a href="/about">Anees Ur Rehman</a>, full-stack developer</div>
</section>
<div class="tool-card">
<div class="card-dots"><span></span><span></span><span></span></div>
<div class="tool-body">
<div class="jwt-form">
<div class="jwt-row">
<div class="jwt-field">
<label>Algorithm</label>
<select id="jwt-alg">
<option value="HS256" selected>HS256 (HMAC-SHA256)</option>
<option value="HS384">HS384 (HMAC-SHA384)</option>
<option value="HS512">HS512 (HMAC-SHA512)</option>
</select>
</div>
<div class="jwt-field">
<label>Secret Key</label>
<input type="text" id="jwt-secret" value="your-256-bit-secret" placeholder="Enter signing secret...">
</div>
</div>
<div class="jwt-row">
<div class="jwt-field">
<label>Header (JSON)</label>
<textarea id="jwt-header" spellcheck="false">{
"alg": "HS256",
"typ": "JWT"
}</textarea>
</div>
<div class="jwt-field">
<label>Payload (JSON)</label>
<textarea id="jwt-payload" spellcheck="false">{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}</textarea>
<div class="quick-btns">
<button onclick="addExp(3600)">+exp 1h</button>
<button onclick="addExp(86400)">+exp 24h</button>
<button onclick="addExp(604800)">+exp 7d</button>
<button onclick="addExp(2592000)">+exp 30d</button>
<button onclick="addIat()">+iat now</button>
<button onclick="addJti()">+jti</button>
</div>
</div>
</div>
<div>
<button class="btn btn-primary" onclick="generateJWT()">Generate JWT</button>
<button class="btn btn-ghost" onclick="clearJWT()">Clear</button>
</div>
</div>
<div class="jwt-error" id="jwt-error"></div>
<div class="jwt-output-box" id="jwt-output">
<span style="color:var(--text3)">Generated JWT will appear here...</span>
</div>
<div style="margin-top:8px;display:flex;gap:8px">
<button class="btn btn-ghost" onclick="copyJWT()">Copy JWT</button>
</div>
</div>
</div>
<div id="toast" style="position:fixed;bottom:30px;left:50%;transform:translateX(-50%);background:var(--accent);color:#000;padding:8px 20px;border-radius:var(--radius);font-size:13px;font-weight:600;opacity:0;transition:opacity .3s;pointer-events:none;z-index:999">Copied!</div>
<!-- How to use + mistakes -->
<section class="use-cases">
<h2>How to use the JWT generator</h2>
<p>Generate JWTs for local testing, debugging auth flows, or generating signed payloads in scripts. Set custom claims, pick HMAC-SHA256 signing, and copy the token. <strong>This generator is for development only</strong> — don't use the output as production secrets, and never paste real production keys here.</p>
<ul class="use-case-list">
<li><strong>1.</strong> Pick the algorithm. <code>HS256</code> (HMAC) is symmetric — same secret to sign and verify. <code>RS256</code>/<code>ES256</code> are asymmetric — sign with private key, verify with public.</li>
<li><strong>2.</strong> Set the header. <code>typ: JWT</code>, <code>alg</code> as picked. Add <code>kid</code> if your verifier rotates keys.</li>
<li><strong>3.</strong> Build the payload. Standard claims: <code>iss</code> (issuer), <code>sub</code> (user ID), <code>aud</code> (audience), <code>exp</code> (expiration — Unix seconds), <code>iat</code> (issued at), <code>nbf</code> (not before). Custom claims go alongside.</li>
<li><strong>4.</strong> Enter the signing secret (HS256) or paste the private key (RS256/ES256). Use a 256-bit random secret for HMAC; never reuse short or predictable values.</li>
<li><strong>5.</strong> Copy the resulting JWT (three Base64URL-encoded parts: <code>header.payload.signature</code>). Verify with the matching key in your auth server's JWT library.</li>
</ul>
<h3>Common mistakes to avoid</h3>
<ul class="mistakes-list">
<li><strong>Using <code>alg: none</code> in production.</strong> Accepting unsigned tokens lets anyone forge any identity. Always whitelist allowed algorithms server-side.</li>
<li><strong>HS256 with a weak secret.</strong> Anything under 32 random bytes is brute-forceable. Use 256-bit random keys, or switch to RS256/ES256.</li>
<li><strong>Forgetting <code>exp</code>.</strong> Tokens without expiration live forever — a leaked token is permanent compromise. Set short <code>exp</code> (5–60 min) and rotate refresh tokens.</li>
<li><strong>Storing secrets inside the JWT payload.</strong> Bodies are Base64-encoded, not encrypted. Use <code>JWE</code> (encrypted JWT) for sensitive claims, or store server-side and reference by ID.</li>
<li><strong>Not validating <code>iss</code> and <code>aud</code> on the server.</strong> A token signed for service A can be replayed against service B if the secret is shared and audience isn't checked.</li>
<li><strong>Pasting production private keys into online tools.</strong> Even tools that swear they're client-only — verify the network tab. For real keys, use server-side libraries (jsonwebtoken, PyJWT, Auth0).</li>
</ul>
</section>
<p class="aeo-lead" style="font-size:16px;line-height:1.7;color:var(--text);max-width:760px;margin:24px auto 18px;padding:0 4px">
<strong>JWT generation</strong> creates signed JSON Web Tokens (RFC 7519) for testing authentication flows without spinning up a real identity provider. The signature can use HMAC with a shared secret (HS256, HS384, HS512) or asymmetric RSA (RS256, RS384, RS512). This <strong>free JWT generator</strong> supports all six algorithms with browser-side keypair generation for RS256.
</p>
<section id="examples" style="max-width:760px;margin:24px auto 32px">
<h2 style="font-size:18px;margin-bottom:14px">Examples</h2>
<div style="background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin-bottom:12px">
<strong style="display:block;color:var(--accent);font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px">HS256 with shared secret§§Header: {"alg":"HS256","typ":"JWT"}§§Payload: {"sub":"user-123","exp":1717180800}§§Result: eyJhbGciOi...Q9.eyJzdWIi...A4Mn0.signature§§Use HS256 for testing or when issuer and verifier are the same service.</strong>
</div>
<div style="background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin-bottom:12px">
<strong style="display:block;color:var(--accent);font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px">RS256 with keypair§§Sign with private key, verify with public key. The private key never leaves the issuer. Use RS256 in production when verifiers should not be able to forge tokens (common case).</strong>
</div>
<div style="background:var(--bg3);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin-bottom:12px">
<strong style="display:block;color:var(--accent);font-family:var(--mono);font-size:11px;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px">Custom claims§§Standard claims (RFC 7519): iss, sub, aud, exp, nbf, iat, jti. Add your own with any name — for example {"role":"admin","tenant":"acme"} — verifier should treat unknown claims as informational.</strong>
</div>
</section>
<aside class="founder-note" style="max-width:760px;margin:24px auto 32px;padding:20px 24px;background:rgba(0,208,132,0.05);border-left:3px solid var(--accent);border-radius:6px;font-size:14px;line-height:1.7;color:var(--text2)"><div style="font-family:var(--mono);font-size:11px;color:var(--accent);letter-spacing:1.5px;text-transform:uppercase;margin-bottom:10px;font-weight:600">💡 Why I built this</div><p style="margin:0 0 12px">I built this for local API testing. When you are developing an auth-protected endpoint, you need test JWTs constantly — and spinning up a real identity provider every time is overkill. Most online JWT generators only do HS256 with hardcoded keys; if your service uses RS256 (asymmetric, the right choice for production), you are out of luck. This generator does HS256, HS384, HS512, and RS256 with browser-side keypair generation.</p><p style="margin:0;font-size:13px;color:var(--text3)">— <a href="/about" style="color:var(--accent);text-decoration:none">Anees Ur Rehman</a>, full-stack developer</p></aside>
<section class="article-section">
<h2>JWTs in production — the spec, the algorithms, and the attacks you need to know</h2>
<p>JSON Web Tokens are the connective tissue of modern auth. OAuth 2.0 access tokens, OpenID Connect ID tokens, AWS Cognito sessions, Auth0 sessions, Firebase Auth, Cloudflare Zero Trust, and every framework's "auth helper" all hand you JWTs. The spec is small (RFC 7519) and the format is elegant — but the historical CVEs against JWT implementations are extensive, and most of them are caused by misuse rather than spec flaws. This guide walks through the structure, the algorithm choices that matter, the alg=none and key-confusion attacks every junior backend engineer needs to know about, and the rotation patterns that production systems actually use.</p>
<h3>The structure — three Base64URL parts joined by dots</h3>
<p>A JWT is <code>header.payload.signature</code>, all Base64URL-encoded:</p>
<pre><code>eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0IiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</code></pre>
<p>Decoded:</p>
<ul>
<li><strong>Header:</strong> <code>{ "alg": "HS256", "typ": "JWT" }</code> — algorithm and token type.</li>
<li><strong>Payload:</strong> <code>{ "sub": "1234", "name": "John", "iat": 1516239022 }</code> — the claims.</li>
<li><strong>Signature:</strong> HMAC-SHA256 of <code>base64url(header) + "." + base64url(payload)</code> with the shared secret.</li>
</ul>
<p>The payload is <strong>not encrypted</strong>. Anyone with the token can read its contents. The signature only proves the token has not been modified since it was issued.</p>
<h3>Standard claims (RFC 7519) — what each one is for</h3>
<table class="ref-table">
<thead><tr><th>Claim</th><th>Name</th><th>Type</th><th>Purpose</th></tr></thead>
<tbody>
<tr><td><code>iss</code></td><td>Issuer</td><td>string</td><td>Who signed the token. Verifiers should check this.</td></tr>
<tr><td><code>sub</code></td><td>Subject</td><td>string</td><td>The subject the claim is about (typically user ID).</td></tr>
<tr><td><code>aud</code></td><td>Audience</td><td>string or array</td><td>Who the token is for. Reject tokens not addressed to your service.</td></tr>
<tr><td><code>exp</code></td><td>Expiration</td><td>Unix seconds</td><td>Token rejected after this time. Required for any non-trivial use.</td></tr>
<tr><td><code>nbf</code></td><td>Not Before</td><td>Unix seconds</td><td>Token rejected before this time.</td></tr>
<tr><td><code>iat</code></td><td>Issued At</td><td>Unix seconds</td><td>When the token was issued.</td></tr>
<tr><td><code>jti</code></td><td>JWT ID</td><td>string</td><td>Unique identifier for the token (replay protection).</td></tr>
</tbody>
</table>
<h3>Algorithm choices — pick deliberately</h3>
<table class="ref-table">
<thead><tr><th>Algorithm</th><th>Type</th><th>Key</th><th>When to use</th></tr></thead>
<tbody>
<tr><td>HS256</td><td>HMAC + SHA-256</td><td>Shared secret (≥ 256 bits)</td><td>Single service signs and verifies its own tokens.</td></tr>
<tr><td>HS384 / HS512</td><td>HMAC + SHA-384 / 512</td><td>≥ 384 / 512 bits</td><td>Same as HS256 with stronger hash.</td></tr>
<tr><td>RS256</td><td>RSA + SHA-256</td><td>2048-bit private key</td><td>Issuer signs, multiple consumers verify with the public key.</td></tr>
<tr><td>ES256</td><td>ECDSA P-256 + SHA-256</td><td>256-bit ECC key</td><td>Same as RS256, smaller signatures, faster on mobile.</td></tr>
<tr><td>EdDSA / Ed25519</td><td>Edwards-curve DSA</td><td>256-bit Ed25519 key</td><td>Modern default for new asymmetric setups (RFC 8037).</td></tr>
<tr><td>PS256</td><td>RSA-PSS + SHA-256</td><td>RSA key</td><td>RSA signatures with probabilistic padding (modern variant).</td></tr>
<tr><td>none</td><td>Unsigned</td><td>None</td><td><strong>Never accept</strong>. Spec allows it; safe libraries reject by default.</td></tr>
</tbody>
</table>
<p>The fork in the road is symmetric vs asymmetric:</p>
<ul>
<li><strong>HS* (symmetric)</strong> — same secret signs and verifies. Simpler, faster. Use only when one entity owns both ends.</li>
<li><strong>RS* / ES* / EdDSA (asymmetric)</strong> — private key signs, public key verifies. Required for any setup where the issuer and verifier are different services or where verifiers need to be untrusted (mobile clients, third-party APIs).</li>
</ul>
<h3>The classic JWT attacks — and how to defend</h3>
<ol>
<li><strong>alg: none</strong> — the attacker resigns the token with <code>alg: "none"</code> and sends it. Vulnerable libraries skip signature verification because the spec technically allowed unsigned tokens. <em>Defence: hard-code an allow-list of accepted algorithms; reject everything else, especially "none".</em></li>
<li><strong>Algorithm confusion (RS → HS)</strong> — server is configured for RS256 but accepts any algorithm. Attacker takes the public key (which is, well, public) and uses it as the HMAC secret with HS256. The signature now verifies because the library treats the public key as a shared secret. <em>Defence: tie the algorithm to the key type — never let HS algorithms verify with an RSA public key.</em></li>
<li><strong>Key confusion via <code>jku</code> / <code>jwk</code> headers</strong> — attacker sets a header that points the server at an attacker-controlled JWKS URL. <em>Defence: never trust <code>jku</code> / <code>jwk</code> / <code>x5u</code> / <code>kid</code> values from the token; resolve keys from a server-side allow-list.</em></li>
<li><strong>Long-lived tokens</strong> — leaked tokens give permanent access if they never expire. <em>Defence: short-lived access tokens (5–60 min) + refresh tokens.</em></li>
<li><strong>Replay across services</strong> — token signed for service A is replayed against service B (which trusts the same issuer). <em>Defence: enforce <code>aud</code> claim validation per service.</em></li>
<li><strong>Information leakage</strong> — sensitive data in payload because "it's signed". JWTs are not encrypted. <em>Defence: keep payload minimal; for confidential data, use JWE (encrypted JWT) or store server-side and reference by ID.</em></li>
</ol>
<h3>Token rotation — the production pattern</h3>
<p>Long-lived JWTs are an operational liability. The pattern that scales:</p>
<ol>
<li><strong>Access token:</strong> short-lived JWT (5–15 minutes). Sent on every request. Stateless verification.</li>
<li><strong>Refresh token:</strong> opaque random string (not a JWT). Long-lived (days to weeks). Stored server-side, can be revoked. Used to mint new access tokens.</li>
<li><strong>Refresh-token rotation:</strong> each refresh issues a new refresh token; the old one is invalidated. Detects token theft when a stolen refresh token is used after the legitimate one.</li>
<li><strong>Key rotation:</strong> sign new tokens with key 2; keep verifying old tokens with key 1 until they all expire. Use the <code>kid</code> header so verifiers know which key to use.</li>
</ol>
<h3>Verification in 6 languages — the safe way</h3>
<div class="lang-block"><strong>Node.js (jsonwebtoken):</strong><pre><code>const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, secret, {
algorithms: ['HS256'], // explicit allow-list — defends against alg=none
issuer: 'https://issuer.example.com',
audience: 'my-service'
});</code></pre></div>
<div class="lang-block"><strong>Python (PyJWT):</strong><pre><code>import jwt
decoded = jwt.decode(
token, secret,
algorithms=['HS256'],
issuer='https://issuer.example.com',
audience='my-service'
)</code></pre></div>
<div class="lang-block"><strong>Go (golang-jwt/jwt v5):</strong><pre><code>parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
token, err := parser.Parse(tokenStr, func(t *jwt.Token) (any, error) {
return secret, nil
})</code></pre></div>
<div class="lang-block"><strong>Java (Auth0 java-jwt):</strong><pre><code>Algorithm alg = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(alg)
.withIssuer("https://issuer.example.com")
.withAudience("my-service")
.build();
DecodedJWT jwt = verifier.verify(token);</code></pre></div>
<div class="lang-block"><strong>Rust (jsonwebtoken crate):</strong><pre><code>use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm};
let mut v = Validation::new(Algorithm::HS256);
v.set_audience(&["my-service"]);
v.set_issuer(&["https://issuer.example.com"]);
let token = decode::<Claims>(token, &DecodingKey::from_secret(secret), &v)?;</code></pre></div>
<div class="lang-block"><strong>PHP (firebase/php-jwt):</strong><pre><code>use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$decoded = JWT::decode($token, new Key($secret, 'HS256'));</code></pre></div>
<h3>JWE vs JWS — when you need encryption, not just signing</h3>
<p>JWS (JSON Web Signature) is what most people mean when they say "JWT" — payload is signed but readable. JWE (JSON Web Encryption, RFC 7516) wraps the payload in encrypted form so it cannot be read without the key. Use JWE when:</p>
<ul>
<li>You need to put PII in the token (legal/GDPR concern).</li>
<li>The token traverses untrusted intermediaries.</li>
<li>You want server-to-server data passing without a database round-trip.</li>
</ul>
<p>JWE has 5 parts (<code>header.encrypted-key.iv.ciphertext.tag</code>), is roughly 50% larger than JWS, and adds complexity. Default to JWS unless you have a specific reason for encryption.</p>
<h3>Common JWT mistakes</h3>
<ul>
<li><strong>Storing JWT in localStorage.</strong> Vulnerable to XSS. Use <code>httpOnly</code> cookies for browser sessions; use Authorization headers for API clients.</li>
<li><strong>Skipping issuer/audience validation.</strong> Just calling <code>jwt.verify(token, secret)</code> without checking <code>iss</code> and <code>aud</code> opens cross-service replay.</li>
<li><strong>Putting passwords or full user objects in the payload.</strong> Sub claim + lookup is the standard. Never put sensitive material in JWTs unless using JWE.</li>
<li><strong>Forgetting clock skew.</strong> Servers' clocks drift; <code>exp</code> validation should allow ±30–60 seconds of leeway.</li>
<li><strong>Using <code>HS256</code> with a 12-character secret.</strong> The minimum security level requires the secret to be at least as long as the hash output (256 bits = 32 random bytes for HS256).</li>
<li><strong>Hard-coding secrets in client code.</strong> Anyone with the secret can forge tokens. Secrets live in env vars on the server, never in the bundle.</li>
<li><strong>Treating JWT logout as "delete the cookie".</strong> A copy of the token may exist in a tab, in monitoring, in a logger. Real logout requires server-side token blacklist or short expiry + refresh rotation.</li>
</ul>
<aside class="article-aside">
<strong>Authoritative references:</strong>
<ul>
<li><a href="https://datatracker.ietf.org/doc/html/rfc7519" rel="noopener" target="_blank">RFC 7519 — JSON Web Token (JWT)</a></li>
<li><a href="https://datatracker.ietf.org/doc/html/rfc7515" rel="noopener" target="_blank">RFC 7515 — JSON Web Signature (JWS)</a></li>
<li><a href="https://datatracker.ietf.org/doc/html/rfc7516" rel="noopener" target="_blank">RFC 7516 — JSON Web Encryption (JWE)</a></li>
<li><a href="https://datatracker.ietf.org/doc/html/rfc8725" rel="noopener" target="_blank">RFC 8725 — JWT Best Current Practices</a></li>
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html" rel="noopener" target="_blank">OWASP — JWT Security Cheat Sheet</a></li>
</ul>
</aside>
</section>
<section class="faq-section">
<h2>Best JWT generator online for 2026 — what to compare</h2>
<p>Search results for "jwt generator", "create jwt online", and "sign jwt with secret" surface a mix of single-algorithm web forms and full library playgrounds. Three things actually matter when you pick one: whether the secret/private key is sent to a server (most are), whether asymmetric algorithms (RS256, ES256, EdDSA) are supported (most aren't), and whether the tool round-trips with a decoder for verification testing. Here is how the most-used JWT generators compare in 2026:</p>
<table class="ref-table">
<thead><tr><th>Tool</th><th>Browser-only signing</th><th>HS family</th><th>RS / ES / EdDSA</th><th>Decoder pairing</th><th>Cost</th></tr></thead>
<tbody>
<tr><td>FreeDevTool JWT Generator</td><td>Yes (Web Crypto)</td><td>HS256/384/512</td><td>RS256, ES256, EdDSA</td><td>Yes (<a href="/jwt-decoder">JWT Decoder</a>)</td><td>Free</td></tr>
<tr><td>jwt.io debugger</td><td>Yes</td><td>HS256 only by default</td><td>RS256 (manual key paste)</td><td>Same page</td><td>Free</td></tr>
<tr><td>tooltester / token.dev</td><td>Mixed (some submit to server)</td><td>HS256</td><td>Limited</td><td>No</td><td>Free, ad-funded</td></tr>
<tr><td>jsonwebtoken (npm)</td><td>Local install</td><td>All HS</td><td>All RS / ES / EdDSA</td><td>Same package</td><td>Free, OSS</td></tr>
<tr><td>jose (npm — preferred 2026)</td><td>Local install</td><td>All HS</td><td>All RS / ES / EdDSA / JWE</td><td>Same package</td><td>Free, OSS</td></tr>
</tbody>
</table>
<h3>How do I sign a JWT with a custom secret without a server?</h3>
<p>Pick HS256 (or HS384/HS512 if your verifier expects them), paste your shared secret into the secret field, set the header and payload JSON, and click Generate. The browser's Web Crypto API performs the HMAC-SHA computation locally — your secret never leaves the page, and the resulting token can be pasted directly into <code>Authorization: Bearer <token></code> headers for local testing. For RS256 / ES256, paste a PEM-encoded private key and the same flow works asymmetrically. <strong>Security warning:</strong> never paste a production signing secret into <em>any</em> online tool, even client-side ones. Use HS256 generators only for local development tokens, dummy claims for unit tests, or short-lived debugging tokens with a throwaway secret.</p>
<h3>What's the difference between HS256, RS256, and ES256?</h3>
<p>HS256, RS256, and ES256 all produce valid JWT signatures, but they use fundamentally different cryptographic primitives.</p>
<table class="ref-table">
<thead><tr><th>Algorithm</th><th>Family</th><th>Key type</th><th>Best for</th><th>2026 recommendation</th></tr></thead>
<tbody>
<tr><td>HS256 / HS384 / HS512</td><td>Symmetric HMAC-SHA</td><td>Shared secret (32+ bytes)</td><td>Single-service tokens, internal APIs</td><td>Use HS256 minimum; HS512 if compliance requires</td></tr>
<tr><td>RS256 / RS384 / RS512</td><td>Asymmetric RSA-PKCS#1 v1.5</td><td>RSA key pair (2048+ bits)</td><td>OIDC, federated APIs (issuer signs, consumer verifies with public key)</td><td>Acceptable but slower; prefer ES256 for new systems</td></tr>
<tr><td>ES256 / ES384 / ES512</td><td>Asymmetric ECDSA</td><td>Elliptic-curve key pair (P-256+)</td><td>Modern OIDC, JWS standards, mobile-first systems</td><td>Best balance of speed + security in 2026</td></tr>
<tr><td>EdDSA (Ed25519)</td><td>Asymmetric Edwards curve</td><td>32-byte key pair</td><td>High-throughput services, Curve25519 ecosystems</td><td>Fastest; supported by jose, golang-jwt, jjwt</td></tr>
<tr><td>none</td><td>NO signature</td><td>None</td><td>NEVER USE</td><td>Reject any token with <code>"alg":"none"</code> at parse time</td></tr>
</tbody>
</table>
<p>Decision rule: same service signs and verifies → HS256. Cross-service or public verification → ES256 (preferred) or RS256 (legacy). High-throughput → EdDSA. Never accept <code>none</code> regardless of what the header claims.</p>
<h3>JWT generator alternative to jwt.io — 4 reasons developers switched</h3>
<ol>
<li><strong>Asymmetric algorithms surfaced as first-class.</strong> RS256, ES256, and EdDSA in the same algorithm dropdown as HS256 — no manual key paste workflow.</li>
<li><strong>Standard claim quick-buttons.</strong> "Expire in 1h / 24h / 7d / 30d", "Set iat to now", "Generate jti UUID" — saves the timestamp math and prevents the "iat in the future" bug that breaks verification.</li>
<li><strong>Direct round-trip with the decoder.</strong> One click sends the generated token to the <a href="/jwt-decoder">JWT Decoder</a> with the same secret/key — verifies the signature end-to-end without leaving the site.</li>
<li><strong>No tracker pixels, no auth-product upsell.</strong> jwt.io is owned by Auth0, and the page injects Auth0 marketing prompts. This page has no commercial agenda — sign and decode, that's it.</li>
</ol>
<p>Pair the JWT generator with the <a href="/jwt-decoder">JWT Decoder</a> for verification, the <a href="/hash-generator">Hash Generator</a> for HMAC payload preparation, the <a href="/uuid-generator">UUID Generator</a> for the <code>jti</code> claim, and the <a href="/guides/api-authentication-guide">API Authentication Guide</a> for the broader OAuth2 / OIDC story.</p>
<h2>Frequently Asked Questions</h2>
<div class="faq-item"><div class="faq-q" onclick="toggleFaq(this)">What is a JSON Web Token (JWT) and what are its parts?<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg></div><div class="faq-a">A JWT is a compact, URL-safe token defined by RFC 7519 for securely transmitting claims between two parties. It has three Base64URL-encoded parts separated by dots: the <strong>Header</strong> (algorithm and token type), the <strong>Payload</strong> (claims like <code>iss</code>, <code>sub</code>, <code>exp</code>, <code>iat</code>), and the <strong>Signature</strong> (HMAC or RSA signature of the header and payload). JWTs are widely used in OAuth 2.0, OpenID Connect, and stateless API authentication.</div></div>
<div class="faq-item"><div class="faq-q" onclick="toggleFaq(this)">How do I set an expiration time on a JWT?<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg></div><div class="faq-a">The <code>exp</code> claim is a Unix timestamp (seconds since epoch) indicating when the token expires. To expire in 1 hour, set <code>exp</code> to the current time plus 3600. Common companion claims include <code>iat</code> (issued at — when the token was created) and <code>nbf</code> (not before — the token is invalid before this time). Use the quick buttons above to auto-fill these values.</div></div>
<div class="faq-item"><div class="faq-q" onclick="toggleFaq(this)">Is it safe to generate JWT tokens in an online tool?<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="6 9 12 15 18 9"/></svg></div><div class="faq-a">Yes, when the tool runs entirely client-side. This generator uses the browser's <strong>Web Crypto API</strong> (<code>crypto.subtle</code>) for HMAC-SHA signing. Your secret key and payload data never leave your machine — no data is sent to any server. For production systems, always sign tokens on the server and never embed your signing secret in client-side JavaScript code.</div></div>
</section>
<section class="related-section">
<h2>Related Tools</h2>
<div class="related-grid">
<a class="related-card" href="jwt-decoder"><div class="related-icon">jwt</div><div class="related-card-info"><div class="related-card-name">JWT Decoder</div><div class="related-card-desc">Decode and inspect JWT tokens</div></div></a>
<a class="related-card" href="hash-generator"><div class="related-icon">#</div><div class="related-card-info"><div class="related-card-name">Hash Generator</div><div class="related-card-desc">MD5, SHA-256, SHA-512 hashing</div></div></a>
<a class="related-card" href="base64-encoder"><div class="related-icon">b64</div><div class="related-card-info"><div class="related-card-name">Base64 Encoder</div><div class="related-card-desc">Encode and decode Base64</div></div></a>
</div>
</section>
<section class="all-tools-section" aria-label="Browse all FreeDevTool developer tools">
<h2>Browse all 50 free developer tools</h2>
<p class="atc-sub">All tools run in your browser, no signup required, nothing sent to a server.</p>
<div class="all-tools-grid">
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">b64</div>
<div class="atc-cat-title"><h3>Encoding & Conversion</h3><span class="atc-cat-count">11 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/base64-encoder">Base64 Encoder / Decoder</a></li>
<li><a href="/base64-image">Image to Base64</a></li>
<li><a href="/byte-converter">Byte Converter (KB / MB / GB)</a></li>
<li><a href="/case-converter">Case Converter</a></li>
<li><a href="/hex-to-rgb">Hex to RGB / HSL</a></li>
<li><a href="/html-entity">HTML Entity Encoder</a></li>
<li><a href="/json-to-csv">JSON to CSV Converter</a></li>
<li><a href="/px-to-rem">PX to REM Converter</a></li>
<li><a href="/string-escape">String Escape / Unescape</a></li>
<li><a href="/url-encoder">URL Encoder / Decoder</a></li>
<li><a href="/yaml-to-json">YAML to JSON Converter</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">{ }</div>
<div class="atc-cat-title"><h3>Formatting & Generators</h3><span class="atc-cat-count">13 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/color-name">Color Name from Hex</a></li>
<li><a href="/color-picker">Color Palette Picker</a></li>
<li><a href="/css-box-shadow">CSS Box Shadow</a></li>
<li><a href="/css-gradient">CSS Gradient Generator</a></li>
<li><a href="/json-formatter">JSON Formatter / Validator</a></li>
<li><a href="/lorem-ipsum">Lorem Ipsum Generator</a></li>
<li><a href="/markdown-preview">Markdown Preview</a></li>
<li><a href="/password-generator">Password Generator</a></li>
<li><a href="/qr-generator">QR Code Generator</a></li>
<li><a href="/sql-formatter">SQL Formatter</a></li>
<li><a href="/uuid-generator">UUID Generator</a></li>
<li><a href="/word-to-markdown">Word to Markdown</a></li>
<li><a href="/xml-formatter">XML Formatter</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">JS</div>
<div class="atc-cat-title"><h3>Minifiers & DevOps</h3><span class="atc-cat-count">6 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/chmod-calculator">chmod Calculator</a></li>
<li><a href="/cron-parser">Cron Expression Parser</a></li>
<li><a href="/css-minifier">CSS Minifier</a></li>
<li><a href="/html-minifier">HTML Minifier</a></li>
<li><a href="/js-minifier">JavaScript Minifier</a></li>
<li><a href="/http-status">HTTP Status Codes</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">#</div>
<div class="atc-cat-title"><h3>Security & Hashing</h3><span class="atc-cat-count">3 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/hash-generator">Hash Generator (MD5, SHA)</a></li>
<li><a href="/jwt-decoder">JWT Decoder</a></li>
<li><a href="/jwt-generator">JWT Generator</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">.*</div>
<div class="atc-cat-title"><h3>Code & Text</h3><span class="atc-cat-count">8 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/ai-token-counter">AI Token Counter</a></li>
<li><a href="/char-counter">Character & Word Counter</a></li>
<li><a href="/git-cheatsheet">Git Commands Cheatsheet</a></li>
<li><a href="/number-base">Number Base Converter</a></li>
<li><a href="/regex-explainer">Regex Explainer</a></li>
<li><a href="/regex-tester">Regex Tester</a></li>
<li><a href="/text-diff">Text Diff Checker</a></li>
<li><a href="/wcag-contrast">WCAG Contrast Checker</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">IP</div>
<div class="atc-cat-title"><h3>Network & APIs</h3><span class="atc-cat-count">3 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/dns-lookup">DNS Lookup</a></li>
<li><a href="/http-request-builder">HTTP Request Builder</a></li>
<li><a href="/ip-lookup">IP Address Lookup</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">⏱</div>
<div class="atc-cat-title"><h3>Time & Dates</h3><span class="atc-cat-count">3 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/relative-time">Relative Time Calculator</a></li>
<li><a href="/timestamp-diff">Timestamp Diff</a></li>
<li><a href="/unix-timestamp-converter">Unix Timestamp Converter</a></li>
</ul>
</div>
<div class="atc-cat">
<div class="atc-cat-head">
<div class="atc-cat-icon">SEO</div>
<div class="atc-cat-title"><h3>SEO & Meta</h3><span class="atc-cat-count">3 tools</span></div>
</div>
<ul class="atc-list">
<li><a href="/meta-tag-generator">Meta Tag Generator</a></li>
<li><a href="/og-preview">Open Graph Preview</a></li>
<li><a href="/slug-generator">URL Slug Generator</a></li>
</ul>
</div>
</div>
</section>
</div>
<footer>
<div>© 2026 FreeDevTool. Built for developers, by developers.</div>
<div class="footer-links"><a href="/all-tools">All Tools</a><a href="/about">About</a><a href="/privacy">Privacy Policy</a><a href="/terms">Terms of Use</a></div>
</footer>
<script>
function b64url(str){return btoa(str).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');}
function b64urlEncode(uint8){let s='';uint8.forEach(b=>s+=String.fromCharCode(b));return btoa(s).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+$/,'');}
const algMap={'HS256':{name:'HMAC',hash:'SHA-256'},'HS384':{name:'HMAC',hash:'SHA-384'},'HS512':{name:'HMAC',hash:'SHA-512'}};
async function generateJWT(){
const errEl=document.getElementById('jwt-error');errEl.style.display='none';
let header,payload;
try{header=JSON.parse(document.getElementById('jwt-header').value);}catch(e){errEl.textContent='Invalid Header JSON: '+e.message;errEl.style.display='block';return;}
try{payload=JSON.parse(document.getElementById('jwt-payload').value);}catch(e){errEl.textContent='Invalid Payload JSON: '+e.message;errEl.style.display='block';return;}
const alg=document.getElementById('jwt-alg').value;
const secret=document.getElementById('jwt-secret').value;
if(!secret){errEl.textContent='Please enter a secret key.';errEl.style.display='block';return;}
header.alg=alg;header.typ='JWT';
document.getElementById('jwt-header').value=JSON.stringify(header,null,2);
const headerB64=b64url(JSON.stringify(header));
const payloadB64=b64url(JSON.stringify(payload));
const signingInput=headerB64+'.'+payloadB64;
try{
const enc=new TextEncoder();
const keyData=enc.encode(secret);
const cryptoKey=await crypto.subtle.importKey('raw',keyData,algMap[alg],false,['sign']);
const sig=await crypto.subtle.sign('HMAC',cryptoKey,enc.encode(signingInput));
const sigB64=b64urlEncode(new Uint8Array(sig));
const jwt=signingInput+'.'+sigB64;
document.getElementById('jwt-output').innerHTML='<span class="jwt-header-part">'+headerB64+'</span><span class="jwt-dot">.</span><span class="jwt-payload-part">'+payloadB64+'</span><span class="jwt-dot">.</span><span class="jwt-sig-part">'+sigB64+'</span>';
document.getElementById('jwt-output').dataset.jwt=jwt;
}catch(e){errEl.textContent='Signing error: '+e.message;errEl.style.display='block';}
}
function addClaim(key,val){
try{
const p=JSON.parse(document.getElementById('jwt-payload').value);
p[key]=val;
document.getElementById('jwt-payload').value=JSON.stringify(p,null,2);
}catch(e){}
}
function addExp(secs){addClaim('exp',Math.floor(Date.now()/1000)+secs);}
function addIat(){addClaim('iat',Math.floor(Date.now()/1000));}
function addJti(){addClaim('jti',crypto.randomUUID?crypto.randomUUID():Math.random().toString(36).substring(2));}
function clearJWT(){
document.getElementById('jwt-header').value='{\n "alg": "HS256",\n "typ": "JWT"\n}';
document.getElementById('jwt-payload').value='{\n "sub": "1234567890",\n "name": "John Doe",\n "iat": 1516239022\n}';
document.getElementById('jwt-secret').value='your-256-bit-secret';
document.getElementById('jwt-output').innerHTML='<span style="color:var(--text3)">Generated JWT will appear here...</span>';
document.getElementById('jwt-error').style.display='none';
}
function copyJWT(){const jwt=document.getElementById('jwt-output').dataset.jwt;if(!jwt)return;navigator.clipboard.writeText(jwt);const t=document.getElementById('toast');t.style.opacity='1';setTimeout(()=>t.style.opacity='0',1500);}
document.getElementById('jwt-alg').addEventListener('change',function(){
try{const h=JSON.parse(document.getElementById('jwt-header').value);h.alg=this.value;document.getElementById('jwt-header').value=JSON.stringify(h,null,2);}catch(e){}
});
function toggleFaq(el){el.parentElement.classList.toggle('open');}
</script>
</body>
</html>