Bagaimana cara mendekode token jwt di javascript tanpa menggunakan perpustakaan?

210

Bagaimana saya bisa men-decode payload JWT menggunakan JavaScript? Tanpa perpustakaan. Jadi token hanya mengembalikan objek muatan yang dapat dikonsumsi oleh aplikasi front-end saya.

Token contoh: xxxxxxxxx.XXXXXXXX.xxxxxxxx

Dan hasilnya adalah payload:

{exp: 10012016 name: john doe, scope:['admin']}
Chrisk8er
sumber
1
Bagaimana cara disandikan? Lakukan sebaliknya. Anda akan membutuhkan rahasia bersama.
Lucky Soni
Itu dikodekan oleh api backend yang menggunakan perpustakaan php. Di sini yang saya butuhkan adalah muatan yang disandikan menggunakan base64 kurasa ...
Chrisk8er
1
Anda dapat mencoba mengunjungi situs web jwt.io dan mendapatkan perpustakaan JavaScript yang disediakannya.
Quentin
12
Karena pertanyaan ini memiliki beberapa lalu lintas, saya ingin menambahkan penafian: Jika Anda membongkar kode pembayaran secara membabi buta, tanpa memvalidasi tanda tangan, Anda mungkin (atau mungkin tidak) mengalami masalah keamanan! Pastikan Anda memahami arsitektur keamanan Anda, sebelum secara buta menggunakan kode apa pun yang disediakan dalam pertanyaan stackoverflow ini.
Carsten Hoffmann
5
@CarstenHoffmann Dan bagaimana tepatnya saya memvalidasi tanda tangan ??
Saurabh Tiwari

Jawaban:

469

Fungsi parser JWT teks unicode:

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
sumber
2
Sayangnya ini sepertinya tidak berfungsi dengan teks unicode.
Paul McMahon
2
Solusi ini bahkan dapat digunakan dalam tukang pos (ketukan tes) karena tidak memerlukan instalasi perpustakaan tambahan. Saya menggunakannya untuk mengekstrak userid dari auth-token.
Wlad
2
CATATAN: Di tukang pos saya harus menghapus "jendela" dari JSON.parse(window.atob(base64))untuk membuatnya berfungsi. Tepat return JSON.parse(atob(base64));dan kemudian postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); "access_token" dalam kasus saya adalah kunci nilai token sebagai respons (mungkin berbeda dalam kasus Anda).
Wlad
12
Solusi di atas hanya menggantikan yang pertama "-" dan "_" di token (fitur "javascript" yang membuat saya sakit). Cukup ganti baris ketiga dalam jawaban dengan:var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
Balap Kecebong
2
Lebih baik menggunakan jwt-decodemodul karena modulnya kecil tetapi penanganannya sedikit lebih baik.
Rantiev
64

Fungsi sederhana dengan try-catch

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

Terima kasih!

Rajan Maharjan
sumber
bagus, ringkas dan menggunakan semua metode asli!
Chris Love
2
atobtelah mengetahui masalah unicode
Tamer Shlash
47

Anda bisa menggunakan jwt-decode , jadi Anda bisa menulis:

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']}*/
Orang
sumber
67
"Maksudku, tidak ada perpustakaan."
SherloxTV
Mereka bermasalah dengan perpustakaan ini. Terutama dengan firefox yang digunakan. Masalah yang saya temui adalah bahwa jika token == null dihasilkan dari logout atau kedaluwarsa; bahwa ini hanya membunuh halaman dengan kesalahan.
LUser
1
@ApertureSecurity Anda perlu menangkap kesalahan ini, tetapi memang ini sebabnya saya tidak ingin menggunakan perpustakaan ini
Luke Robertson
Ini sepertinya tidak mendukung GZIP. Bahkan, saya tidak dapat menemukan perpustakaan JS yang mendukung GZIP untuk klaim.
Andrew T Finnell
18

Anda dapat menggunakan atob()fungsi javascript murni untuk mendekode token menjadi string:

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

atau parsing langsung menjadi objek json:

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

membaca tentang atob()dan btoa()fungsi javascript bawaan Pengkodean dan penguraian base64 - API Web | MDN .

Muhammed Moussa
sumber
9

@ Peekje akan berfungsi, tetapi Anda akan memiliki masalah dengan unicode. Untuk memperbaikinya saya menggunakan kode di 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
sumber
+1 tetapi jika komentar Racing Tadpole pada jawaban Peheje benar (bahwa panggilan penggantian hanya akan menggantikan contoh pertama), maka perbaikan yang sama akan berlaku di sini.
Gary McGill
9

Karena objek "window" tidak ada di lingkungan nodejs, kita bisa menggunakan baris kode berikut:

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

Ini bekerja untuk saya dengan sempurna. Semoga ini bisa membantu.

Avik
sumber
1
jawaban sempurna untuk simpul 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);

Jika menggunakan node, Anda mungkin harus menggunakan paket buffer:

npm install buffer
var Buffer = require('buffer/').Buffer
sudah termasuk72
sumber
6

Saya menggunakan fungsi ini untuk mendapatkan payload, header, exp (Expiration Time), iat (Issued At) berdasarkan jawaban ini

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
sumber
Jawaban ini agak lebih baik, tetapi memiliki dua setengah masalah. Pertama, itu tidak memeriksa tanda tangan (item array 2). Kedua, REPLACE tidak akan berfungsi dengan benar, karena mereka kehilangan bendera "g" di regex (hanya akan menggantikan kejadian pertama - dan _ di JWT, seperti Racing Tadpole mengomentari pos lain). Dan setengahnya: untuk mendekode item array 0 dan 1, Anda bisa menggunakan loop FOR, daripada menduplikasi seluruh kode (ini adalah kode pendek, tetapi bisa dibuat lebih efisien, seperti itu, SPLIT dijalankan dua kali ).
Cyberknight
4

semua fitur jwt.io tidak mendukung semua bahasa. Di NodeJs Anda dapat menggunakan

var decoded = jwt.decode(token);
Jithin Vijayan
sumber
1
Tanpa pustaka Anda hanya melakukan decoding base64 di bagian kedua token {var payload = token.split ('.') [1]); } Kemudian lakukan decoding base64 {var decodedData = atob (payload); }
Jithin Vijayan
4

Saya menemukan kode ini di jwt.io dan berfungsi dengan baik.

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

Dalam beberapa kasus (platform pengembangan tertentu),
jawaban terbaik (untuk saat ini) menghadapi masalah panjang base64 yang tidak valid.
Jadi, saya membutuhkan cara yang lebih stabil.

Saya harap ini akan membantu Anda.

Nao Ito
sumber
2

Guy dan Peheje sudah menjawab pertanyaan itu. Untuk pemula total seperti saya, sangat membantu untuk memiliki garis impor yang ditentukan dalam contoh.

Juga butuh beberapa menit bagi saya untuk mengetahui bahwa token adalah set lengkap kredensial yang akan diposting kembali (seluruh token JWT, bukan hanya bagian idToken dari itu). Langsung setelah Anda mengetahuinya ..

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
sumber
2
Posting jawaban yang sama persis dengan pengguna lain yang juga bertentangan dengan apa yang diminta OP tidak terlalu membantu
Cacoon
2

Solusi NodeJS Sederhana untuk Mendekode 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
sumber
2

Jawaban berdasarkan dari GitHub - auth0 / jwt-decode . Mengubah input / output untuk memasukkan string splitting dan mengembalikan objek {header, payload, signature} sehingga Anda bisa meneruskan token keseluruhan.

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

Berikut adalah solusi yang lebih kaya fitur yang baru saya buat setelah mempelajari pertanyaan ini:

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

Berikut beberapa contoh penggunaan:

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
}

Saya tidak dapat membuat runnable itu di alat snippet kode StackOverflow, tapi di sini kira-kira apa yang akan Anda lihat jika Anda menjalankan kode itu:

masukkan deskripsi gambar di sini

Saya membuat parseJwtfungsi selalu mengembalikan objek (pada tingkat tertentu karena alasan mengetik statis).

Ini memungkinkan Anda untuk menggunakan sintaksis seperti:

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

Kemudian Anda dapat menguji pada waktu berjalan untuk jenis kesalahan tertentu dan menghindari tabrakan penamaan.

Jika ada yang bisa memikirkan upaya rendah, perubahan bernilai tinggi pada kode ini, jangan ragu untuk mengedit jawaban saya untuk kepentingan next(person).

agm1984
sumber
0

Berdasarkan jawaban di sini dan di sini :

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

Menjalankan Javascript node.js express, saya harus menginstal paket sebagai berikut:

npm install jwt-decode --save

lalu di kode app.js saya dapatkan paket:

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

Kemudian jalankan kodenya:

let jwt_decoded = jwt_decode(jwt_source);

Kemudian keajaiban:

console.log('sub:',jwt_decoded.sub);
David White
sumber
4
ingat "tanpa menggunakan perpustakaan"
Olaf
1
OK cukup adil. Namun, saya menghadapi masalah yang sama dan saya tidak memiliki batasan untuk tidak dapat menggunakan perpustakaan. Ini berhasil untuk saya. Saya membiarkannya diposting karena mungkin orang lain menghadapi masalah yang sama dan tidak memiliki batasan yang sama.
David White