{"id":10238,"date":"2026-08-15T00:22:32","date_gmt":"2026-08-14T22:22:32","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/toolbox\/jwt-oauth-token-inspector\/"},"modified":"2026-08-15T00:22:32","modified_gmt":"2026-08-14T22:22:32","slug":"jwt-oauth-token-inspector","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/","title":{"rendered":"JWT &#038; OAuth Token Inspector"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0b0); margin-bottom: 20px;\">A JSON Web Token is signed, not encrypted: anyone holding it can read the payload. This tool decodes header and payload, does the arithmetic on the lifetime, and can recompute an HS256 signature without the secret ever leaving the browser.<\/p>\n<p>    <label for=\"jwt-eingabe\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Token (a leading Bearer is ignored)<\/label><br \/>\n    <textarea id=\"jwt-eingabe\" class=\"form-control\" style=\"width: 100%; height: 130px; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; font-size: 0.85rem; box-sizing: border-box;\" placeholder=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"><\/textarea><\/p>\n<p>    <label for=\"jwt-geheimnis\" style=\"color: var(--text-secondary, #a0a0b0); display: block; font-size: 0.85rem; margin: 15px 0 5px;\">Shared secret for HS256 (optional)<\/label><br \/>\n    <input type=\"text\" id=\"jwt-geheimnis\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); border-radius: 6px; font-family: monospace; box-sizing: border-box;\" placeholder=\"only needed to recompute the signature\"><\/p>\n<div style=\"display: flex; gap: 10px; flex-wrap: wrap; margin: 18px 0 0;\">\n        <button id=\"jwt-btn\" class=\"button\" style=\"background: var(--accent, #7ee787); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Inspect<\/button><br \/>\n        <button id=\"jwt-beispiel\" class=\"button\" type=\"button\" style=\"background: transparent; color: var(--text-primary, #e8e8ee); border: 1px solid var(--border, #2a2a35); padding: 12px 20px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Load example<\/button>\n    <\/div>\n<div id=\"jwt-ergebnis\" style=\"margin-top: 22px;\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    const T = {\"einleitung\":\"A JSON Web Token is signed, not encrypted: anyone holding it can read the payload. This tool decodes header and payload, does the arithmetic on the lifetime, and can recompute an HS256 signature without the secret ever leaving the browser.\",\"label_token\":\"Token (a leading Bearer is ignored)\",\"label_geheimnis\":\"Shared secret for HS256 (optional)\",\"ph_geheimnis\":\"only needed to recompute the signature\",\"knopf\":\"Inspect\",\"beispiel\":\"Load example\",\"fehler_leer\":\"No token was entered.\",\"fehler_form\":\"A JWT consists of at least two parts separated by dots.\",\"fehler_jwe\":\"This looks like an encrypted token (JWE) with five parts, which cannot be read without the key.\",\"fehler_base64\":\"Header or payload is not valid web-safe base64.\",\"fehler_kopf_json\":\"The header is not valid JSON.\",\"fehler_rumpf_json\":\"The payload is not valid JSON.\",\"h_befunde\":\"Findings\",\"h_kopf\":\"Header\",\"h_ansprueche\":\"Claims\",\"h_bereiche\":\"Scopes\",\"h_signatur\":\"Signature\",\"tag\":\"day\",\"tage\":\"days\",\"stunde\":\"hour\",\"stunden\":\"hours\",\"minute\":\"minute\",\"minuten\":\"minutes\",\"sekunden\":\"seconds\",\"b_alg_none\":\"The algorithm is none: this token carries no signature at all and can be rewritten by anyone.\",\"b_alg_fehlt\":\"The header names no algorithm, which no library will accept.\",\"b_ohne_signatur\":\"The third part is missing although the header names an algorithm - only header and payload arrived.\",\"b_ohne_exp\":\"There is no exp claim: this token never expires on its own.\",\"b_abgelaufen\":\"Expired %s ago.\",\"b_laeuft_ab\":\"Still valid for %s.\",\"b_lang\":\"The remaining lifetime of %s is unusually long for an access token; that is refresh token territory.\",\"b_noch_nicht\":\"Not valid yet, nbf lies %s in the future.\",\"b_zukunft\":\"The issue time lies in the future, which points to a clock that is out of step.\",\"b_heikel\":\"The payload carries fields with a personal reference: %s. Base64 is not encryption, every holder of the token reads them.\",\"b_ohne_aud\":\"There is no aud claim, so the token does not say which service it was meant for.\",\"b_ohne_iss\":\"There is no iss claim, so the issuer cannot be checked.\",\"s_erklaerung\":\"Decoding says nothing about authenticity. Only the signature does that, and checking it needs the key: the shared secret for HS256, the public key of the issuer for RS256 and ES256.\",\"s_pruefe\":\"checking...\",\"s_gueltig\":\"HS256 signature matches the secret.\",\"s_ungueltig\":\"HS256 signature does not match this secret.\",\"s_keine_signatur\":\"No signature part present, nothing to recompute.\",\"s_nur_hs256\":\"Recomputation here only covers HS256; this token uses %s, which needs the public key of the issuer.\",\"s_kein_geheimnis\":\"Enter the shared secret to recompute the signature.\",\"s_keine_krypto\":\"This browser offers no crypto interface for the recomputation.\"};<\/p>\n<p>    \/\/ --- reine Auswertung ---<\/p>\n<p>    \/\/ Felder, die einen Personenbezug tragen. Der Rumpf eines JWT ist nur\n    \/\/ kodiert, nicht verschluesselt - alles hier steht fuer jeden lesbar da,\n    \/\/ der den Token in die Finger bekommt.\n    const HEIKEL = ['email', 'e_mail', 'mail', 'name', 'given_name', 'family_name', 'nickname',\n        'preferred_username', 'phone', 'phone_number', 'address', 'birthdate', 'gender',\n        'picture', 'ssn', 'password', 'secret', 'api_key', 'credit_card'];<\/p>\n<p>    const ZEITFELDER = ['exp', 'iat', 'nbf', 'auth_time', 'updated_at'];<\/p>\n<p>    function vonBase64Url(text) {\n        let s = String(text).replace(\/-\/g, '+').replace(\/_\/g, '\/');\n        while (s.length % 4 !== 0) { s += '='; }\n        let roh;\n        try {\n            roh = atob(s);\n        } catch (e) {\n            return null;\n        }\n        \/\/ atob liefert Bytes; fuer Umlaute und alles jenseits von ASCII muss\n        \/\/ daraus noch UTF-8 werden.\n        try {\n            const bytes = new Uint8Array(roh.length);\n            for (let i = 0; i < roh.length; i++) { bytes[i] = roh.charCodeAt(i); }\n            return new TextDecoder('utf-8').decode(bytes);\n        } catch (e) {\n            return roh;\n        }\n    }\n\n    function datumText(sekunden) {\n        const ms = Number(sekunden) * 1000;\n        if (!isFinite(ms)) { return '-'; }\n        const d = new Date(ms);\n        function zwei(n) { return (n < 10 ? '0' : '') + n; }\n        return d.getUTCFullYear() + '-' + zwei(d.getUTCMonth() + 1) + '-' + zwei(d.getUTCDate())\n            + ' ' + zwei(d.getUTCHours()) + ':' + zwei(d.getUTCMinutes()) + ':' + zwei(d.getUTCSeconds()) + ' UTC';\n    }\n\n    function dauerText(sekunden) {\n        let s = Math.abs(Math.floor(sekunden));\n        const tage = Math.floor(s \/ 86400);\n        s -= tage * 86400;\n        const stunden = Math.floor(s \/ 3600);\n        s -= stunden * 3600;\n        const minuten = Math.floor(s \/ 60);\n        const teile = [];\n        if (tage) { teile.push(tage + ' ' + (tage === 1 ? T.tag : T.tage)); }\n        if (stunden) { teile.push(stunden + ' ' + (stunden === 1 ? T.stunde : T.stunden)); }\n        if (minuten &#038;&#038; !tage) { teile.push(minuten + ' ' + (minuten === 1 ? T.minute : T.minuten)); }\n        if (!teile.length) { teile.push((s % 60) + ' ' + T.sekunden); }\n        return teile.join(', ');\n    }\n\n    function feldZerlegen(text) {\n        const roh = String(text === undefined || text === null ? '' : text).trim();\n        const ergebnis = {\n            ok: false, fehler: '', teile: 0,\n            kopf: null, rumpf: null, signatur: '',\n            kopfRoh: '', rumpfRoh: '', befunde: [], ansprueche: []\n        };\n        if (roh === '') { ergebnis.fehler = T.fehler_leer; return ergebnis; }\n\n        \/\/ Ein vorangestelltes Bearer stoert nur.\n        const ohneBearer = roh.replace(\/^bearer\\s+\/i, '').trim();\n        const teile = ohneBearer.split('.');\n        ergebnis.teile = teile.length;\n\n        if (teile.length < 2) { ergebnis.fehler = T.fehler_form; return ergebnis; }\n        if (teile.length > 5) { ergebnis.fehler = T.fehler_jwe; return ergebnis; }<\/p>\n<p>        const kopfText = vonBase64Url(teile[0]);\n        const rumpfText = vonBase64Url(teile[1]);\n        if (kopfText === null || rumpfText === null) { ergebnis.fehler = T.fehler_base64; return ergebnis; }\n        ergebnis.kopfRoh = kopfText;\n        ergebnis.rumpfRoh = rumpfText;<\/p>\n<p>        try {\n            ergebnis.kopf = JSON.parse(kopfText);\n        } catch (e) {\n            ergebnis.fehler = T.fehler_kopf_json;\n            return ergebnis;\n        }\n        try {\n            ergebnis.rumpf = JSON.parse(rumpfText);\n        } catch (e) {\n            ergebnis.fehler = T.fehler_rumpf_json;\n            return ergebnis;\n        }\n        if (!ergebnis.rumpf || typeof ergebnis.rumpf !== 'object') {\n            ergebnis.fehler = T.fehler_rumpf_json;\n            return ergebnis;\n        }<\/p>\n<p>        ergebnis.signatur = teile.length > 2 ? teile[2] : '';\n        ergebnis.ok = true;\n        return ergebnis;\n    }<\/p>\n<p>    function bewerten(zerlegt, jetzt) {\n        const befunde = [];\n        const zeit = Math.floor(Number(jetzt) \/ 1000);\n        const kopf = zerlegt.kopf || {};\n        const rumpf = zerlegt.rumpf || {};<\/p>\n<p>        const alg = String(kopf.alg === undefined ? '' : kopf.alg);\n        if (alg.toLowerCase() === 'none') {\n            befunde.push({ schwer: true, text: T.b_alg_none });\n        }\n        if (alg === '') {\n            befunde.push({ schwer: true, text: T.b_alg_fehlt });\n        }\n        if (zerlegt.signatur === '' && alg.toLowerCase() !== 'none') {\n            befunde.push({ schwer: true, text: T.b_ohne_signatur });\n        }<\/p>\n<p>        if (rumpf.exp === undefined) {\n            befunde.push({ schwer: true, text: T.b_ohne_exp });\n        } else {\n            const rest = Number(rumpf.exp) - zeit;\n            if (rest <= 0) {\n                befunde.push({ schwer: true, text: T.b_abgelaufen.replace('%s', dauerText(rest)) });\n            } else {\n                befunde.push({ schwer: false, text: T.b_laeuft_ab.replace('%s', dauerText(rest)) });\n                if (rest > 60 * 60 * 24 * 30) {\n                    befunde.push({ schwer: true, text: T.b_lang.replace('%s', dauerText(rest)) });\n                }\n            }\n        }\n        if (rumpf.nbf !== undefined && Number(rumpf.nbf) > zeit) {\n            befunde.push({ schwer: true, text: T.b_noch_nicht.replace('%s', dauerText(Number(rumpf.nbf) - zeit)) });\n        }\n        if (rumpf.iat !== undefined) {\n            const alter = zeit - Number(rumpf.iat);\n            if (alter < -300) {\n                befunde.push({ schwer: true, text: T.b_zukunft });\n            }\n        }\n\n        const heikel = [];\n        Object.keys(rumpf).forEach(function (schluessel) {\n            if (HEIKEL.indexOf(String(schluessel).toLowerCase()) !== -1) { heikel.push(schluessel); }\n        });\n        if (heikel.length) {\n            befunde.push({ schwer: false, text: T.b_heikel.replace('%s', heikel.join(', ')) });\n        }\n\n        if (rumpf.aud === undefined) { befunde.push({ schwer: false, text: T.b_ohne_aud }); }\n        if (rumpf.iss === undefined) { befunde.push({ schwer: false, text: T.b_ohne_iss }); }\n\n        return befunde;\n    }\n\n    function ansprueche(rumpf) {\n        const liste = [];\n        Object.keys(rumpf || {}).forEach(function (name) {\n            const wert = rumpf[name];\n            let text;\n            if (ZEITFELDER.indexOf(name) !== -1 &#038;&#038; typeof wert === 'number') {\n                text = datumText(wert) + '  (' + wert + ')';\n            } else if (wert === null) {\n                text = 'null';\n            } else if (typeof wert === 'object') {\n                text = JSON.stringify(wert);\n            } else {\n                text = String(wert);\n            }\n            liste.push({ name: name, wert: text, roh: wert });\n        });\n        return liste;\n    }\n\n    function bereiche(rumpf) {\n        \/\/ scope kommt als Zeichenkette mit Leerzeichen, scp gern als Feld.\n        const raus = [];\n        if (typeof rumpf.scope === 'string') {\n            rumpf.scope.split(\/\\s+\/).forEach(function (s) { if (s) { raus.push(s); } });\n        }\n        if (Array.isArray(rumpf.scp)) {\n            rumpf.scp.forEach(function (s) { raus.push(String(s)); });\n        } else if (typeof rumpf.scp === 'string') {\n            rumpf.scp.split(\/\\s+\/).forEach(function (s) { if (s) { raus.push(s); } });\n        }\n        if (Array.isArray(rumpf.permissions)) {\n            rumpf.permissions.forEach(function (s) { raus.push(String(s)); });\n        }\n        return raus;\n    }\n\n    function untersuchen(text, jetzt) {\n        const zerlegt = feldZerlegen(text);\n        if (!zerlegt.ok) { return zerlegt; }\n        zerlegt.befunde = bewerten(zerlegt, jetzt);\n        zerlegt.ansprueche = ansprueche(zerlegt.rumpf);\n        zerlegt.bereiche = bereiche(zerlegt.rumpf);\n        return zerlegt;\n    }\n\n    \/**\n     * HS256 nachrechnen. Das Geheimnis bleibt im Browser, gerechnet wird mit\n     * der eingebauten Krypto-Schnittstelle.\n     *\/\n    function pruefeHS256(token, geheimnis) {\n        const teile = String(token).replace(\/^bearer\\s+\/i, '').trim().split('.');\n        if (teile.length < 3 || !teile[2]) {\n            return Promise.resolve({ moeglich: false, grund: T.s_keine_signatur });\n        }\n        const zerlegt = feldZerlegen(token);\n        if (!zerlegt.ok) { return Promise.resolve({ moeglich: false, grund: zerlegt.fehler }); }\n        const alg = String((zerlegt.kopf || {}).alg || '').toUpperCase();\n        if (alg !== 'HS256') {\n            return Promise.resolve({ moeglich: false, grund: T.s_nur_hs256.replace('%s', alg || '?') });\n        }\n        if (!geheimnis) {\n            return Promise.resolve({ moeglich: false, grund: T.s_kein_geheimnis });\n        }\n        if (!window.crypto || !window.crypto.subtle) {\n            return Promise.resolve({ moeglich: false, grund: T.s_keine_krypto });\n        }\n\n        const kodierer = new TextEncoder();\n        const daten = kodierer.encode(teile[0] + '.' + teile[1]);\n        return window.crypto.subtle.importKey(\n            'raw', kodierer.encode(geheimnis), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']\n        ).then(function (schluessel) {\n            return window.crypto.subtle.sign('HMAC', schluessel, daten);\n        }).then(function (unterschrift) {\n            const bytes = new Uint8Array(unterschrift);\n            let roh = '';\n            for (let i = 0; i < bytes.length; i++) { roh += String.fromCharCode(bytes[i]); }\n            const meins = btoa(roh).replace(\/\\+\/g, '-').replace(\/\\\/\/g, '_').replace(\/=+$\/, '');\n            return { moeglich: true, gueltig: meins === teile[2], erwartet: meins };\n        }).catch(function (e) {\n            return { moeglich: false, grund: (e &#038;&#038; e.message) ? e.message : T.s_keine_krypto };\n        });\n    }\n\n    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.JWT = {\n            untersuchen: untersuchen,\n            feldZerlegen: feldZerlegen,\n            pruefeHS256: pruefeHS256,\n            vonBase64Url: vonBase64Url,\n            dauerText: dauerText,\n            datumText: datumText,\n            bereiche: bereiche\n        };\n    }\n\n    \/\/ --- Anbindung an das Dokument ---\n\n    const btn = document.getElementById('jwt-btn');\n    const out = document.getElementById('jwt-ergebnis');\n    const feld = document.getElementById('jwt-eingabe');\n    const geheimFeld = document.getElementById('jwt-geheimnis');\n    const beispielBtn = document.getElementById('jwt-beispiel');\n    if (!btn || !out || !feld || !geheimFeld || !beispielBtn) { return; }\n\n    const KASTEN = 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 14px; margin-bottom: 18px;';\n    const UEBERSCHRIFT = 'color: var(--text-primary, #e8e8ee); font-weight: 700; margin-bottom: 8px;';\n\n    function el(tag, css, text) {\n        const knoten = document.createElement(tag);\n        if (css) { knoten.setAttribute('style', css); }\n        if (text !== undefined &#038;&#038; text !== null) { knoten.textContent = text; }\n        return knoten;\n    }\n\n    function meldung(text) {\n        out.textContent = '';\n        out.appendChild(el('div', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-left: 3px solid var(--accent, #7ee787); color: var(--text-secondary, #a0a0b0); padding: 14px; border-radius: 6px;', text));\n    }\n\n    function zeigen(erg) {\n        out.textContent = '';\n\n        if (erg.befunde.length) {\n            out.appendChild(el('div', UEBERSCHRIFT, T.h_befunde));\n            const kasten = el('div', KASTEN);\n            erg.befunde.forEach(function (b) {\n                const z = el('div', 'display: flex; gap: 10px; padding: 5px 0;');\n                z.appendChild(el('span', 'color: ' + (b.schwer ? '#ffb86c' : 'var(--accent, #7ee787)') + '; font-family: monospace;', b.schwer ? '!' : '+'));\n                z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0); font-size: 0.9rem; line-height: 1.5;', b.text));\n                kasten.appendChild(z);\n            });\n            out.appendChild(kasten);\n        }\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_kopf));\n        const kopf = el('pre', 'background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ee); padding: 16px; border-radius: 6px; overflow-x: auto; font-family: monospace; font-size: 0.85rem; white-space: pre-wrap; word-break: break-all; margin-bottom: 18px;');\n        kopf.textContent = JSON.stringify(erg.kopf, null, 2);\n        out.appendChild(kopf);\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_ansprueche));\n        const tabelle = el('div', KASTEN);\n        erg.ansprueche.forEach(function (a) {\n            const z = el('div', 'display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap; padding: 5px 0; border-bottom: 1px solid var(--border, #2a2a35);');\n            z.appendChild(el('span', 'color: var(--text-secondary, #a0a0b0); font-family: monospace;', a.name));\n            z.appendChild(el('span', 'color: var(--text-primary, #e8e8ee); font-family: monospace; text-align: right; word-break: break-all; max-width: 70%;', a.wert));\n            tabelle.appendChild(z);\n        });\n        out.appendChild(tabelle);\n\n        if (erg.bereiche &#038;&#038; erg.bereiche.length) {\n            out.appendChild(el('div', UEBERSCHRIFT, T.h_bereiche));\n            const kasten = el('div', KASTEN);\n            erg.bereiche.forEach(function (b) {\n                kasten.appendChild(el('div', 'color: var(--text-primary, #e8e8ee); font-family: monospace; font-size: 0.85rem; padding: 3px 0;', b));\n            });\n            out.appendChild(kasten);\n        }\n\n        out.appendChild(el('div', UEBERSCHRIFT, T.h_signatur));\n        const sig = el('div', KASTEN);\n        sig.appendChild(el('div', 'color: var(--text-secondary, #a0a0b0); font-size: 0.9rem; line-height: 1.6;', T.s_erklaerung));\n        const stand = el('div', 'color: var(--text-secondary, #a0a0b0); font-family: monospace; font-size: 0.85rem; padding-top: 8px;', T.s_pruefe);\n        sig.appendChild(stand);\n        out.appendChild(sig);\n\n        pruefeHS256(feld.value, geheimFeld.value).then(function (ergebnis) {\n            if (!ergebnis.moeglich) {\n                stand.textContent = ergebnis.grund;\n                stand.style.color = 'var(--text-secondary, #a0a0b0)';\n                return;\n            }\n            stand.textContent = ergebnis.gueltig ? T.s_gueltig : T.s_ungueltig;\n            stand.style.color = ergebnis.gueltig ? 'var(--accent, #7ee787)' : '#ffb86c';\n        });\n    }\n\n    btn.addEventListener('click', function () {\n        const erg = untersuchen(feld.value, Date.now());\n        if (!erg.ok) { meldung(erg.fehler); return; }\n        zeigen(erg);\n    });\n\n    beispielBtn.addEventListener('click', function () {\n        \/\/ Beispieltoken mit dem Geheimnis \"lukaswojcik\" unterschrieben.\n        feld.value = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImxvZ2luLTIwMjYifQ.eyJpc3MiOiJodHRwczovL3d3dy5sdWthc3dvamNpay5jb20iLCJzdWIiOiIxNDIiLCJhdWQiOiJ3cC1yZXN0IiwiaWF0IjoxNzg1NTc4NDAwLCJuYmYiOjE3ODU1Nzg0MDAsImV4cCI6MTc4NTU4MjAwMCwianRpIjoiYTNmOWMyIiwic2NvcGUiOiJwb3N0czpyZWFkIHBvc3RzOndyaXRlIiwiZW1haWwiOiJyZWRha3Rpb25AZXhhbXBsZS5jb20ifQ.fJnDPZw_ogedOHA5uiKr_0IcnJVZfUa5pygLEx7ykDM';\n        geheimFeld.value = 'lukaswojcik';\n        btn.click();\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Decodes a JSON Web Token in the browser, works out how long it is still valid, lists its scopes and points out a missing signature, an endless lifetime or personal data in the payload.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":38,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"tags":[91392,91099,91115],"class_list":["post-10238","page","type-page","status-publish","hentry","tag-rest-api","tag-server-administration","tag-web-security"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>JWT &amp; OAuth Token Inspector - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"JWT &amp; OAuth Token Inspector - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Decodes a JSON Web Token in the browser, works out how long it is still valid, lists its scopes and points out a missing signature, an endless lifetime or personal data in the payload.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/jwt-oauth-token-inspector\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/jwt-oauth-token-inspector\\\/\",\"name\":\"JWT & OAuth Token Inspector - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-14T22:22:32+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/jwt-oauth-token-inspector\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/jwt-oauth-token-inspector\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/jwt-oauth-token-inspector\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/en\\\/toolbox\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"JWT &#038; OAuth Token Inspector\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"JWT & OAuth Token Inspector - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/","og_locale":"en_US","og_type":"article","og_title":"JWT & OAuth Token Inspector - Lukas Wojcik - Blog","og_description":"Decodes a JSON Web Token in the browser, works out how long it is still valid, lists its scopes and points out a missing signature, an endless lifetime or personal data in the payload.","og_url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/","og_site_name":"Lukas Wojcik - Blog","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/","url":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/","name":"JWT & OAuth Token Inspector - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-14T22:22:32+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/jwt-oauth-token-inspector\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/en\/toolbox\/"},{"@type":"ListItem","position":3,"name":"JWT &#038; OAuth Token Inspector"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10238","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=10238"}],"version-history":[{"count":0,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/10238\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/38"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=10238"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/tags?post=10238"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}