メインコンテンツまでスキップ

Go JWT 検証例

⚠️ 重要なお知らせ: これはデモンストレーション目的の基本的な例です。実運用で使用する場合は、ソフトウェアスタック、セキュリティ要件、デプロイ環境に特化したベストプラクティスを調査し、実装してください。常に組織のセキュリティガイドラインに従い、確立されたJWTライブラリやフレームワークの使用を検討してください。

📝 注記: この例は教育目的でJWKSのダウンロードとファイルキャッシュを示しています。実運用環境では、インフラストラクチャに基づいて、構成管理、環境変数、または好みのキャッシング戦略を使用してJWKSキー管理を実装することを選択できます。

この例は、Goを使用してRokt JWTトークンを検証する方法を示しています。

前提条件

必要なパッケージをインストールします:

go get github.com/golang-jwt/jwt/v5

完全な例

package main

import (
"crypto/ecdsa"
"crypto/elliptic"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"math/big"
"net/http"
"os"
"strings"
"time"

"github.com/golang-jwt/jwt/v5"
)

// JWKSはJSON Web Key Set構造を表します
type JWKS struct {
Keys []JWK `json:"keys"`
}

// JWKはJSON Web Key構造を表します
type JWK struct {
Kid string `json:"kid"`
Kty string `json:"kty"`
Crv string `json:"crv"`
X string `json:"x"`
Y string `json:"y"`
}

// ReferralClaimsはJWTクレーム内の紹介データを表します
type ReferralClaims struct {
CampaignID string `json:"cid"`
CreativeID string `json:"crid"`
RCLID string `json:"rclid"`
jwt.RegisteredClaims
}

// ValidateReferralTokenはJWTトークンを検証し、紹介データを抽出します
func ValidateReferralToken(jwtToken, jwksJson string) (*ReferralClaims, error) {
// JWKSを解析
var jwks JWKS
if err := json.Unmarshal([]byte(jwksJson), &jwks); err != nil {
return nil, fmt.Errorf("JWKSの解析に失敗しました: %v", err)
}

if len(jwks.Keys) == 0 {
return nil, fmt.Errorf("JWKSにキーが見つかりません")
}

// 最初のキーを取得(単一キーを仮定して簡略化)
jwk := jwks.Keys[0]

// base64urlエンコードされた座標をデコード
xBytes, err := base64.RawURLEncoding.DecodeString(jwk.X)
if err != nil {
return nil, fmt.Errorf("X座標のデコードに失敗しました: %v", err)
}

yBytes, err := base64.RawURLEncoding.DecodeString(jwk.Y)
if err != nil {
return nil, fmt.Errorf("Y座標のデコードに失敗しました: %v", err)
}

// ECDSA公開鍵を作成
publicKey := &ecdsa.PublicKey{
Curve: elliptic.P256(),
X: new(big.Int).SetBytes(xBytes),
Y: new(big.Int).SetBytes(yBytes),
}

// JWTトークンを解析および検証
token, err := jwt.ParseWithClaims(jwtToken, &ReferralClaims{}, func(token *jwt.Token) (interface{}, error) {
// 署名方法を確認
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("予期しない署名方法: %v", token.Header["alg"])
}
return publicKey, nil
}, jwt.WithLeeway(60*time.Second)) // 1分の時計のずれを許可

if err != nil {
return nil, fmt.Errorf("JWTの解析に失敗しました: %v", err)
}

if claims, ok := token.Claims.(*ReferralClaims); ok && token.Valid {
return claims, nil
}

return nil, fmt.Errorf("無効なトークン")
}

func downloadAndCacheJWKS(jwksURL, cacheFile string) (string, error) {
// キャッシュファイルが存在し、最近のものであるか(24時間以内)を確認
if info, err := os.Stat(cacheFile); err == nil {
if time.Since(info.ModTime()) < 24*time.Hour {
fmt.Printf("キャッシュされたJWKSを使用: %s\n", cacheFile)
data, err := os.ReadFile(cacheFile)
if err != nil {
return "", fmt.Errorf("キャッシュファイルの読み込みに失敗しました: %v", err)
}
return string(data), nil
}
}

// JWKSをダウンロード
fmt.Printf("JWKSをダウンロード中: %s\n", jwksURL)
resp, err := http.Get(jwksURL)
if err != nil {
return "", fmt.Errorf("JWKSのダウンロードに失敗しました: %v", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("JWKSのダウンロードに失敗しました: HTTP %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("レスポンスボディの読み込みに失敗しました: %v", err)
}

// JWKSをキャッシュ
if err := os.WriteFile(cacheFile, body, 0644); err != nil {
fmt.Printf("警告: JWKSのキャッシュに失敗しました: %v\n", err)
} else {
fmt.Printf("JWKSをキャッシュしました: %s\n", cacheFile)
}

return string(body), nil
}

func main() {
// RoktからのサンプルJWTトークン
// 概要ページからテストトークンをコピー
sampleToken := "PASTE_TEST_TOKEN_HERE"

// JWKSエンドポイントURL
jwksURL := "https://public-api.rokt.com/.well-known/jwks.json"
jwksCacheFile := "jwks_cache.json"

fmt.Println("=== Go JWT バリデーター ===")
fmt.Printf("トークン: %s...\n", sampleToken[:50])
fmt.Printf("JWKS URL: %s\n", jwksURL)
fmt.Println()

// JWKS のダウンロードとキャッシュ
jwksJson, err := downloadAndCacheJWKS(jwksURL, jwksCacheFile)
if err != nil {
fmt.Printf("❌ JWKS のダウンロードに失敗しました: %v\n", err)
return
}
fmt.Println("JWKS のダウンロードとキャッシュに成功しました")

// JWKS から公開鍵の座標を抽出
var jwks JWKS
if err := json.Unmarshal([]byte(jwksJson), &jwks); err != nil {
fmt.Printf("❌ JWKS の解析に失敗しました: %v\n", err)
return
}

if len(jwks.Keys) == 0 {
fmt.Println("❌ JWKS に鍵が見つかりません")
return
}

key := jwks.Keys[0]

// トークンの検証
claims, err := ValidateReferralToken(sampleToken, jwksJson)
if err != nil {
log.Fatalf("トークンの検証に失敗しました: %v", err)
}

fmt.Println("✅ トークンの検証に成功しました!")
fmt.Printf("キャンペーン ID: %s\n", claims.CampaignID)
fmt.Printf("クリエイティブ ID: %s\n", claims.CreativeID)
fmt.Printf("RCLID: %s\n", claims.RCLID)
fmt.Printf("発行日時: %v\n", claims.IssuedAt)
}

入力/出力例

入力

  • JWTトークン: Overviewページからテストトークンをコピー
  • 公開鍵ソース: https://public-api.rokt.com/.well-known/jwks.json

出力

=== Go JWT Validator ===
Token: eyJhbGciOiJFUzI1NiIsImtpZCI6InJva3Qtc2lnbmluZy1rZXkiLCJ0eXAiOiJKV1QifQ...
JWKS URL: https://public-api.rokt.com/.well-known/jwks.json

Downloading JWKS from: https://public-api.rokt.com/.well-known/jwks.json
JWKS cached to: jwks_cache.json
JWKS downloaded and cached successfully

✅ トークンの検証に成功しました!
キャンペーンID: 3436085368692408324
クリエイティブID: 3437732754935906308
RCLID: 7db958dbd232247a4a8285a34d22fe0f4e9affa463bf5ee54e26721ab0df0e23
発行日時: 2025-08-20 15:10:01 +1000 AEST

実行方法

  1. コードをrokt_jwt_validator.goに保存
  2. 依存関係をインストール: go mod init rokt-validator && go get github.com/golang-jwt/jwt/v5
  3. 実行: go run rokt_jwt_validator.go

go.modを使用した代替実装

go.modファイルを作成します:

module rokt-validator

go 1.21

require github.com/golang-jwt/jwt/v5 v5.0.0

注意事項

  • 公開鍵はRoktのJWKSエンドポイントから取得されます

  • この例では署名検証にECDSA-256 (ES256)アルゴリズムを使用します

  • パフォーマンス向上のために公開鍵をキャッシュすることを検討してください

  • Goの標準ライブラリは優れた暗号化サポートを提供します

この記事は役に立ちましたか?