Como decodificar o token jwt em javascript sem usar uma biblioteca?

209

Como decodificar a carga útil do JWT usando JavaScript? Sem uma biblioteca. Portanto, o token retorna apenas um objeto de carga útil que pode ser consumido pelo meu aplicativo front-end.

Exemplo de token: xxxxxxxxx.XXXXXXXX.xxxxxxxx

E o resultado é a carga útil:

{exp: 10012016 name: john doe, scope:['admin']}
Chrisk8er
fonte
1
Como foi codificado? Apenas faça o contrário. Você precisará do segredo compartilhado.
Lucky Soni
Foi codificado pela API de back-end que usava a biblioteca php. Aqui necessidade i é a carga que codificados usando base64 eu acho ...
Chrisk8er
1
Você pode tentar acessar o site jwt.io e obter a biblioteca JavaScript que ele fornece.
Quentin
12
Como essa pergunta tem algum tráfego, desejo adicionar um aviso de isenção de responsabilidade: se você decodificar cegamente a carga útil do token, sem validar a assinatura, poderá (ou não) ter problemas de segurança! Certifique-se de entender sua arquitetura de segurança, antes de usar cegamente qualquer código fornecido nesta pergunta sobre o stackoverflow.
Carsten Hoffmann
5
@CarstenHoffmann E como exatamente valido a assinatura?
Saurabh Tiwari

Respostas:

468

Função de analisador JWT de texto unicode de trabalho:

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
fonte
2
Infelizmente, isso não parece funcionar com texto unicode.
Paul McMahon
2
Esta solução pode até ser usada no Postman (tap tap), pois não requer instalação de nenhuma biblioteca adicional. Eu o usei para extrair o ID do usuário do token de autenticação.
Wlad
2
NOTA: No Postman, tive que remover a "janela" JSON.parse(window.atob(base64))para fazê-la funcionar. Só return JSON.parse(atob(base64));então postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" é, no meu caso, a chave do valor do token em resposta (pode ser diferente no seu caso).
Wlad
12
A solução acima substitui apenas o primeiro "-" e "_" no token (um recurso "javascript" que continua me causando dor). Basta substituir a terceira linha na resposta por:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Racing Tadpole
2
É melhor usar o jwt-decodemódulo porque é pequeno, mas faz um manuseio um pouco mais melhor.
Rantiev 13/02/19
64

Função simples com try - catch

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

Obrigado!

Rajan Maharjan
fonte
agradável, conciso e usa todos os métodos nativos!
Chris Love
2
atobconheceu problemas unicode
Tamer Shlash 10/01
47

Você pode usar o jwt-decode , para escrever:

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']}*/
Cara
fonte
67
"Não quero dizer biblioteca."
precisa saber é o seguinte
Eles são problemas com esta biblioteca. Principalmente com o Firefox em uso. O problema que encontrei foi que, se um token == nulo resultante do logout ou expiração; que isso apenas mata a página com um erro.
luser
1
@ApertureSecurity, você precisa capturar esse erro, mas é por isso que é por isso que eu não quero usar esta biblioteca
Luke Robertson
Parece que isso não suporta o GZIP. De fato, não consigo encontrar nenhuma biblioteca JS compatível com GZIP para as reivindicações.
Andrew T Finnell
18

você pode usar a atob()função javascript pura para decodificar o token em uma string:

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

ou analise-o diretamente em um objeto json:

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

leia sobre atob()e btoa()funções JavaScript embutidas Codificação e decodificação Base64 - APIs da Web | MDN .

Muhammed Moussa
fonte
9

O @Peheje funcionará, mas você terá problemas com o Unicode. Para corrigi-lo, uso o código em 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
fonte
+1, mas se o comentário do Racing Tadpole sobre a resposta de Peheje estiver correto (que as chamadas de substituição substituirão apenas a primeira instância), a mesma correção será aplicada aqui.
Gary McGill
9

Como o objeto "window" não está presente no ambiente nodejs, podemos usar as seguintes linhas 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'));

Está funcionando perfeitamente para mim. Espero que ajude.

Avik
fonte
1
resposta perfeita para node js
ireshan pathirana 14/03
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 estiver usando o nó, talvez seja necessário usar o pacote de buffer:

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

Eu uso essa função para obter carga útil, cabeçalho, exp (tempo de expiração), iat (emitido em) com base nesta resposta

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
fonte
Essa resposta é um pouco melhor, mas tem dois problemas e meio. Primeiro, ele não verifica a assinatura (item 2 da matriz). Segundo, os REPLACEs não funcionarão corretamente, porque perdem o sinalizador "g" na regex (substituirá apenas as primeiras ocorrências de - e _ no JWT, como o Racing Tadpole comentou em outro post). E a metade: para decodificar os itens da matriz 0 e 1, você poderia ter usado um loop FOR, em vez de duplicar o código inteiro (é um código curto, mas pode ser mais eficiente, como é o caso, o SPLIT é executado duas vezes )
Cyberknight
4

todos os recursos do jwt.io não suportam todos os idiomas. Nos NodeJs você pode usar

var decoded = jwt.decode(token);
Jithin Vijayan
fonte
1
Sem a biblioteca, você apenas realiza a decodificação base64 na segunda parte do token {var payload = token.split ('.') [1]); } Execute a decodificação base64 {var decodedData = atob (carga útil); }
Jithin Vijayan 20/09/17
4

Encontrei esse código em jwt.io e funciona bem.

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

Em alguns casos (determinadas plataformas de desenvolvimento),
a melhor resposta (por enquanto) enfrenta um problema de comprimento base64 inválido.
Então, eu precisava de uma maneira mais estável.

Espero que ajude você.

Nao Ito
fonte
2

Guy e Peheje já responderam à pergunta. Para um iniciante como eu, foi útil também ter a linha de importação definida no exemplo.

Também levei alguns minutos para descobrir que o token é o conjunto completo de credenciais que são postadas de volta (o token JWT inteiro, não apenas a parte idToken). Simples, uma vez que você conhece ..

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
fonte
2
Publicação exatamente a mesma resposta como outro usuário que também vai contra o que OP pediu não é muito útil
Cacoon
2

Solução NodeJS simples para decodificar um JSON Web Token (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
fonte
2

Resposta baseada no GitHub - auth0 / jwt-decode . Alterou a entrada / saída para incluir a divisão de seqüência de caracteres e o objeto de retorno {cabeçalho, carga útil, assinatura} para que você possa passar o token inteiro.

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

Aqui está uma solução mais rica em recursos que acabei de criar após estudar esta pergunta:

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

Aqui estão alguns exemplos 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
}

Não consegui tornar esse executável na ferramenta de trecho de código StackOverflow, mas aqui está aproximadamente o que você veria se executasse esse código:

insira a descrição da imagem aqui

Eu fiz a parseJwtfunção sempre retornar um objeto (até certo ponto por razões de digitação estática).

Isso permite que você utilize sintaxe como:

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

Em seguida, você pode testar em tempo de execução tipos específicos de erros e evitar colisões de nomes.

Se alguém puder pensar em algum esforço baixo, alterações de alto valor nesse código, fique à vontade para editar minha resposta em benefício de next(person).

agm1984
fonte
0

Com base nas respostas aqui e aqui :

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

Executando o Javascript node.js express, primeiro tive que instalar o pacote da seguinte maneira:

npm install jwt-decode --save

então, no meu código app.js, obtenha o pacote:

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

Em seguida, execute o código:

let jwt_decoded = jwt_decode(jwt_source);

Então a mágica:

console.log('sub:',jwt_decoded.sub);
David White
fonte
4
lembre-se "sem usar uma biblioteca"
Olaf
1
ok justo o suficiente. No entanto, eu estava enfrentando o mesmo problema e não tinha a restrição de não poder usar uma biblioteca. Isso funcionou para mim. Deixo-o publicado, pois talvez alguém enfrente um problema semelhante e não tenha a mesma restrição.
David White