ライブラリを使用せずにJavaScriptでJWTトークンをデコードする方法は?


209

JavaScriptを使用してJWTのペイロードをどのようにデコードできますか?ライブラリなし。したがって、トークンは、私のフロントエンドアプリが使用できるペイロードオブジェクトを返すだけです。

トークンの例: xxxxxxxxx.XXXXXXXX.xxxxxxxx

そして結果はペイロードです:

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

1
どのようにエンコードされましたか?ちょうど逆を行います。共有シークレットが必要になります。
Lucky Soni、

PHPライブラリを使用するバックエンドAPIによってエンコードされました。ここで私が必要とするのは、base64を使用してエンコードされたペイロードです...
Chrisk8er

1
jwt.io Webサイトにアクセスして、それが提供するJavaScriptライブラリーを入手してみてください。
クエンティン

12
この質問にはトラフィックがあるため、免責事項を追加したいと思います。署名を検証せずにトークンのペイロードを盲目的にデコードすると、セキュリティの問題が発生する可能性があります(または発生しない可能性があります)。このスタックオーバーフローの質問で提供されたコードを盲目的に使用する前に、セキュリティアーキテクチャを理解してください。
Carsten Hoffmann

5
@CarstenHoffmannそして、どのように正確に署名を検証しますか?
Saurabh Tiwari

回答:


468

機能するユニコードテキストJWTパーサー関数:

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
残念ながら、これはユニコードテキストでは機能しないようです。
ポールマクマホン

2
このソリューションは、追加のライブラリのインストールを必要としないため、Postman(テストタップ)でも使用できます。auth-tokenからuseridを抽出するために使用しました。
2017年

2
注:Postmanでは、「ウィンドウ」を削除しJSON.parse(window.atob(base64))て機能させる必要がありました。ただ、return JSON.parse(atob(base64));その後、postman.setEnvironmentVariable("userId", parseJwt(jsonData.access_token)); 「access_tokenはは」(あなたのケースでは異なる場合があります)私の場合には、応答でのトークン値のキーです。
2017年

12
上記のソリューションでは、トークンの最初の「-」と「_」のみが置き換えられます(javascriptの「機能」が原因で痛みを引き起こし続けます)。:ちょうどと答えの3行目置き換えるvar base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
レーシングオタマジャクシ

2
jwt-decodeモジュールは小さいので使用する方が良いですが、処理が少し良くなります。
ランティエフ2018


47

jwt-decodeを使用できるため、次のように記述できます。

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
「図書館がないという意味です。」
SherloxTV 2017

彼らはこのライブラリの問題です。主にFirefoxを使用しています。私が遭遇した問題は、ログアウトまたは期限切れが原因でトークン== nullが発生した場合です。これはエラーでページを強制終了するだけです。
LUser

1
@ApertureSecurityあなたはこのエラーをキャッチする必要がありますが、確かにこれが私がこのライブラリを使用したくない理由です
Luke Robertson

これはGZIPをサポートしていないようです。実際、GZIPをサポートするJSライブラリは見つかりません。
Andrew T Finnell、2018


9

@Pehejeは機能しますが、Unicodeで問題が発生します。これを修正するには、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が、Pehejeの回答に関するRacing Tadpoleのコメントが正しい場合(replace呼び出しは最初のインスタンスのみを置き換える)、同じ修正がここで適用されます。
ゲイリーマギル

9

「ウィンドウ」オブジェクトはnodejs環境に存在しないため、次のコード行を使用できます。

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

それは完全に私のために働いています。それが役に立てば幸い。


1
ノード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);

ノードを使用する場合は、バッファーパッケージを使用する必要がある場合があります。

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

6

この関数を使用して、この回答に基づいてペイロード、ヘッダー、exp(有効期限)、iat(発行時刻)を取得します

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

この答えはいくぶん良くなりますが、2つ半の問題があります。まず、署名をチェックしません(配列項目2)。次に、正規表現の「g」フラグが欠落しているため、REPLACEは正しく機能しません(Racing Tadpoleが別の投稿にコメントしたように、JWTで最初に出現する-と_のみが置き換えられます)。そして半分:配列項目0と1をデコードするには、コード全体を複製する代わりに、FORループを使用できます(短いコードですが、より効率的にすることができます)、SPLITは2回実行されます)。
Cyber​​knight

4

jwt.ioのすべての機能がすべての言語をサポートしているわけではありません。NodeJで使用できる

var decoded = jwt.decode(token);

1
ライブラリがなければ、トークンの2番目の部分でbase64デコードを実行するだけです{var payload = token.split( '。')[1]); 次に、base64デコードを実行します{var decodeData = atob(payload); }
Jithin Vijayan 2017年

4

このコードはjwt.ioで見つかりました。

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

場合によっては(特定の開発プラットフォーム)、
最善の答え(現時点では)が無効なbase64の長さの問題に直面しています。
それで、もっと安定した方法が必要でした。

お役に立てれば幸いです。


2

GuyとPehejeの両方がすでに質問に回答しました。私のような完全な初心者にとっては、例で定義されているインポート行も持つと役に立ちました。

また、トークンがポストバックされる資格情報の完全なセットであることを理解するのに数分かかりました(そのidToken部分だけでなく、JWTトークン全体)。あなたがそれを知ったら、簡単です。

import jwt_decode from 'jwt-decode';

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

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


2
OPの要求に反する別のユーザーとまったく同じ回答を投稿してもあまり役に立ちません
Cacoon

2

JSON Web Token(JWT)をデコードするためのシンプルなNodeJSソリューション

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

GitHub-auth0 / jwt-decodeからの回答。トークン全体を渡すことができるように、文字列分割と戻りオブジェクト{ヘッダー、ペイロード、署名}を含むように入出力を変更しました。

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

これは、私がこの質問を研究した直後に作成したより機能豊富なソリューションです。

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

以下に使用例をいくつか示します。

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
}

StackOverflowコードスニペットツールで実行可能にすることはできませんでしたが、そのコードを実行した場合のおおよその表示は次のとおりです。

ここに画像の説明を入力してください

私はparseJwt関数が常にオブジェクトを返すようにしました(静的型付けの理由からある程度)。

これにより、次のような構文を利用できます。

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

次に、実行時に特定のタイプのエラーをテストし、名前の衝突を回避できます。

誰かがこのコードへの少しの努力、高い価値の変更について考えることができるなら、の利益のために私の答えを自由に編集してnext(person)ください。


0

こことここの答えに基づいて:

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

Javascript node.js expressを実行するには、まず次のようにパッケージをインストールする必要がありました。

npm install jwt-decode --save

次に、私のapp.jsコードでパッケージを取得します。

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

次に、コードを実行します。

let jwt_decoded = jwt_decode(jwt_source);

次に魔法:

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

4
「ライブラリを使用せずに」を思い出してください
オラフ

1
わかりました。しかし、私は同じ問題に直面しており、ライブラリを使用できないという制限はありませんでした。これでうまくいきました。他の誰かが同様の問題に直面しており、同じ制限がないため、私はそれを投稿したままにします。
デビッドホワイト
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.