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
実行方法
- コードを
rokt_jwt_validator.go