¿Cómo decodificar el token jwt en javascript sin usar una biblioteca?

210

¿Cómo puedo decodificar la carga útil de JWT usando JavaScript? Sin una biblioteca Entonces, el token solo devuelve un objeto de carga útil que puede consumir mi aplicación front-end.

Token de ejemplo: xxxxxxxxx.XXXXXXXX.xxxxxxxx

Y el resultado es la carga útil:

{exp: 10012016 name: john doe, scope:['admin']}
Chrisk8er
fuente
1
¿Cómo fue codificado? Solo haz lo contrario. Necesitarás el secreto compartido.
Lucky Soni
Fue codificado por una API de backend que utilizaba la biblioteca php Aquí necesito la carga útil que codificó usando base64, supongo ...
Chrisk8er
1
Puede intentar ir al sitio web jwt.io y obtener la biblioteca de JavaScript que proporciona.
Quentin
12
Dado que esta pregunta tiene algo de tráfico, quiero agregar un descargo de responsabilidad: si decodifica ciegamente la carga útil del token, sin validar la firma, ¡puede (o no) tener problemas de seguridad! Asegúrese de comprender su arquitectura de seguridad, antes de usar ciegamente cualquier código provisto en esta pregunta de stackoverflow.
Carsten Hoffmann el
55
@CarstenHoffmann ¿Y cómo valido exactamente la firma?
Saurabh Tiwari

Respuestas:

469

Función de analizador JWT de texto unicode de trabajo:

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
fuente
2
Lamentablemente, esto no parece funcionar con texto Unicode.
Paul McMahon
2
Esta solución incluso se puede usar en Postman (pruebas de tap) porque no requiere ninguna instalación adicional de la biblioteca. Lo usé para extraer el ID de usuario del token de autenticación.
Wlad
2
NOTA: En Postman tuve que quitar "ventana" JSON.parse(window.atob(base64))para que funcione. Justo return JSON.parse(atob(base64));y luego postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" es en mi caso la clave del valor del token en respuesta (puede diferir en su caso).
Wlad
12
La solución anterior solo reemplaza el primer "-" y "_" en el token (una "característica" de JavaScript que me sigue causando dolor). Simplemente reemplace la tercera línea en la respuesta con:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole
2
Es mejor usar el jwt-decodemódulo porque es pequeño pero tiene un manejo un poco mejor.
Rantiev
64

Función simple con try - catch

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

¡Gracias!

Rajan Maharjan
fuente
agradable, conciso y utiliza todos los métodos nativos!
Chris Love
2
atobha conocido problemas Unicode
Tamer Shlash
47

Puede usar jwt-decode , para poder escribir:

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']}*/
Chico
fuente
67
"Quiero decir que no hay biblioteca".
SherloxTV
Son problemas con esta biblioteca. Principalmente con Firefox en uso. El problema con el que me encontré fue que si un token == nulo resultaba de cerrar sesión o caducar; que esto simplemente mata la página con un error.
LUser
1
@ApertureSecurity necesita detectar este error, pero es cierto que es por eso que no quiero usar esta biblioteca
Luke Robertson
Esto no parece ser compatible con GZIP. De hecho, no puedo encontrar ninguna biblioteca JS que admita GZIP para las reclamaciones.
Andrew T Finnell
18

puedes usar la atob()función de javascript puro para decodificar el token en una cadena:

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

o analizarlo directamente en un objeto json:

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

leer sobre atob()y btoa()funciones integradas de javascript Base64 codificación y decodificación - Web API | MDN .

Muhammed Moussa
fuente
9

@Peheje funcionará, pero tendrá problemas con Unicode. Para solucionarlo, uso el código en 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
fuente
+1 pero si el comentario de Racing Tadpole sobre la respuesta de Peheje es correcto (que las llamadas de reemplazo solo reemplazarán la primera instancia), entonces la misma solución se aplicaría aquí.
Gary McGill
9

Como el objeto "ventana" no está presente en el entorno de nodejs, podríamos usar las siguientes líneas de código:

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

Me está funcionando perfectamente. Espero eso ayude.

Avik
fuente
1
respuesta perfecta para el 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);

Si usa el nodo, es posible que deba usar el paquete de búfer:

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

Utilizo esta función para obtener carga útil, encabezado, exp (tiempo de caducidad), iat (emitido en) en función de esta respuesta

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
fuente
Esta respuesta es algo mejor, pero tiene dos problemas y medio. Primero, no verifica la firma (elemento de matriz 2). En segundo lugar, los REEMPLAZOS no funcionarán correctamente, ya que omiten la bandera "g" en la expresión regular (solo reemplazará las primeras apariciones de - y _ en el JWT, como comentó Racing Tadpole en otra publicación). Y la mitad: para decodificar los elementos de matriz 0 y 1, podría haber utilizado un bucle FOR, en lugar de duplicar todo el código (es un código corto, pero podría hacerse más eficiente, como es, SPLIT se ejecuta dos veces )
Cyberknight
4

Todas las características de jwt.io no son compatibles con todos los idiomas. En NodeJs puedes usar

var decoded = jwt.decode(token);
Jithin Vijayan
fuente
1
Sin la biblioteca, solo realiza la decodificación base64 en la segunda parte del token {var payload = token.split ('.') [1]); } Luego realice la decodificación base64 {var decodedData = atob (carga útil); }
Jithin Vijayan
4

Encontré este código en jwt.io y funciona 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;
  }
}

En algunos casos (ciertas plataformas de desarrollo),
la mejor respuesta (por ahora) enfrenta un problema de longitud base64 no válida.
Entonces, necesitaba una forma más estable.

Espero que te ayude.

Nao Ito
fuente
2

Tanto Guy como Peheje ya respondieron la pregunta. Para un principiante total como yo, también fue útil tener la línea de importación definida en el ejemplo.

También me llevó algunos minutos darme cuenta de que el token es el conjunto completo de credenciales que se publican de nuevo (todo el token JWT, no solo la parte idToken). Directo una vez que lo sabes ...

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
fuente
2
Publicar exactamente la misma respuesta que otro usuario que también va en contra de lo que OP solicitó no es muy útil
Cacoon
2

Solución NodeJS simple para decodificar 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}`)
Derek Soike
fuente
2

Respuesta basada en GitHub - auth0 / jwt-decode . Se modificó la entrada / salida para incluir la división de cadenas y el objeto de retorno {encabezado, carga útil, firma} para que pueda pasar todo el 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])
        };

    };
calingasan
fuente
1

Aquí hay una solución más rica en funciones que acabo de hacer después de estudiar esta pregunta:

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

Aquí hay algunas muestras de uso:

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
}

No pude hacer que se pueda ejecutar en la herramienta de fragmento de código StackOverflow, pero esto es aproximadamente lo que vería si ejecutara ese código:

ingrese la descripción de la imagen aquí

Hice que la parseJwtfunción siempre devuelva un objeto (hasta cierto punto por razones de tipeo estático).

Esto le permite utilizar sintaxis como:

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

Luego, puede probar en tiempo de ejecución para tipos específicos de errores y evitar cualquier colisión de nombres.

Si alguien puede pensar en cambios de bajo esfuerzo y alto valor en este código, no dude en editar mi respuesta para beneficio de next(person).

agm1984
fuente
0

Basado en respuestas aquí y aquí :

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
fuente
-1

Al ejecutar Javascript node.js express, primero tuve que instalar el paquete de la siguiente manera:

npm install jwt-decode --save

luego en mi código app.js obtengo el paquete:

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

Luego ejecuta el código:

let jwt_decoded = jwt_decode(jwt_source);

Entonces la magia:

console.log('sub:',jwt_decoded.sub);
David White
fuente
44
recuerde "sin usar una biblioteca"
Olaf
1
OK bastante justo. Sin embargo, estaba enfrentando el mismo problema y no tenía la restricción de no poder usar una biblioteca. Esto funcionó para mí. Lo dejo publicado ya que tal vez alguien más se enfrenta a un problema similar y no tiene la misma restricción.
David White