Comment décoder le jeton jwt en javascript sans utiliser de bibliothèque?

210

Comment puis-je décoder la charge utile de JWT en utilisant JavaScript? Sans bibliothèque. Ainsi, le jeton renvoie simplement un objet de charge utile qui peut être consommé par mon application frontale.

Exemple de jeton: xxxxxxxxx.XXXXXXXX.xxxxxxxx

Et le résultat est la charge utile:

{exp: 10012016 name: john doe, scope:['admin']}
Chrisk8er
la source
1
Comment était-il codé? Faites l'inverse. Vous aurez besoin du secret partagé.
Lucky Soni
Il a été encodé par une API backend qui a utilisé la bibliothèque php. Ici, j'ai besoin de la charge utile encodée en base64, je suppose ...
Chrisk8er
1
Vous pouvez essayer d'aller sur le site Web jwt.io et d'obtenir la bibliothèque JavaScript qu'il fournit.
Quentin
12
Étant donné que cette question a du trafic, je veux ajouter un avertissement: si vous décodez aveuglément la charge utile du jeton, sans valider la signature, vous pouvez (ou non) rencontrer des problèmes de sécurité! Assurez-vous de bien comprendre votre architecture de sécurité, avant d'utiliser aveuglément tout code fourni dans cette question de stackoverflow.
Carsten Hoffmann
5
@CarstenHoffmann Et comment puis-je valider exactement la signature ??
Saurabh Tiwari

Réponses:

469

Fonction d'analyseur JWT de texte unicode de travail:

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
la source
2
Malheureusement, cela ne semble pas fonctionner avec du texte Unicode.
Paul McMahon
2
Cette solution peut même être utilisée dans Postman (test tap) car elle ne nécessite aucune installation de bibliothèque supplémentaire. Je l'ai utilisé pour extraire l'ID utilisateur du jeton d'authentification.
Wlad
2
REMARQUE: Dans Postman, j'ai dû supprimer "window" JSON.parse(window.atob(base64))pour que cela fonctionne. Juste return JSON.parse(atob(base64));après, postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" est dans mon cas la clé de la valeur du token en réponse (peut différer dans votre cas).
Wlad
12
La solution ci-dessus ne remplace que les premiers "-" et "_" dans le jeton (une "fonctionnalité" javascript qui me fait toujours mal). Remplacez simplement la troisième ligne de la réponse par:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole
2
Il est préférable d'utiliser le jwt-decodemodule car il est petit mais offre une meilleure gestion.
Rantiev
64

Fonction simple avec try - catch

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

Merci!

Rajan Maharjan
la source
agréable, concis et utilise toutes les méthodes natives!
Chris Love
2
atoba connu des problèmes Unicode
Tamer Shlash
47

Vous pouvez utiliser jwt-decode , vous pouvez donc écrire:

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']}*/
Gars
la source
67
"Je veux dire pas de bibliothèque."
SherloxTV le
Ce sont des problèmes avec cette bibliothèque. Principalement avec Firefox utilisé. Le problème que j'ai rencontré était que si un jeton == null résultant de la déconnexion ou de l'expiration; que cela tue juste la page avec une erreur.
LUser
1
@ApertureSecurity vous devez attraper cette erreur, mais il est vrai que c'est pourquoi je ne veux pas utiliser cette bibliothèque
Luke Robertson
Cela ne semble pas prendre en charge GZIP. En fait, je ne trouve aucune bibliothèque JS prenant en charge GZIP pour les revendications.
Andrew T Finnell
18

vous pouvez utiliser la atob()fonction javascript pur pour décoder le jeton en une chaîne:

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

ou l'analyser directement dans un objet json:

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

lire atob()et btoa()fonctions javascript intégrées Encodage et décodage Base64 - API Web | MDN .

Muhammed Moussa
la source
9

@Peheje fonctionnera, mais vous aurez un problème avec unicode. Pour le corriger, j'utilise le code sur 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
la source
+1 mais si le commentaire de Racing Tadpole sur la réponse de Peheje est correct (que les appels de remplacement ne remplaceront que la première instance), alors le même correctif s'appliquerait ici.
Gary McGill
9

Comme l'objet "window" n'est pas présent dans l'environnement nodejs, nous pourrions utiliser les lignes de code suivantes:

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

Cela fonctionne parfaitement pour moi. J'espère que ça aide.

Avik
la source
1
réponse parfaite pour le noeud 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);

Si vous utilisez un nœud, vous devrez peut-être utiliser un package de tampon:

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

J'utilise cette fonction pour obtenir la charge utile, l'en-tête, exp (heure d'expiration), iat (émis à) en fonction de cette réponse

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
la source
Cette réponse est un peu meilleure, mais elle comporte deux problèmes et demi. Tout d'abord, il ne vérifie pas la signature (élément de tableau 2). Deuxièmement, les REMPLACEMENTS ne fonctionneront pas correctement, car ils manquent le drapeau "g" sur l'expression régulière (ne remplacera que les premières occurrences de - et _ sur le JWT, comme Racing Tadpole l'a commenté dans un autre post). Et la moitié: pour décoder les éléments de tableau 0 et 1, vous auriez pu utiliser une boucle FOR, au lieu de dupliquer tout le code (c'est un code court, mais pourrait être rendu plus efficace, comme c'est le cas, le SPLIT est exécuté deux fois ).
Cyberknight
4

toutes les fonctionnalités de jwt.io ne prennent pas en charge toutes les langues. Dans NodeJs, vous pouvez utiliser

var decoded = jwt.decode(token);
Jithin Vijayan
la source
1
Sans bibliothèque, vous effectuez simplement le décodage base64 dans la deuxième partie du token {var payload = token.split ('.') [1]); } Effectuez ensuite le décodage base64 {var decodedData = atob (payload); }
Jithin Vijayan
4

J'ai trouvé ce code sur jwt.io et cela fonctionne bien.

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

Dans certains cas (certaines plates-formes de développement),
la meilleure réponse (pour l'instant) fait face à un problème de longueur de base64 non valide.
Donc, j'avais besoin d'un moyen plus stable.

J'espère que ça t'aidera.

Nao Ito
la source
2

Guy et Peheje ont déjà répondu à la question. Pour un débutant total comme moi, il était utile d'avoir également défini la ligne d'importation dans l'exemple.

De plus, il m'a fallu quelques minutes pour comprendre que le jeton est l'ensemble complet des informations d'identification qui sont publiées (le jeton JWT entier, pas seulement la partie idToken). Simple une fois que vous le savez ..

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
la source
2
Publier la même réponse exacte qu'un autre utilisateur qui va également à l'encontre de ce que OP a demandé n'est pas très utile
Cacoon
2

Solution NodeJS simple pour décoder un jeton 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}`)
Derek Soike
la source
2

Réponse basée sur GitHub - auth0 / jwt-decode . Modification de l'entrée / sortie pour inclure le fractionnement de chaîne et l'objet de retour {en-tête, charge utile, signature} afin que vous puissiez simplement passer tout le jeton.

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
la source
1

Voici une solution plus riche en fonctionnalités que je viens de faire après avoir étudié cette question:

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

Voici quelques exemples d'utilisation:

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
}

Je n'ai pas pu rendre ce fichier exécutable dans l'outil d'extrait de code StackOverflow, mais voici approximativement ce que vous verriez si vous exécutiez ce code:

entrez la description de l'image ici

J'ai fait que la parseJwtfonction retourne toujours un objet (dans une certaine mesure pour des raisons de typage statique).

Cela vous permet d'utiliser une syntaxe telle que:

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

Ensuite, vous pouvez tester au moment de l'exécution des types d'erreurs spécifiques et éviter toute collision de noms.

Si quelqu'un peut penser à un faible effort, à des modifications de grande valeur de ce code, n'hésitez pas à modifier ma réponse au profit de next(person).

agm1984
la source
0

Basé sur les réponses ici et ici :

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
la source
-1

En exécutant Javascript node.js express, j'ai d'abord dû installer le package comme suit:

npm install jwt-decode --save

puis dans mon code app.js récupérez le package:

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

Exécutez ensuite le code:

let jwt_decoded = jwt_decode(jwt_source);

Puis la magie:

console.log('sub:',jwt_decoded.sub);
David White
la source
4
rappelez-vous "sans utiliser de bibliothèque"
Olaf
1
OK très bien. Cependant, j'étais confronté au même problème et je n'avais pas la restriction de ne pas pouvoir utiliser une bibliothèque. Cela a fonctionné pour moi. Je le laisse affiché car peut-être que quelqu'un d'autre fait face à un problème similaire et n'a pas la même restriction.
David White