function initJwtDecoder() { const inputEl = document.getElementById("jwtInput"); const statusEl = document.getElementById("jwtStatus"); const claimsMetaEl = document.getElementById("jwtClaimsMeta"); const headerOutEl = document.getElementById("jwtHeaderOutput"); const payloadOutEl = document.getElementById("jwtPayloadOutput"); const signatureEl = document.getElementById("jwtSignature"); const decodeBtn = document.getElementById("jwtDecodeBtn"); const copyHeaderBtn = document.getElementById("jwtCopyHeaderBtn"); const copyPayloadBtn = document.getElementById("jwtCopyPayloadBtn"); const clearBtn = document.getElementById("jwtClearBtn"); if (!inputEl || !headerOutEl || !payloadOutEl || !signatureEl) return; function setStatus(text, isError) { if (!statusEl) return; statusEl.textContent = text; statusEl.classList.toggle("status-error", !!isError); statusEl.classList.toggle("status-success", !isError); } function setClaimsMeta(text, isError) { if (!claimsMetaEl) return; claimsMetaEl.textContent = text; claimsMetaEl.classList.toggle("status-error", !!isError); claimsMetaEl.classList.toggle("status-success", !isError); } function updateButtons() { const hasInput = !!inputEl.value.trim(); const hasHeader = !!headerOutEl.value.trim(); const hasPayload = !!payloadOutEl.value.trim(); if (decodeBtn) decodeBtn.disabled = !hasInput; if (copyHeaderBtn) copyHeaderBtn.disabled = !hasHeader; if (copyPayloadBtn) copyPayloadBtn.disabled = !hasPayload; if (clearBtn) clearBtn.disabled = !hasInput && !hasHeader && !hasPayload; } function base64UrlToUtf8(segment) { const base64 = segment.replace(/-/g, "+").replace(/_/g, "/"); const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4); const binary = atob(padded); const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0)); return new TextDecoder().decode(bytes); } function parseJsonSegment(segment, label) { try { return JSON.parse(base64UrlToUtf8(segment)); } catch (err) { throw new Error(`Invalid ${label} segment`); } } function formatEpoch(epoch) { if (typeof epoch !== "number" || !Number.isFinite(epoch)) return null; const date = new Date(epoch * 1000); return `${date.toISOString()} (${Math.floor((date.getTime() - Date.now()) / 1000)}s from now)`; } function buildClaimsSummary(payload) { const parts = []; const exp = formatEpoch(payload.exp); const iat = formatEpoch(payload.iat); const nbf = formatEpoch(payload.nbf); if (iat) parts.push(`iat: ${iat}`); if (nbf) parts.push(`nbf: ${nbf}`); if (exp) parts.push(`exp: ${exp}`); return parts.length ? parts.join(" | ") : "Decoded without standard time claims."; } function decodeAction() { const token = inputEl.value.trim(); if (!token) return; const parts = token.split("."); if (parts.length < 2) { setStatus("Invalid JWT format. Expected header.payload.signature", true); setClaimsMeta("No token decoded yet.", true); headerOutEl.value = ""; payloadOutEl.value = ""; signatureEl.value = ""; updateButtons(); return; } try { const header = parseJsonSegment(parts[0], "header"); const payload = parseJsonSegment(parts[1], "payload"); headerOutEl.value = JSON.stringify(header, null, 4); payloadOutEl.value = JSON.stringify(payload, null, 4); signatureEl.value = parts[2] || ""; if (header.alg === "none") { setStatus("Decoded. Warning: alg 'none' token.", true); } else { setStatus("JWT decoded successfully."); } setClaimsMeta(buildClaimsSummary(payload), false); } catch (err) { setStatus(err.message || "Failed to decode JWT.", true); setClaimsMeta("No token decoded yet.", true); headerOutEl.value = ""; payloadOutEl.value = ""; signatureEl.value = ""; } updateButtons(); } async function copyText(sourceEl, buttonEl, successMessage) { if (!sourceEl.value.trim()) return; await navigator.clipboard.writeText(sourceEl.value); const original = buttonEl.textContent; buttonEl.textContent = "Copied"; setStatus(successMessage, false); setTimeout(() => { buttonEl.textContent = original; }, 1200); } function clearAction() { inputEl.value = ""; headerOutEl.value = ""; payloadOutEl.value = ""; signatureEl.value = ""; setStatus("Paste a JWT to decode header and payload."); setClaimsMeta("No token decoded yet."); updateButtons(); } inputEl.addEventListener("input", updateButtons); if (decodeBtn) decodeBtn.addEventListener("click", decodeAction); if (copyHeaderBtn) { copyHeaderBtn.addEventListener("click", () => copyText(headerOutEl, copyHeaderBtn, "Header copied.") ); } if (copyPayloadBtn) { copyPayloadBtn.addEventListener("click", () => copyText(payloadOutEl, copyPayloadBtn, "Payload copied.") ); } if (clearBtn) clearBtn.addEventListener("click", clearAction); updateButtons(); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initJwtDecoder); } else { initJwtDecoder(); }