メインコンテンツへスキップ

AI を使用して Auth0 を統合する

Claude Code、Cursor、GitHub Copilot などの AI コーディングアシスタントを使用している場合は、agent skills を使って数分で Auth0 API 認証を自動的に追加できます。インストール:
npx skills add auth0/agent-skills --skill auth0-quickstart --skill go-jwt-middleware
次に、AI アシスタントに次のように依頼します。
Add Auth0 JWT authentication to my Go API
AI アシスタントは、Auth0 API の作成、認証情報の取得、go-jwt-middleware のインストール、バリデーターの設定、JWT 検証による API エンドポイントの保護を自動的に行います。agent skills の完全なドキュメント →
前提条件: 開始する前に、以下がインストールされていることを確認してください。
  • Go 1.24 以降 (go-jwt-middleware v3 でジェネリクスをサポートするために必要)
  • Git バージョン管理用
インストールの確認: go version

はじめに

公開アクセス、JWT 認証、スコープベースの権限保護という 3 つの異なる保護レベルを示すエンドポイントを備えた Go API を構築します。完全な実装では、Go の標準 net/http ライブラリと go-jwt-middleware v3 を使用します。

GitHub でサンプルを表示

テストを含む完全な動作例
1

新規プロジェクトを作成

Go API 用の新しいディレクトリを作成し、モジュールを初期化します。
mkdir myapi && cd myapi
go mod init github.com/yourorg/myapi
必要な依存パッケージをインストールします。
go get github.com/auth0/go-jwt-middleware/v3
go get github.com/joho/godotenv
go mod download
プロジェクトの構成を作成します:
mkdir -p cmd/server internal/auth internal/config internal/handlers
touch .env cmd/server/main.go internal/config/auth.go internal/auth/claims.go internal/auth/validator.go internal/auth/middleware.go internal/handlers/api.go
go.mod
module github.com/yourorg/myapi

go 1.24

require (
    github.com/auth0/go-jwt-middleware/v3 v3.1.0
    github.com/joho/godotenv v1.5.1
)
2

Auth0 APIを設定する

次に、Auth0テナントに新しいAPIを作成し、環境変数をプロジェクトに追加する必要があります。Auth0 API の設定方法は 2 つあります。CLI コマンドを使うか、Auth0 Dashboard から手動で設定します。
Auth0 API を作成するには、プロジェクトのルートディレクトリで次のコマンドを実行します。
# Auth0 CLI をインストール(まだインストールしていない場合)
brew tap auth0/auth0-cli && brew install auth0

# Auth0 API を作成
auth0 apis create \
  --name "My Go API" \
  --identifier https://my-go-api.example.com
作成後、IdentifierDomain の値をコピーし、.env ファイルを作成します。
このコマンドでは次の処理が行われます。
  1. 認証済みかどうかを確認し、必要に応じてログインを求めます
  2. 指定した Identifier で Auth0 API を作成します
  3. ドメインと Identifier を含む API の詳細を表示します
セキュリティ: .env ファイルは絶対にバージョン管理へコミットしないでください。.gitignore ファイルに .env を追加してください。
3

API の権限を定義する

Permissions (スコープ) を使用して、リソースへのアクセス方法を定義できます。たとえば、マネージャーには read アクセスを付与し、管理者には write アクセスを付与します。
  1. API の設定で、Permissions タブをクリックします
  2. 次の権限を作成します。
PermissionDescription
read:messagesAPI からメッセージを読み取る
このチュートリアルでは、スコープで保護されたエンドポイントを保護するために read:messages スコープを使用します。アプリケーションの要件に応じて、追加の権限を定義できます。
4

設定ローダーを作成

環境変数の読み込みと検証を行う設定パッケージを作成します。
internal/config/auth.go
package config

import (
    "fmt"
    "os"
)

type AuthConfig struct {
    Domain   string
    Audience string
}

func LoadAuthConfig() (*AuthConfig, error) {
    domain := os.Getenv("AUTH0_DOMAIN")
    if domain == "" {
        return nil, fmt.Errorf("AUTH0_DOMAIN environment variable required")
    }

    audience := os.Getenv("AUTH0_AUDIENCE")
    if audience == "" {
        return nil, fmt.Errorf("AUTH0_AUDIENCE environment variable required")
    }

    return &AuthConfig{
        Domain:   domain,
        Audience: audience,
    }, nil
}
この処理で行うこと:
  • 環境変数から Auth0 のドメインとオーディエンスを読み込みます
  • 起動時に、必要な設定が揃っていることを検証します
  • アプリケーション全体で使用できる型安全な設定構造体を返します
5

カスタムクレームとJWTバリデーターの作成

カスタムクレームを使用すると、JWT からアプリケーション固有のデータを抽出して検証できます。validator は、Auth0 に対してトークンを検証する中核コンポーネントです。
internal/auth/claims.go
package auth

import (
    "context"
    "fmt"
    "strings"
)

// CustomClaims には、JWT から解析するカスタムデータが含まれます。
type CustomClaims struct {
    Scope string `json:"scope"`
}

// Validate は、カスタムクレームの形式が正しいことを確認します。
func (c *CustomClaims) Validate(ctx context.Context) error {
    if c.Scope == "" {
        return nil
    }

    if strings.TrimSpace(c.Scope) != c.Scope {
        return fmt.Errorf("scope claim has invalid whitespace")
    }

    if strings.Contains(c.Scope, "  ") {
        return fmt.Errorf("scope claim contains double spaces")
    }

    return nil
}

// HasScope は、クレームに特定のスコープが含まれているかどうかを確認します。
func (c *CustomClaims) HasScope(expectedScope string) bool {
    if c.Scope == "" {
        return false
    }

    scopes := strings.Split(c.Scope, " ")
    for _, scope := range scopes {
        if scope == expectedScope {
            return true
        }
    }
    return false
}
重要なポイント:
  • Validate メソッドは、JWT の解析後にミドルウェアによって自動的に呼び出されます
  • HasScope は、権限ベースのアクセス制御のためにスペース区切りのスコープを解析します
  • validator は JWKS キャッシュ (TTL 5 分) を使用し、30 秒のクロックスキューを許容します
  • RS256 アルゴリズムは、algorithm confusion attacks を防ぐために明示的に設定されています
6

HTTPミドルウェアとハンドラーを作成する

このミドルウェアは、HTTP リクエスト用バリデーターをラップします。ハンドラーでは、公開、非公開、スコープベースの 3 つの保護レベルを示しています。
internal/auth/middleware.go
package auth

import (
    "log/slog"
    "net/http"

    jwtmiddleware "github.com/auth0/go-jwt-middleware/v3"
    "github.com/auth0/go-jwt-middleware/v3/validator"
)

func NewMiddleware(jwtValidator *validator.Validator) (*jwtmiddleware.JWTMiddleware, error) {
    return jwtmiddleware.New(
        jwtmiddleware.WithValidator(jwtValidator),
        jwtmiddleware.WithValidateOnOptions(false),
        jwtmiddleware.WithErrorHandler(func(w http.ResponseWriter, r *http.Request, err error) {
            slog.Error("JWT validation failed", "error", err, "path", r.URL.Path)
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte(`{"message":"Failed to validate JWT."}`))
        }),
    )
}
保護レベル:
  • Public (/api/public) — 認証は不要です
  • Private (/api/private) — 有効な JWT が必要です
  • Scoped (/api/private-scoped) — 有効な JWT と read:messages 権限が必要です
7

メインサーバーを作成

本番運用向けのタイムアウトとグレースフルシャットダウンを設定し、メインのエントリポイントですべてを組み合わせます:
cmd/server/main.go
package main

import (
    "context"
    "log"
    "net/http"
    "os"
    "os/signal"
    "time"

    "github.com/yourorg/myapi/internal/auth"
    "github.com/yourorg/myapi/internal/config"
    "github.com/yourorg/myapi/internal/handlers"
    "github.com/joho/godotenv"
)

func main() {
    // .envファイルから環境変数を読み込む
    if err := godotenv.Load(); err != nil {
        log.Println("No .env file found, using environment variables")
    }

    // Auth0の設定を読み込む
    cfg, err := config.LoadAuthConfig()
    if err != nil {
        log.Fatalf("Failed to load config: %v", err)
    }

    // JWTバリデーターを作成する
    jwtValidator, err := auth.NewValidator(cfg.Domain, cfg.Audience)
    if err != nil {
        log.Fatalf("Failed to create validator: %v", err)
    }

    // HTTPミドルウェアを作成する
    middleware, err := auth.NewMiddleware(jwtValidator)
    if err != nil {
        log.Fatalf("Failed to create middleware: %v", err)
    }

    // ルーティングを設定する
    mux := http.NewServeMux()
    mux.HandleFunc("/api/public", handlers.PublicHandler)
    mux.Handle("/api/private", middleware.CheckJWT(http.HandlerFunc(handlers.PrivateHandler)))
    mux.Handle("/api/private-scoped", middleware.CheckJWT(http.HandlerFunc(handlers.ScopedHandler)))

    // 本番環境向けタイムアウトを設定してサーバーを構成する
    srv := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    // ゴルーチンでサーバーを起動する
    go func() {
        log.Println("Server starting on :8080")
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server failed: %v", err)
        }
    }()

    // グレースフルシャットダウン
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, os.Interrupt)
    <-quit

    log.Println("Shutting down server...")
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        log.Fatalf("Server forced to shutdown: %v", err)
    }

    log.Println("Server exited")
}
myapi/
├── cmd/
│   └── server/
│       └── main.go              # アプリケーションのエントリーポイント
├── internal/
│   ├── auth/
│   │   ├── claims.go            # カスタム JWT クレーム
│   │   ├── middleware.go         # JWT ミドルウェア
│   │   └── validator.go         # JWT バリデーター
│   ├── config/
│   │   └── auth.go              # 設定ローダー
│   └── handlers/
│       └── api.go               # HTTP ハンドラー(公開、非公開、スコープ付き)
├── .env                         # 環境変数(コミット対象外)
├── .gitignore
├── go.mod
└── go.sum
8

API を実行してテストする

開発サーバーを起動します:
go run cmd/server/main.go
次のように表示されます: Server starting on :8080公開エンドポイントをテストします (認証不要) :
curl http://localhost:8080/api/public
次のように表示されます:
{
  "message": "Hello from a public endpoint! You don't need to be authenticated to see this."
}
トークンなしでプライベートエンドポイントをテストします (失敗するはずです) :
curl http://localhost:8080/api/private
401 Unauthorized エラーが表示されます。
{
  "message": "Failed to validate JWT."
}
有効なトークンでテストするには、Auth0 Dashboard で対象のAPIを開き、Test タブをクリックしてアクセストークンをコピーします。次に、以下を実行します。
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     http://localhost:8080/api/private
スコープが設定されたエンドポイントをテストします (read:messages 権限が必要です) :
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
     http://localhost:8080/api/private-scoped
チェックポイントこれで、保護された Go API を用意できているはずです。API は次のように動作します。
  1. パブリックエンドポイントへのリクエストは、認証なしで受け入れる
  2. 保護されたエンドポイントへのリクエストは、有効なトークンがない場合は拒否する
  3. JWT を Auth0 のドメインとオーディエンスに照らして検証する
  4. スコープを使用して、権限ベースのアクセス制御を適用する

API の呼び出し

保護された API は、Authorization ヘッダーで Bearer トークンとしてアクセストークンを渡すことで、任意のアプリケーションから呼び出せます。

クライアントコードの例

curl --request GET \
  --url http://localhost:8080/api/private \
  --header 'authorization: Bearer YOUR_ACCESS_TOKEN'
シングルページアプリケーションまたはモバイル/ネイティブアプリケーションから API を呼び出す場合は、認可フローの完了後にアクセストークンを取得します。トークンの取得方法や API の呼び出し方法は、開発しているアプリケーションの種類や使用しているフレームワークによって異なります。

シングルページアプリケーション

React、Vue、Angular のクイックスタート (例付き)

モバイル / ネイティブアプリケーション

iOS、Android、React Native のクイックスタート

高度な使用方法

DPoP (Demonstrating Proof-of-Possession) は RFC 9449 で定義されており、トークンを暗号鍵にバインドすることでトークンの盗難を防ぎ、セキュリティを強化します。
internal/auth/middleware.go
func NewMiddleware(jwtValidator *validator.Validator) *jwtmiddleware.JWTMiddleware {
    return jwtmiddleware.New(
        jwtmiddleware.WithValidator(jwtValidator),
        jwtmiddleware.WithDPoPMode(jwtmiddleware.DPoPRequired),
        jwtmiddleware.WithLogger(slog.Default()),
    )
}
DPoP モード:
  • DPoPAllowed (デフォルト) — Bearer トークンと DPoP トークンの両方を受け入れます
  • DPoPRequired — DPoP トークンのみを受け入れ、Bearer は拒否します
  • DPoPDisabled — Bearer トークンのみを受け入れ、DPoP は拒否します
DPoP は、金融 API、医療 API、高いセキュリティが求められるエンタープライズアプリケーションに推奨されます。詳しくは DPoP ドキュメントを参照してください。
Webアプリケーションからのリクエストを許可するには、CORS を有効にします。簡単なミドルウェアや、rs/cors のようなライブラリを使用できます。
go get github.com/rs/cors
cmd/server/main.go
import "github.com/rs/cors"

// mux を CORS ミドルウェアでラップする
handler := cors.New(cors.Options{
    AllowedOrigins:   []string{"http://localhost:3000"},
    AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE"},
    AllowedHeaders:   []string{"Authorization", "Content-Type"},
    AllowCredentials: true,
}).Handler(mux)

srv := &http.Server{
    Addr:    ":8080",
    Handler: handler,
}
本番環境では、ワイルドカードではなく明示的なオリジンを指定してください。
トークン検証をデバッグするために、詳細なログを有効にします。
internal/auth/middleware.go
middleware := jwtmiddleware.New(
    jwtmiddleware.WithValidator(jwtValidator),
    jwtmiddleware.WithLogger(slog.Default()),
)
起動時の確認を追加します。
cmd/server/main.go
log.Printf("Validator configured:")
log.Printf("  Issuer: https://%s/", cfg.Domain)
log.Printf("  Audience: %s", cfg.Audience)
log.Printf("  Algorithm: RS256")

トラブルシューティング

「JWT の検証に失敗しました」または 401 Unauthorized

問題: API がアクセストークンを見つけられないか、検証できません。解決策:
  1. Authorization ヘッダーが含まれていることを確認します: Authorization: Bearer YOUR_TOKEN
  2. トークンの前に Bearer が含まれていることを確認します
  3. トークンの有効期限が切れていないことを確認します
  4. アクセストークンを使用しており、IDトークンではないことを確認します

「aud claim mismatch」

問題: トークンのオーディエンスが API と一致していません。解決策: AUTH0_AUDIENCE が Auth0 Dashboard の API Identifier と完全に一致していることを確認します。オーディエンスの末尾にスラッシュを付けてはいけません。
# 正しい
AUTH0_AUDIENCE=https://my-go-api.example.com

# 誤り(末尾にスラッシュあり)
AUTH0_AUDIENCE=https://my-go-api.example.com/
また、クライアントアプリケーションは正しい audience パラメーターを指定してトークンをリクエストする必要があります。

「unexpected signing method」

問題: トークンのアルゴリズムがバリデーターの設定と一致していません。解決策:
  1. Auth0 はデフォルトで RS256 (非対称) を使用します
  2. バリデーターに validator.RS256 を指定していることを確認します
  3. 明示的に設定している場合を除き、Auth0 のトークンに validator.HS256 を使用しないでください

JWKS エンドポイントに到達できない

問題: JWKS キャッシュプロバイダーが Auth0 の公開鍵エンドポイントに接続できません。解決策:
  1. Auth0 へのネットワーク接続を確認します (ファイアウォール/プロキシ設定)
  2. JWKS エンドポイントを手動でテストします: curl https://YOUR_AUTH0_DOMAIN/.well-known/jwks.json
  3. Auth0 のリージョン (us/eu/au) が正しいことを確認します

import パスが誤っている

問題: cannot find package "github.com/auth0/go-jwt-middleware/v3/..."解決策: すべての import で /v3 サフィックスを使用していることを確認します。
// 正しい
import "github.com/auth0/go-jwt-middleware/v3/validator"

// 誤り
import "github.com/auth0/go-jwt-middleware/validator"

クロックスキュー / トークン期限切れエラー

問題: サーバーの時刻がずれているため、有効なトークンが期限切れと判定されます。解決策: バリデーターにはすでに 30 秒のクロックスキュー許容値が含まれています。さらに必要な場合は、次のように調整します。
validator.WithAllowedClockSkew(60*time.Second)

クレームの抽出に失敗する

問題: ジェネリクスの使用時に Failed to retrieve claims が発生します。解決策: 正しい型パラメーターを使用していることを確認します。
claims, err := jwtmiddleware.GetClaims[*validator.ValidatedClaims](r.Context())

次のステップ

保護されたAPIを用意したら、次のトピックも確認してください。

リソース