メインコンテンツへスキップ
前提条件: 始める前に、以下がインストールされていることを確認してください。
  • Go 1.25 以降
  • バージョン管理用の Git
インストールを確認するには、go version を実行します。

はじめに

このクイックスタートでは、Go の Web アプリケーションに Auth0 認証を追加する方法を説明します。go-auth0 SDK と Go 標準の net/http ライブラリを使用して、ログイン、ログアウト、ユーザープロフィール機能を備えたサーバーサイドアプリケーションを構築します。
1

新規プロジェクトを作成

Go アプリケーション用の新しいディレクトリを作成し、モジュールを初期化します。
mkdir myapp && cd myapp
go mod init myapp
必要な依存関係をインストールします。
go get github.com/auth0/go-auth0/v2
go get github.com/gorilla/sessions
go get github.com/joho/godotenv
プロジェクト構成を作成します:
mkdir templates
touch .env auth.go handlers.go main.go templates/home.html templates/user.html
go.mod
module myapp

go 1.25

require (
    github.com/auth0/go-auth0/v2 v2.10.0
    github.com/gorilla/sessions v1.4.0
    github.com/joho/godotenv v1.5.1
)
2

Auth0アプリケーションを設定する

Auth0 のサービスを使用するには、Auth0 Dashboard でアプリケーションを設定する必要があります。Auth0 アプリケーションでは、プロジェクトの認証を構成します。アプリケーションの Settings タブから、次の情報を取得する必要があります。
  • ドメイン
  • クライアントID
  • クライアントシークレット
アプリのDashboard
Auth0 アプリケーションの設定方法は 3 つあります。Quick Setup ツール (推奨) を使用する方法、CLI コマンドを実行する方法、または Dashboard で手動設定する方法です。
Auth0 アプリを作成し、適切な設定値があらかじめ入力された .env ファイルをコピーします。
.env ファイルが存在することを確認します: cat .env (Mac/Linux) または type .env (Windows)
3

Auth0 のクライアントを作成する

auth.go ファイルを作成します。このファイルは go-auth0 の認証クライアントをラップし、認可 URL を生成するためのヘルパーを提供します。
auth.go
package main

import (
    "context"
    "fmt"
    "net/url"
    "os"

    "github.com/auth0/go-auth0/v2/authentication"
)

// Authenticator は go-auth0 認証クライアントをラップします。
type Authenticator struct {
    *authentication.Authentication
    Domain      string
    ClientID    string
    CallbackURL string
}

// NewAuthenticator は新しい Authenticator を作成して設定します。
func NewAuthenticator() (*Authenticator, error) {
    domain := os.Getenv("AUTH0_DOMAIN")
    clientID := os.Getenv("AUTH0_CLIENT_ID")
    clientSecret := os.Getenv("AUTH0_CLIENT_SECRET")
    callbackURL := os.Getenv("AUTH0_CALLBACK_URL")

    authClient, err := authentication.New(
        context.Background(),
        domain,
        authentication.WithClientID(clientID),
        authentication.WithClientSecret(clientSecret),
    )
    if err != nil {
        return nil, fmt.Errorf("failed to initialize authentication client: %w", err)
    }

    return &Authenticator{
        Authentication: authClient,
        Domain:         domain,
        ClientID:       clientID,
        CallbackURL:    callbackURL,
    }, nil
}

// AuthorizationURL はユーザーを Auth0 の Universal Login ページに
// リダイレクトするための /authorize URL を構築します。
func (a *Authenticator) AuthorizationURL(state string) string {
    u, _ := url.Parse("https://" + a.Domain + "/authorize")
    params := url.Values{
        "response_type": {"code"},
        "client_id":     {a.ClientID},
        "redirect_uri":  {a.CallbackURL},
        "scope":         {"openid profile email"},
        "state":         {state},
    }
    u.RawQuery = params.Encode()
    return u.String()
}
この処理で行うこと:
  • テナントのドメイン、クライアントID、クライアントシークレットを使用して、go-auth0 の認証クライアントを初期化します
  • 必要な OAuth2 パラメーターを含む /authorize へのリダイレクトURLを構築する AuthorizationURL ヘルパーを提供します
4

ルートハンドラーを作成

ログイン、コールバック、ユーザープロフィール、ログアウト用のハンドラーを含む handlers.go ファイルを作成します。
handlers.go
package main

import (
    "crypto/rand"
    "encoding/base64"
    "encoding/gob"
    "net/http"
    "net/url"
    "os"

    "github.com/auth0/go-auth0/v2/authentication/oauth"
    "github.com/gorilla/sessions"
)

var store *sessions.CookieStore

func init() {
    gob.Register(map[string]interface{}{})
}

func initSessionStore() {
    store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_SECRET")))
    store.Options = &sessions.Options{
        Path:     "/",
        MaxAge:   86400,
        HttpOnly: true,
        Secure:   false, // Set to true in production (requires HTTPS)
        SameSite: http.SameSiteLaxMode,
    }
}

// HomeHandler はホームページをレンダリングするか、すでにログイン済みの場合は /user にリダイレクトします。
func HomeHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "auth-session")
    if session.Values["profile"] != nil {
        http.Redirect(w, r, "/user", http.StatusSeeOther)
        return
    }
    if err := templates.ExecuteTemplate(w, "home.html", nil); err != nil {
        http.Error(w, "Internal error", http.StatusInternalServerError)
    }
}

// LoginHandler はユーザーをAuth0のUniversal Loginページにリダイレクトします。
func LoginHandler(auth *Authenticator) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        state, err := generateRandomState()
        if err != nil {
            http.Error(w, "Internal error", http.StatusInternalServerError)
            return
        }

        session, _ := store.Get(r, "auth-session")
        session.Values["state"] = state
        if err := session.Save(r, w); err != nil {
            http.Error(w, "Internal error", http.StatusInternalServerError)
            return
        }

        http.Redirect(w, r, auth.AuthorizationURL(state), http.StatusTemporaryRedirect)
    }
}

// CallbackHandler は認証後にAuth0からのコールバックを処理します。
func CallbackHandler(auth *Authenticator) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "auth-session")

        // CSRF攻撃を防ぐためにstateパラメーターを検証します。
        if r.URL.Query().Get("state") != session.Values["state"] {
            http.Error(w, "Invalid state parameter", http.StatusBadRequest)
            return
        }

        // 認可コードをトークンと交換します。
        tokenSet, err := auth.OAuth.LoginWithAuthCode(r.Context(), oauth.LoginWithAuthCodeRequest{
            Code:        r.URL.Query().Get("code"),
            RedirectURI: auth.CallbackURL,
        }, oauth.IDTokenValidationOptions{})
        if err != nil {
            http.Error(w, "Failed to exchange authorization code for token", http.StatusUnauthorized)
            return
        }

        // ユーザーのプロファイル情報を取得します。
        userInfo, err := auth.UserInfo(r.Context(), tokenSet.AccessToken)
        if err != nil {
            http.Error(w, "Failed to get user info", http.StatusInternalServerError)
            return
        }

        session.Values["access_token"] = tokenSet.AccessToken
        session.Values["profile"] = map[string]interface{}{
            "nickname": userInfo.Nickname,
            "name":     userInfo.Name,
            "picture":  userInfo.Picture,
            "email":    userInfo.Email,
        }
        if err := session.Save(r, w); err != nil {
            http.Error(w, "Internal error", http.StatusInternalServerError)
            return
        }

        http.Redirect(w, r, "/user", http.StatusTemporaryRedirect)
    }
}

// UserHandler は認証済みユーザーのプロファイルを表示します。
func UserHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "auth-session")
    profile, ok := session.Values["profile"].(map[string]interface{})
    if !ok {
        http.Redirect(w, r, "/", http.StatusSeeOther)
        return
    }

    if err := templates.ExecuteTemplate(w, "user.html", profile); err != nil {
        http.Error(w, "Internal error", http.StatusInternalServerError)
    }
}

// LogoutHandler はセッションをクリアし、Auth0のログアウトエンドポイントにリダイレクトします。
func LogoutHandler(auth *Authenticator) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "auth-session")
        session.Options.MaxAge = -1
        session.Save(r, w)

        logoutURL, _ := url.Parse("https://" + auth.Domain + "/v2/logout")
        scheme := "http"
        if r.TLS != nil {
            scheme = "https"
        }

        returnTo, _ := url.Parse(scheme + "://" + r.Host)
        params := url.Values{}
        params.Add("returnTo", returnTo.String())
        params.Add("client_id", auth.ClientID)
        logoutURL.RawQuery = params.Encode()

        http.Redirect(w, r, logoutURL.String(), http.StatusTemporaryRedirect)
    }
}

func generateRandomState() (string, error) {
    b := make([]byte, 32)
    _, err := rand.Read(b)
    if err != nil {
        return "", err
    }
    return base64.RawURLEncoding.EncodeToString(b), nil
}
重要なポイント:
  • LoginHandler は、CSRF 対策のためにランダムな state パラメーターを生成し、Auth0 の Universal Login にリダイレクトします
  • CallbackHandlergo-auth0 を使用して認可コードをトークンに交換し、その後 UserInfo を呼び出してユーザープロファイルを取得します
  • LogoutHandler はセッションを削除し、Auth0 の /v2/logout エンドポイントにリダイレクトします
  • UserHandler はセッションからユーザープロファイルを取得して、テンプレートをレンダリングします
5

HTMLテンプレートを作成

templates/home.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Auth0 Go Web App</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #0a0a0a;
            color: #e2e8f0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .container {
            text-align: center;
            padding: 3rem;
            background: #1a1a2e;
            border-radius: 16px;
            box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
            max-width: 440px;
            width: 90%;
        }
        .logo {
            width: 48px;
            height: 48px;
            margin: 0 auto 1.5rem;
            background: linear-gradient(135deg, #635bff, #00d4aa);
            border-radius: 12px;
        }
        h1 {
            font-size: 2rem;
            font-weight: 700;
            margin-bottom: 0.5rem;
            color: #f8fafc;
        }
        p {
            color: #94a3b8;
            font-size: 1.05rem;
            margin-bottom: 2rem;
            line-height: 1.6;
        }
        .btn {
            display: inline-block;
            padding: 0.85rem 2.5rem;
            background: linear-gradient(135deg, #635bff, #4f46e5);
            color: #fff;
            text-decoration: none;
            border-radius: 8px;
            font-size: 1rem;
            font-weight: 600;
            transition: transform 0.2s, box-shadow 0.2s;
            box-shadow: 0 4px 14px rgba(99, 91, 255, 0.4);
        }
        .btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(99, 91, 255, 0.6);
        }
        .footer {
            margin-top: 2rem;
            font-size: 0.85rem;
            color: #475569;
        }
        .footer a { color: #635bff; text-decoration: none; }
    </style>
</head>
<body>
    <div class="container">
        <div class="logo"></div>
        <h1>Go + Auth0</h1>
        <p>Secure authentication for your Go web application, powered by Auth0.</p>
        <a href="/login" class="btn">Sign In</a>
        <div class="footer">
            Protected by <a href="https://auth0.com">Auth0</a>
        </div>
    </div>
</body>
</html>
6

メインサーバーを作成

main.go ですべてをまとめます:
main.go
package main

import (
    "html/template"
    "log"
    "net/http"

    "github.com/joho/godotenv"
)

var templates *template.Template

func main() {
    // .env ファイルは省略可能です。Docker では、環境変数は --env-file フラグで渡されます。
    godotenv.Load()

    initSessionStore()

    auth, err := NewAuthenticator()
    if err != nil {
        log.Fatalf("Failed to initialize the authenticator: %v", err)
    }

    templates = template.Must(template.ParseGlob("templates/*.html"))

    mux := http.NewServeMux()
    mux.HandleFunc("/", HomeHandler)
    mux.HandleFunc("/login", LoginHandler(auth))
    mux.HandleFunc("/callback", CallbackHandler(auth))
    mux.HandleFunc("/user", UserHandler)
    mux.HandleFunc("/logout", LogoutHandler(auth))

    log.Print("Server listening on http://localhost:3000/")
    if err := http.ListenAndServe("0.0.0.0:3000", mux); err != nil {
        log.Fatalf("There was an error with the http server: %v", err)
    }
}
myapp/
├── main.go          # アプリケーションのエントリーポイントとルート定義
├── auth.go          # Auth0 クライアントの設定と認可 URL
├── handlers.go      # HTTP ハンドラー(ログイン、コールバック、ログアウト、プロフィール)
├── templates/
│   ├── home.html    # サインインリンク付きのホームページ
│   └── user.html    # ユーザープロフィールページ
├── .env             # 環境変数(コミットしない)
├── go.mod
└── go.sum
7

アプリケーションの実行とテスト

開発サーバーを起動します。
go run .
次のように表示されます: Server listening on http://localhost:3000/ブラウザーで http://localhost:3000 を開きます。Sign In をクリックすると、Auth0 の Universal Login ページにリダイレクトされます。認証後、アプリにリダイレクトされ、ユーザープロファイル情報が表示されます。
ポート 3000 がすでに使用されている場合は、.env ファイルの AUTH0_CALLBACK_URL と、Auth0 の Application Settings にある Allowed Callback URLs および Allowed Logout URLs を、新しいポートを使用するように更新してください。
チェックポイントこれで、Auth0 認証を備えた完全に動作する Go Web アプリケーションが localhost 上で実行されているはずです。このアプリでは、次のことができます。
  1. ユーザーを認証のために Auth0 の Universal Login にリダイレクトする
  2. go-auth0 SDK を使用して認可コードをトークンに交換する
  3. ユーザープロフィール情報を取得して表示する
  4. ログアウト時にセッションをクリーンアップする

高度な使用法

認証が必要なルートを保護するために、IsAuthenticated ミドルウェアを作成します。これを handlers.go に追加します。
handlers.go
// IsAuthenticated は、ユーザーが
// 認証済みかどうかを確認するミドルウェアです。
func IsAuthenticated(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        session, _ := store.Get(r, "auth-session")
        if session.Values["profile"] == nil {
            http.Redirect(w, r, "/", http.StatusSeeOther)
            return
        }
        next.ServeHTTP(w, r)
    })
}
保護対象のルートにミドルウェアを適用するには、main.go に次を追加します。
main.go
mux.Handle("/user", IsAuthenticated(http.HandlerFunc(UserHandler)))
認証後は、セッションに保存されたアクセストークンを使用して保護された API を呼び出せます。
func ApiHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := store.Get(r, "auth-session")
    accessToken, ok := session.Values["access_token"].(string)
    if !ok {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    req, _ := http.NewRequest("GET", "https://your-api.example.com/api/private", nil)
    req.Header.Add("Authorization", "Bearer "+accessToken)

    res, err := http.DefaultClient.Do(req)
    if err != nil {
        http.Error(w, "API request failed", http.StatusInternalServerError)
        return
    }
    defer res.Body.Close()

    w.Header().Set("Content-Type", "application/json")
    io.Copy(w, res.Body)
}
API 用のアクセストークンをリクエストするには、auth.go の認可 URL に audience パラメーターを追加します。
params := url.Values{
    "response_type": {"code"},
    "client_id":     {a.ClientID},
    "redirect_uri":  {a.CallbackURL},
    "scope":         {"openid profile email"},
    "audience":      {"https://your-api.example.com"},
    "state":         {state},
}
アプリケーションでリフレッシュトークンを使用している場合は、go-auth0 SDK を使用して、リフレッシュトークンを新しいトークンセットと交換できます。
newTokenSet, err := auth.OAuth.RefreshToken(r.Context(), oauth.RefreshTokenRequest{
    RefreshToken: storedRefreshToken,
})
リフレッシュトークンを受け取るには、認可 URL のスコープに offline_access を追加します。
"scope": {"openid profile email offline_access"},

トラブルシューティング

「認可コードをトークンに交換できませんでした」

問題: コールバックハンドラーで認可コードを交換できません。解決策:
  1. .env ファイルの AUTH0_CLIENT_SECRET が正しいことを確認します
  2. AUTH0_CALLBACK_URL が Auth0 の Application Settings の Allowed Callback URLs と完全に一致していることを確認します
  3. 認可コードの有効期限が切れていないことを確認します (認可コードは 1 回限り有効で、有効期間も短く設定されています)

「ユーザー情報を取得できませんでした」

問題: /userinfo エンドポイントがエラーを返します。解決策:
  1. 認可 URL に openid スコープが含まれていることを確認します
  2. アクセストークンが有効で、有効期限切れでないことを確認します
  3. Auth0 ドメインへのネットワーク接続を確認します

「無効な state パラメーター」

問題: コールバックの state パラメーターがセッションと一致しません。解決策:
  1. ブラウザーでクッキーが有効になっていることを確認します
  2. リクエスト間でセッションストアのシークレットが変更されていないことを確認します
  3. ログインフロー中に複数のブラウザータブを使用していないことを確認します

ユーザーがログアウトできない

問題: ログアウトをクリックした後、ユーザーに Auth0 のエラーページが表示されます。解決策:
  1. http://localhost:3000 が Auth0 の Application Settings の Allowed Logout URLs に含まれていることを確認します
  2. client_id パラメーターがアプリケーションのクライアントIDと一致していることを確認します
  3. returnTo URL が許可されているログアウト URL のいずれか 1 つと完全に一致していることを確認します

セッションデータが保持されない

問題: ユーザープロフィールのデータがリクエスト間で消えてしまいます。解決策:
  1. データを保存する前に gob.Register(map[string]interface{}{}) が呼び出されていることを確認します
  2. セッションの値を変更した後に session.Save(r, w) が呼び出されていることを確認します
  3. ブラウザーの設定でクッキーがブロックされていないことを確認します

次のステップ

認証が機能するようになったら、次のトピックも参照してください。

リソース