JWT 解析

JWT Token 解析ツール、JSON Web Tokenの解析と検証

ツール概要:JWT 解析ツール

JWT(JSON Web Token)は、当事者間で情報を安全に転送するためのコンパクトでURLセーフなトークン形式です。主にWeb認証、API認証、情報交換などの場面で使用され、現代のWeb開発において重要な技術の一つです。

JWT 解析ツールとは?

JWT 解析ツールは構造化データを解析し、主要フィールドを表示します。

使い方

  1. 解析したい内容を貼り付けます。
  2. ツールがフィールドを抽出して表示します。
  3. 必要なフィールドをコピーします。

よくある利用シーン

  • 連携時のフィールド確認
  • 構造や形式のデバッグ
  • 構造化データの文書化

❓ よくある質問

Q1: 解析に失敗する?
A: 入力形式が完全か確認してください。

Q2: フィールドが欠ける?
A: 入力に必要な項目があるか確認してください。

Q3: 署名検証できる?
A: 解析のみで、検証には鍵が必要です。

✨ 主な機能

  • 🔍 完全解析:Header、Payload、Signatureの詳細分析
  • 形式検証:JWT構造の正確性を自動チェック
  • 📊 視覚化表示:JSON形式で構造化された情報を表示
  • 🔧 デバッグ支援:開発時のトークン検証とデバッグ
  • 📋 ワンクリックコピー:解析結果を直接コピー可能

📖 使用例

標準的なJWT

入力JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuWxseS4reWkquiOlyIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

解析結果:

Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload:

{
  "sub": "1234567890",
  "name": "山中太郎",
  "iat": 1516239022
}

Signature:

SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

複雑なペイロード

入力JWT(複雑なクレーム):

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMzQ1In0.eyJpc3MiOiJodHRwczovL2F1dGgudG9vbG1pLmNvbSIsInN1YiI6InVzZXJfMTIzNDUiLCJhdWQiOlsiYXBpLnRvb2xtaS5jb20iLCJhZG1pbi50b29sbWkuY29tIl0sImV4cCI6MTY4NzUzNjAwMCwiaWF0IjoxNjg3NDQ5NjAwLCJuYmYiOjE2ODc0NDk2MDAsImp0aSI6ImFiY2RlZi0xMjM0NTYiLCJuYW1lIjoi5LiJ55Sw6Iqx5a2QIiwiZW1haWwiOiJtaXRhbmlAZXhhbXBsZS5jb20iLCJyb2xlcyI6WyJ1c2VyIiwiYWRtaW4iXSwicGVybWlzc2lvbnMiOlsicmVhZCIsIndyaXRlIiwiZGVsZXRlIl19.signature_here

解析結果:

Header:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "12345"
}

Payload:

{
  "iss": "https://auth.toolmi.com",
  "sub": "user_12345",
  "aud": ["api.toolmi.com", "admin.toolmi.com"],
  "exp": 1687536000,
  "iat": 1687449600,
  "nbf": 1687449600,
  "jti": "abcdef-123456",
  "name": "三田芳子",
  "email": "mitani@example.com",
  "roles": ["user", "admin"],
  "permissions": ["read", "write", "delete"]
}

🎯 応用シーン

1. Web認証システム

JWT を使用したユーザー認証:

// ログイン処理
const login = async (email, password) => {
  try {
    const response = await fetch('/api/auth/login', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ email, password })
    });

    if (response.ok) {
      const data = await response.json();
      const token = data.token;
      
      // JWTをローカルストレージに保存
      localStorage.setItem('authToken', token);
      
      // トークンの内容を解析
      const payload = parseJWT(token);
      console.log('ユーザー情報:', payload);
      
      return { success: true, user: payload };
    } else {
      throw new Error('ログインに失敗しました');
    }
  } catch (error) {
    console.error('ログインエラー:', error);
    return { success: false, error: error.message };
  }
};

// JWT解析関数
const parseJWT = (token) => {
  try {
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
    
    return JSON.parse(jsonPayload);
  } catch (error) {
    console.error('JWT解析エラー:', error);
    return null;
  }
};

// 認証状態の確認
const checkAuthStatus = () => {
  const token = localStorage.getItem('authToken');
  if (!token) {
    return { authenticated: false };
  }

  const payload = parseJWT(token);
  if (!payload) {
    return { authenticated: false };
  }

  // トークンの有効期限をチェック
  const currentTime = Math.floor(Date.now() / 1000);
  if (payload.exp && payload.exp < currentTime) {
    localStorage.removeItem('authToken');
    return { authenticated: false, reason: 'expired' };
  }

  return { 
    authenticated: true, 
    user: payload,
    expiresAt: payload.exp 
  };
};

2. API認証

API リクエストでのJWT使用:

// API クライアントクラス
class APIClient {
  constructor(baseURL) {
    this.baseURL = baseURL;
    this.token = localStorage.getItem('authToken');
  }

  // 認証ヘッダーを含むリクエスト
  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    const headers = {
      'Content-Type': 'application/json',
      ...options.headers
    };

    // JWTトークンを Authorization ヘッダーに追加
    if (this.token) {
      headers.Authorization = `Bearer ${this.token}`;
    }

    try {
      const response = await fetch(url, {
        ...options,
        headers
      });

      // 401エラーの場合、トークンが無効
      if (response.status === 401) {
        this.handleUnauthorized();
        throw new Error('認証が必要です');
      }

      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      console.error('API リクエストエラー:', error);
      throw error;
    }
  }

  // 認証エラー処理
  handleUnauthorized() {
    localStorage.removeItem('authToken');
    this.token = null;
    // ログインページにリダイレクト
    window.location.href = '/login';
  }

  // トークンの更新
  updateToken(newToken) {
    this.token = newToken;
    localStorage.setItem('authToken', newToken);
  }

  // ユーザー情報の取得
  async getUserProfile() {
    return await this.request('/api/user/profile');
  }

  // データの取得
  async getData(endpoint) {
    return await this.request(endpoint);
  }

  // データの更新
  async updateData(endpoint, data) {
    return await this.request(endpoint, {
      method: 'PUT',
      body: JSON.stringify(data)
    });
  }
}

// 使用例
const apiClient = new APIClient('https://api.toolmi.com');

// ユーザープロフィールの取得
apiClient.getUserProfile()
  .then(profile => {
    console.log('ユーザープロフィール:', profile);
  })
  .catch(error => {
    console.error('プロフィール取得エラー:', error);
  });

3. トークンリフレッシュ

JWT の自動更新システム:

// トークンリフレッシュマネージャー
class TokenManager {
  constructor() {
    this.accessToken = localStorage.getItem('accessToken');
    this.refreshToken = localStorage.getItem('refreshToken');
    this.refreshPromise = null;
  }

  // アクセストークンの取得
  getAccessToken() {
    return this.accessToken;
  }

  // トークンの有効期限チェック
  isTokenExpired(token) {
    if (!token) return true;

    try {
      const payload = this.parseJWT(token);
      const currentTime = Math.floor(Date.now() / 1000);
      
      // 有効期限の5分前に期限切れとみなす
      return payload.exp < (currentTime + 300);
    } catch (error) {
      return true;
    }
  }

  // JWT解析
  parseJWT(token) {
    const base64Url = token.split('.')[1];
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(atob(base64).split('').map(function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
    
    return JSON.parse(jsonPayload);
  }

  // トークンリフレッシュ
  async refreshAccessToken() {
    if (this.refreshPromise) {
      return this.refreshPromise;
    }

    this.refreshPromise = this.performRefresh();
    
    try {
      const result = await this.refreshPromise;
      return result;
    } finally {
      this.refreshPromise = null;
    }
  }

  async performRefresh() {
    if (!this.refreshToken) {
      throw new Error('リフレッシュトークンがありません');
    }

    try {
      const response = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          refreshToken: this.refreshToken
        })
      });

      if (!response.ok) {
        throw new Error('トークンリフレッシュに失敗しました');
      }

      const data = await response.json();
      
      this.accessToken = data.accessToken;
      this.refreshToken = data.refreshToken;
      
      localStorage.setItem('accessToken', this.accessToken);
      localStorage.setItem('refreshToken', this.refreshToken);

      return this.accessToken;
    } catch (error) {
      // リフレッシュに失敗した場合、ログアウト
      this.logout();
      throw error;
    }
  }

  // 有効なアクセストークンの取得(必要に応じてリフレッシュ)
  async getValidAccessToken() {
    if (!this.isTokenExpired(this.accessToken)) {
      return this.accessToken;
    }

    return await this.refreshAccessToken();
  }

  // ログアウト
  logout() {
    this.accessToken = null;
    this.refreshToken = null;
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
  }
}

// 使用例
const tokenManager = new TokenManager();

// API リクエスト前にトークンを確認
const makeAuthenticatedRequest = async (url, options = {}) => {
  try {
    const token = await tokenManager.getValidAccessToken();
    
    const response = await fetch(url, {
      ...options,
      headers: {
        ...options.headers,
        'Authorization': `Bearer ${token}`
      }
    });

    return response;
  } catch (error) {
    console.error('認証リクエストエラー:', error);
    // ログインページにリダイレクト
    window.location.href = '/login';
  }
};

🔧 技術詳細

JWT構造

JWT は3つの部分から構成されます:

1. Header(ヘッダー)

{
  "alg": "HS256",    // 署名アルゴリズム
  "typ": "JWT"       // トークンタイプ
}

2. Payload(ペイロード)

{
  "iss": "issuer",           // 発行者
  "sub": "subject",          // 主体
  "aud": "audience",         // 対象者
  "exp": 1234567890,         // 有効期限
  "iat": 1234567890,         // 発行時刻
  "nbf": 1234567890,         // 有効開始時刻
  "jti": "jwt-id"            // JWT ID
}

3. Signature(署名)

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

標準クレーム

JWT で使用される標準的なクレーム:

  • iss (Issuer):トークンの発行者
  • sub (Subject):トークンの主体(通常はユーザーID)
  • aud (Audience):トークンの対象者
  • exp (Expiration Time):有効期限(Unix タイムスタンプ)
  • iat (Issued At):発行時刻
  • nbf (Not Before):有効開始時刻
  • jti (JWT ID):JWT の一意識別子

💡 使用のコツ

  • 有効期限の確認:トークンの exp クレームで有効期限をチェック
  • セキュリティ:機密情報をペイロードに含めない
  • 署名検証:本番環境では必ず署名を検証する
  • トークンサイズ:ペイロードが大きくなりすぎないよう注意

⚠️ 注意事項

  • 機密情報:JWTのペイロードは暗号化されていないため、機密情報を含めない
  • 署名検証:このツールは解析のみで、署名の検証は行わない
  • 有効期限:期限切れのトークンは使用しない
  • 保存場所:ブラウザでのトークン保存場所に注意(XSS対策)

🚀 使い方

  1. JWT入力:解析したいJWTトークンを入力ボックスに貼り付け
  2. 自動解析:入力と同時にHeader、Payload、Signatureが自動的に解析される
  3. 結果確認:各部分の詳細情報をJSON形式で確認
  4. コピー使用:「コピー」ボタンで解析結果をクリップボードにコピー
  5. デバッグ活用:開発時のトークン検証とデバッグに活用

ヒント:このツールはクライアント側でローカル処理を行い、JWTトークンをサーバーに送信しないため、セキュリティが保証されます。