Come decodificare il token jwt in javascript senza usare una libreria?


210

Come posso decodificare il payload di JWT usando JavaScript? Senza biblioteca Quindi il token restituisce solo un oggetto payload che può essere utilizzato dalla mia app front-end.

Token di esempio: xxxxxxxxx.XXXXXXXX.xxxxxxxx

E il risultato è il payload:

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

1
Come è stato codificato? Fai solo il contrario. Avrai bisogno del segreto condiviso.
Lucky Soni,

È stato codificato da API back-end che utilizzava la libreria php. Qui ho bisogno del payload codificato utilizzando
Base64,

1
Potresti provare a visitare il sito Web jwt.io e ottenere la libreria JavaScript fornita.
Quentin,

12
Dato che questa domanda ha un po 'di traffico, voglio aggiungere una dichiarazione di non responsabilità: se decodifichi ciecamente il payload del token, senza convalidare la firma, potresti incorrere (o meno) in problemi di sicurezza! Assicurati di comprendere l'architettura di sicurezza, prima di utilizzare ciecamente qualsiasi codice fornito in questa domanda di StackOverflow.
Carsten Hoffmann,

5
@CarstenHoffmann E come convalidare esattamente la firma ??
Saurabh Tiwari,

Risposte:


469

Funzione parser JWT di testo Unicode funzionante:

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);
};

2
Sfortunatamente questo non sembra funzionare con il testo Unicode.
Paul McMahon,

2
Questa soluzione può essere utilizzata anche in Postman (test tap) perché non richiede alcuna installazione di libreria aggiuntiva. L'ho usato per estrarre userid dal token di autenticazione.
Wlad

2
NOTA: In Postman ho dovuto rimuovere "window" da JSON.parse(window.atob(base64))per farlo funzionare. Giusto return JSON.parse(atob(base64));e quindi postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" è nel mio caso la chiave del valore del token in risposta (potrebbe differire nel tuo caso).
Salato il

12
La soluzione sopra sostituisce solo il primo "-" e "_" nel token (una "funzione" javascript che continua a farmi male). Basta sostituire la terza riga nella risposta con:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole

2
È meglio usare il jwt-decodemodulo perché è piccolo ma fa un po 'meglio la gestione.
Rantiev,


47

Puoi usare jwt-decode , quindi puoi scrivere:

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']}*/

67
"Intendo nessuna biblioteca."
SherloxTV

Sono problemi con questa libreria. Principalmente con Firefox in uso. Il problema che ho riscontrato è che se un token == null risultante dalla disconnessione o dalla scadenza; che questo uccide solo la pagina con un errore.
LUser

1
@ApertureSecurity è necessario rilevare questo errore, ma è vero che questo è il motivo per cui non voglio usare questa libreria
Luke Robertson,

Questo non sembra supportare GZIP. In effetti, non riesco a trovare alcuna libreria JS che supporti GZIP per i reclami.
Andrew T Finnell,

18

puoi usare la pura atob()funzione JavaScript per decodificare il token in una stringa:

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

o analizzalo direttamente in un oggetto json:

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

leggere atob()e btoa()incorporare le funzioni javascript Codifica e decodifica Base64 - API Web | MDN .


9

@Peheje funzionerà, ma avrai problemi con Unicode. Per risolverlo uso il codice su 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>


+1 ma se il commento di Racing Tadpole sulla risposta di Peheje è corretto (che le chiamate di sostituzione sostituiranno solo la prima istanza), la stessa correzione si applicherebbe qui.
Gary McGill,

9

Poiché l'oggetto "finestra" non è presente nell'ambiente nodejs, è possibile utilizzare le seguenti righe di codice:

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

Funziona perfettamente per me. Spero che sia d'aiuto.


1
risposta perfetta per il nodo js
ireshan pathirana

7
function parseJwt(token) {
  var base64Payload = token.split('.')[1];
  var payload = Buffer.from(base64Payload, 'base64');
  return JSON.parse(payload.tostring());
}
let payload= parseJwt("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c");
console.log("payload:- ", payload);

Se si utilizza il nodo, potrebbe essere necessario utilizzare il pacchetto buffer:

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

6

Uso questa funzione per ottenere payload, header, exp (Expiration Time), iat (Issued At) in base a questa risposta

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)
}

Questa risposta è in qualche modo migliore, ma ha due problemi e mezzo. Innanzitutto, non controlla la firma (elemento dell'array 2). In secondo luogo, i REPLACE non funzioneranno correttamente, perché mancano il flag "g" sulla regex (sostituiranno solo le prime occorrenze di - e _ sul JWT, come Racing Tadpole ha commentato un altro post). E la metà: per decodificare gli elementi dell'array 0 e 1, avresti potuto usare un ciclo FOR, invece di duplicare l'intero codice (è un codice breve, ma potrebbe essere reso più efficiente, così com'è, lo SPLIT viene eseguito due volte ).
Cyberknight,

4

tutte le funzionalità di jwt.io non supportano tutte le lingue. In NodeJs puoi usare

var decoded = jwt.decode(token);

1
Senza libreria esegui semplicemente la decodifica base64 nella seconda parte del token {var payload = token.split ('.') [1]); } Quindi eseguire la decodifica base64 {var decodedData = atob (payload); }
Jithin Vijayan,

4

Ho trovato questo codice su jwt.io e funziona bene.

//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 alcuni casi (alcune piattaforme di sviluppo),
la risposta migliore (per ora) deve affrontare un problema di lunghezza base64 non valida.
Quindi, avevo bisogno di un modo più stabile.

Spero che ti possa aiutare.


2

Guy e Peheje hanno già risposto alla domanda. Per un principiante totale come me è stato utile avere anche la linea di importazione definita nell'esempio.

Inoltre mi ci sono voluti alcuni minuti per capire che il token è l'insieme completo di credenziali che viene registrato (l'intero token JWT, non solo la parte idToken di esso). Semplice una volta che lo sai ..

import jwt_decode from 'jwt-decode';

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

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


2
Pubblicare la stessa identica risposta di un altro utente che va anche contro ciò che OP ha richiesto non è molto utile
Cacoon

2

Soluzione NodeJS semplice per la decodifica di un token Web JSON (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}`)

2

Risposta basata su GitHub - auth0 / jwt-decode . Modificato l'input / output per includere la suddivisione della stringa e restituire l'oggetto {header, payload, signature} in modo da poter semplicemente passare l'intero token.

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])
        };

    };

1

Ecco una soluzione più ricca di funzionalità che ho appena fatto dopo aver studiato questa domanda:

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.',
        };
    }
};

Ecco alcuni esempi di utilizzo:

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
}

Non sono stato in grado di renderlo eseguibile nello strumento frammento di codice StackOverflow, ma ecco approssimativamente cosa vedresti se eseguissi quel codice:

inserisci qui la descrizione dell'immagine

Ho fatto il parseJwt funzione restituire sempre un oggetto (in una certa misura per motivi di digitazione statica).

Ciò consente di utilizzare la sintassi come:

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

Quindi è possibile verificare in fase di esecuzione specifici tipi di errori ed evitare qualsiasi collisione di denominazione.

Se qualcuno può pensare a qualche sforzo basso, modifiche di alto valore a questo codice, sentiti libero di modificare la mia risposta a beneficio di next(person).


0

Sulla base delle risposte qui e qui :

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);
};

-1

Eseguendo Javascript node.js express dovevo prima installare il pacchetto come segue:

npm install jwt-decode --save

quindi nel mio codice app.js ottenere il pacchetto:

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

Quindi eseguire il codice:

let jwt_decoded = jwt_decode(jwt_source);

Quindi la magia:

console.log('sub:',jwt_decoded.sub);

4
ricorda "senza usare una biblioteca"
Olaf

1
ok abbastanza giusto. Tuttavia, stavo affrontando lo stesso problema e non avevo la limitazione di non poter utilizzare una libreria. Questo ha funzionato per me. Lo lascio pubblicato perché forse qualcun altro deve affrontare un problema simile e non ha le stesse restrizioni.
David White,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.