Wie kann ich ein JWT-Token in Javascript dekodieren, ohne eine Bibliothek zu verwenden?

208

Wie kann ich die Nutzlast von JWT mit JavaScript dekodieren? Ohne Bibliothek. Das Token gibt also nur ein Nutzdatenobjekt zurück, das von meiner Front-End-App verwendet werden kann.

Beispiel-Token: xxxxxxxxx.XXXXXXXX.xxxxxxxx

Und das Ergebnis ist die Nutzlast:

{exp: 10012016 name: john doe, scope:['admin']}
Chrisk8er
quelle
1
Wie wurde es verschlüsselt? Mach einfach das Gegenteil. Sie benötigen das gemeinsame Geheimnis.
Lucky Soni
Es wurde von der Backend-API codiert, die die PHP-Bibliothek verwendete. Hier brauche ich die Nutzlast, die mit base64 codiert wurde, denke ich ...
Chrisk8er
1
Sie können versuchen, auf die Website jwt.io zu gehen und die bereitgestellte JavaScript-Bibliothek abzurufen .
Quentin
12
Da diese Frage etwas Verkehr hat, möchte ich einen Haftungsausschluss hinzufügen: Wenn Sie die Nutzdaten des Tokens blind entschlüsseln, ohne die Signatur zu validieren, können Sicherheitsprobleme auftreten (oder auch nicht)! Stellen Sie sicher, dass Sie Ihre Sicherheitsarchitektur verstanden haben, bevor Sie blind den in dieser Frage zum Stapelüberlauf bereitgestellten Code verwenden.
Carsten Hoffmann
4
@CarstenHoffmann Und wie genau validiere ich die Signatur?
Saurabh Tiwari

Antworten:

467

JWT-Parserfunktion für Unicode-Text:

function parseJwt (token) {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    var jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));

    return JSON.parse(jsonPayload);
};
Peheje
quelle
2
Leider scheint dies mit Unicode-Text nicht zu funktionieren.
Paul McMahon
2
Diese Lösung kann sogar in Postman (Testtippen) verwendet werden, da keine zusätzliche Bibliotheksinstallation erforderlich ist. Ich habe es verwendet, um die Benutzer-ID aus dem Auth-Token zu extrahieren.
Wlad
2
HINWEIS: In Postman musste ich "Fenster" entfernen JSON.parse(window.atob(base64)), damit es funktioniert. In return JSON.parse(atob(base64));diesem postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); Fall ist "access_token" in meinem Fall der Schlüssel für den Wert des Tokens als Antwort (kann in Ihrem Fall abweichen).
Wlad
12
Die obige Lösung ersetzt nur das erste "-" und "_" im Token (eine Javascript- "Funktion", die mir immer wieder Schmerzen bereitet). Ersetzen Sie einfach die dritte Zeile in der Antwort durch:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole
2
Es ist besser, das jwt-decodeModul zu verwenden, weil es klein ist, aber etwas besser zu handhaben ist.
Rantiev
63

Einfache Funktion mit try - catch

const parseJwt = (token) => {
  try {
    return JSON.parse(atob(token.split('.')[1]));
  } catch (e) {
    return null;
  }
};

Vielen Dank!

Rajan Maharjan
quelle
nett, prägnant und verwendet alle nativen Methoden!
Chris Love
2
atobhat Unicode-Probleme
Tamer Shlash
47

Sie können jwt-decode verwenden , um dann zu schreiben:

import jwt_decode from 'jwt-decode';

var token = 'eyJ0eXAiO.../// jwt token';

var decoded = jwt_decode(token);
console.log(decoded);
/*{exp: 10012016 name: john doe, scope:['admin']}*/
Kerl
quelle
65
"Ich meine keine Bibliothek."
SherloxTV
Sie sind Probleme mit dieser Bibliothek. Hauptsächlich mit Firefox im Einsatz. Das Problem, auf das ich gestoßen bin, war, dass wenn ein Token == null aus dem Abmelden oder Ablaufen resultiert; dass dies nur die Seite mit einem Fehler beendet.
Benutzer
1
@ApertureSecurity Sie müssen diesen Fehler abfangen, aber zugegebenermaßen ist dies der Grund, warum ich diese Bibliothek nicht verwenden möchte
Luke Robertson
Dies scheint GZIP nicht zu unterstützen. Tatsächlich kann ich keine JS-Bibliotheken finden, die GZIP für die Ansprüche unterstützen.
Andrew T Finnell
18

Sie können die reine Javascript- atob()Funktion verwenden, um Token in eine Zeichenfolge zu dekodieren:

atob(token.split('.')[1]);

oder analysieren Sie es direkt in ein JSON-Objekt:

JSON.parse(atob(token.split('.')[1]));

lesen über atob() und btoa()Integrieren von Javascript-Funktionen Base64-Codierung und -Decodierung - Web-APIs | MDN .

Muhammed Moussa
quelle
9

@ Peheje wird funktionieren, aber Sie werden Probleme mit Unicode haben. Um dies zu beheben, verwende ich den Code unter https://stackoverflow.com/a/30106551/5277071 ;

let b64DecodeUnicode = str =>
  decodeURIComponent(
    Array.prototype.map.call(atob(str), c =>
      '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
    ).join(''))

let parseJwt = token =>
  JSON.parse(
    b64DecodeUnicode(
      token.split('.')[1].replace('-', '+').replace('_', '/')
    )
  )


let form = document.getElementById("form")
form.addEventListener("submit", (e) => {
   form.out.value = JSON.stringify(
      parseJwt(form.jwt.value)
   )
   e.preventDefault();
})
textarea{width:300px; height:60px; display:block}
<form id="form" action="parse">
  <textarea name="jwt">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkrDtGhuIETDs8OoIiwiYWRtaW4iOnRydWV9.469tBeJmYLERjlKi9u6gylb-2NsjHLC_6kZNdtoOGsA</textarea>
  <textarea name="out"></textarea>
  <input type="submit" value="parse" />
</form>

Rafael Quintela
quelle
+1, aber wenn der Kommentar von Racing Tadpole zu Pehejes Antwort korrekt ist (dass die Ersetzungsaufrufe nur die erste Instanz ersetzen), würde hier der gleiche Fix gelten.
Gary McGill
9

Da das "Fenster" -Objekt in der Umgebung von nodejs nicht vorhanden ist, können wir die folgenden Codezeilen verwenden:

let base64Url = token.split('.')[1]; // token you get
let base64 = base64Url.replace('-', '+').replace('_', '/');
let decodedData = JSON.parse(Buffer.from(base64, 'base64').toString('binary'));

Es funktioniert perfekt für mich. Ich hoffe es hilft.

Avik
quelle
1
perfekte Antwort für Knoten js
ireshan pathirana
7
function parseJwt(token) {
  var base64Payload = token.split('.')[1];
  var payload = Buffer.from(base64Payload, 'base64');
  return JSON.parse(payload);
}
let payload= parseJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
console.log("payload:- ", payload);

Wenn Sie einen Knoten verwenden, müssen Sie möglicherweise ein Pufferpaket verwenden:

npm install buffer
var Buffer = require('buffer/').Buffer
hashinclude72
quelle
6

Ich benutze diese Funktion, um Nutzlast, Header, Exp (Ablaufzeit), iat (Issued At) basierend auf dieser Antwort zu erhalten

function parseJwt(token) {
  try {
    // Get Token Header
    const base64HeaderUrl = token.split('.')[0];
    const base64Header = base64HeaderUrl.replace('-', '+').replace('_', '/');
    const headerData = JSON.parse(window.atob(base64Header));

    // Get Token payload and date's
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace('-', '+').replace('_', '/');
    const dataJWT = JSON.parse(window.atob(base64));
    dataJWT.header = headerData;

// TODO: add expiration at check ...


    return dataJWT;
  } catch (err) {
    return false;
  }
}

const jwtDecoded = parseJwt('YOUR_TOKEN') ;
if(jwtDecoded)
{
    console.log(jwtDecoded)
}
Softmixt
quelle
Diese Antwort ist etwas besser, hat aber zweieinhalb Probleme. Erstens wird die Signatur nicht überprüft (Array-Element 2). Zweitens funktionieren die REPLACEs nicht richtig, da sie das "g" -Flag in der Regex übersehen (ersetzen nur die ersten Vorkommen von - und _ in der JWT, wie Racing Tadpole in einem anderen Beitrag kommentiert hat). Und die Hälfte: Um die Array-Elemente 0 und 1 zu dekodieren, hätten Sie eine FOR-Schleife verwenden können, anstatt den gesamten Code zu duplizieren (es handelt sich um einen Kurzcode, der jedoch effizienter gestaltet werden könnte, da SPLIT so wie er ist zweimal ausgeführt wird) ).
Cyberknight
4

Alle Funktionen von jwt.io unterstützen nicht alle Sprachen. In NodeJs können Sie verwenden

var decoded = jwt.decode(token);
Jithin Vijayan
quelle
1
Ohne Bibliothek führen Sie einfach die Base64-Decodierung im zweiten Teil des Tokens durch {var payload = token.split ('.') [1]); } Führen Sie dann die base64-Decodierung durch {var decodedData = atob (payload); }
Jithin Vijayan
4

Ich habe diesen Code bei jwt.io gefunden und er funktioniert gut.

//this is used to parse base64
function url_base64_decode(str) {
  var output = str.replace(/-/g, '+').replace(/_/g, '/');
  switch (output.length % 4) {
    case 0:
      break;
    case 2:
      output += '==';
      break;
    case 3:
      output += '=';
      break;
    default:
      throw 'Illegal base64url string!';
  }
  var result = window.atob(output); //polifyll https://github.com/davidchambers/Base64.js
  try{
    return decodeURIComponent(escape(result));
  } catch (err) {
    return result;
  }
}

In einigen Fällen (auf bestimmten Entwicklungsplattformen) ist
die beste Antwort (vorerst) mit einem Problem ungültiger base64-Länge konfrontiert.
Also brauchte ich einen stabileren Weg.

Ich hoffe es würde dir helfen.

Nao Ito
quelle
2

Sowohl Guy als auch Peheje haben die Frage bereits beantwortet. Für einen Anfänger wie mich war es hilfreich, auch die Importzeile im Beispiel definiert zu haben.

Außerdem habe ich einige Minuten gebraucht, um herauszufinden, dass das Token der vollständige Satz von Anmeldeinformationen ist, die zurückgesendet werden (das gesamte JWT-Token, nicht nur der idToken-Teil davon). Einfach, sobald Sie es wissen ..

import jwt_decode from 'jwt-decode';

var token = 'eyJ0eXAiO.../// jwt token';
var decoded = jwt_decode(token);

/*{exp: 10012016 name: john doe, scope:['admin']}*/

Campo Blanco
quelle
2
Es ist nicht sehr hilfreich, genau die gleiche Antwort wie ein anderer Benutzer zu veröffentlichen, die ebenfalls gegen das
verstößt,
2

Einfache NodeJS-Lösung zum Dekodieren eines JSON-Web-Tokens (JWT)

function decodeTokenComponent(value) {
    const buff = new Buffer(value, 'base64')
    const text = buff.toString('ascii')
    return JSON.parse(text)
}

const token = 'xxxxxxxxx.XXXXXXXX.xxxxxxxx'
const [headerEncoded, payloadEncoded, signature] = token.split('.')
const [header, payload] = [headerEncoded, payloadEncoded].map(decodeTokenComponent)

console.log(`header: ${header}`)
console.log(`payload: ${payload}`)
console.log(`signature: ${signature}`)
Derek Soike
quelle
2

Antwort basierend auf GitHub - auth0 / jwt-decode . Die Eingabe / Ausgabe wurde so geändert, dass sie das Teilen von Zeichenfolgen und das Rückgabeobjekt {Header, Payload, Signatur} enthält, sodass Sie einfach das gesamte Token übergeben können.

var jwtDecode = function (jwt) {

        function b64DecodeUnicode(str) {
            return decodeURIComponent(atob(str).replace(/(.)/g, function (m, p) {
                var code = p.charCodeAt(0).toString(16).toUpperCase();
                if (code.length < 2) {
                    code = '0' + code;
                }
                return '%' + code;
            }));
        }

        function decode(str) {
            var output = str.replace(/-/g, "+").replace(/_/g, "/");
            switch (output.length % 4) {
                case 0:
                    break;
                case 2:
                    output += "==";
                    break;
                case 3:
                    output += "=";
                    break;
                default:
                    throw "Illegal base64url string!";
            }

            try {
                return b64DecodeUnicode(output);
            } catch (err) {
                return atob(output);
            }
        }

        var jwtArray = jwt.split('.');

        return {
            header: decode(jwtArray[0]),
            payload: decode(jwtArray[1]),
            signature: decode(jwtArray[2])
        };

    };
Calingasan
quelle
1

Hier ist eine funktionsreichere Lösung, die ich gerade nach dem Studium dieser Frage gemacht habe:

const parseJwt = (token) => {
    try {
        if (!token) {
            throw new Error('parseJwt# Token is required.');
        }

        const base64Payload = token.split('.')[1];
        let payload = new Uint8Array();

        try {
            payload = Buffer.from(base64Payload, 'base64');
        } catch (err) {
            throw new Error(`parseJwt# Malformed token: ${err}`);
        }

        return {
            decodedToken: JSON.parse(payload),
        };
    } catch (err) {
        console.log(`Bonus logging: ${err}`);

        return {
            error: 'Unable to decode token.',
        };
    }
};

Hier sind einige Anwendungsbeispiele:

const unhappy_path1 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvtmalformedtoken');
console.log('unhappy_path1', unhappy_path1);

const unhappy_path2 = parseJwt('sk4u7vgbis4ewku7gvtybrose4ui7gvt.malformedtoken');
console.log('unhappy_path2', unhappy_path2);

const unhappy_path3 = parseJwt();
console.log('unhappy_path3', unhappy_path3);

const { error, decodedToken } = parseJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c');
if (!decodedToken.exp) {
    console.log('almost_happy_path: token has illegal claims (missing expires_at timestamp)', decodedToken);
    // note: exp, iat, iss, jti, nbf, prv, sub
}

Ich war nicht in der Lage, dies im StackOverflow-Code-Snippet-Tool ausführbar zu machen, aber hier ist ungefähr das, was Sie sehen würden, wenn Sie diesen Code ausführen würden:

Geben Sie hier die Bildbeschreibung ein

Ich habe das gemacht parseJwt Funktion immer ein Objekt zurückgibt (bis zu einem gewissen Grad aus Gründen der statischen Typisierung).

Auf diese Weise können Sie folgende Syntax verwenden:

const { decodedToken, error } = parseJwt(token);

Anschließend können Sie zur Laufzeit auf bestimmte Fehlertypen testen und Namenskollisionen vermeiden.

Wenn sich jemand Änderungen mit geringem Aufwand und hohem Wert für diesen Code vorstellen kann, können Sie meine Antwort zum Nutzen von bearbeiten next(person).

agm1984
quelle
0

Basierend auf Antworten hier und hier :

const dashRE = /-/g;
const lodashRE = /_/g;

module.exports = function jwtDecode(tokenStr) {
  const base64Url = tokenStr.split('.')[1];
  if (base64Url === undefined) return null;
  const base64 = base64Url.replace(dashRE, '+').replace(lodashRE, '/');
  const jsonStr = Buffer.from(base64, 'base64').toString();
  return JSON.parse(jsonStr);
};
Webjay
quelle
-1

Mit Javascript node.js express musste ich das Paket zuerst wie folgt installieren:

npm install jwt-decode --save

dann in meinem app.js Code bekomme ich das Paket:

const jwt_decode = require('jwt-decode');

Führen Sie dann den Code aus:

let jwt_decoded = jwt_decode(jwt_source);

Dann die Magie:

console.log('sub:',jwt_decoded.sub);
David White
quelle
4
Denken Sie daran, "ohne eine Bibliothek zu benutzen"
Olaf
1
OK Fair genug. Ich hatte jedoch das gleiche Problem und hatte nicht die Einschränkung, keine Bibliothek verwenden zu können. Das hat bei mir funktioniert. Ich lasse es auf dem Laufenden, da möglicherweise jemand anderes vor einem ähnlichen Problem steht und nicht die gleiche Einschränkung hat.
David White