> ## Documentation Index
> Fetch the complete documentation index at: https://translations.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> このガイドでは、go-auth0 SDK を使用して Go Web アプリケーションにユーザーログインを追加する方法を説明します。

# Go アプリケーションにログインを追加

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

export const CreateInteractiveApp = ({placeholderText = "Auth0", appType = "regular_web", allowedCallbackUrls = ["localhost:3000"], allowedLogoutUrls = ["localhost:3000"], allowedOriginUrls = ["localhost:3000"]}) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [storeReady, setStoreReady] = useState(false);
  const [displayForm, setDisplayForm] = useState(true);
  useEffect(() => {
    const init = () => setStoreReady(true);
    if (window.rootStore) {
      window.rootStore.clientStore.setSelectedClient(null);
      window.rootStore.clientStore.setSelectedClientSecret(undefined);
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
    };
  }, []);
  useEffect(() => {
    if (!storeReady) return;
    const disposer = autorun(() => {
      const rootStore = window.rootStore;
      setIsAuthenticated(rootStore.sessionStore.isAuthenticated);
    });
    return () => {
      disposer();
    };
  }, [storeReady]);
  if (!storeReady || typeof window === "undefined" || !displayForm) {
    return <></>;
  }
  const login = () => {
    const baseUrl = window.rootStore.config.apiBaseUrl;
    const returnTo = encodeURIComponent(window.location.href);
    window.location.href = `${baseUrl}/auth/user/login?returnTo=${returnTo}`;
  };
  const Card = ({className = "", children}) => {
    return <div className={`
          flex border rounded-2xl
          border-gray-950/10 dark:border-white/10
          py-3.5 px-4 gap-2
          text-sm text-gray-900 dark:text-gray-200
          ${className}
        `}>
        {children}
      </div>;
  };
  const Button = ({children, ...props}) => {
    return <button className="bg-[--button-primary] text-[--foreground-inverse] px-[1.125rem] py-1.5 rounded-lg font-medium" {...props}>
        {children}
      </button>;
  };
  const CreateApplicationForm = () => {
    const [name, setName] = useState("");
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState("");
    const handleSubmit = async () => {
      if (!name.trim()) {
        setError("アプリケーション名は必須です");
        return;
      }
      setIsLoading(true);
      setError(null);
      try {
        await window.rootStore.clientStore.createClient({
          name: name.trim(),
          app_type: appType,
          callbacks: allowedCallbackUrls,
          allowed_logout_urls: allowedLogoutUrls,
          web_origins: allowedOriginUrls,
          client_metadata: {
            created_by: "quickstart-docs-app-creation-component"
          }
        });
        setDisplayForm(false);
      } catch (err) {
        console.error("クライアントの作成中にエラーが発生しました:", err);
        const errorMessage = err instanceof Error ? err.message : "アプリケーションの作成に失敗しました";
        setError(errorMessage);
      } finally {
        setIsLoading(false);
      }
    };
    return <Card className="flex-col items-start p-4 gap-3.75">
        <span className="font-medium text-gray-900 dark:text-gray-200">Auth0 アプリの作成</span>
        <div className="w-full flex gap-2">
          <input id="app-name" name={name} className="
              w-full max-w-[448px] h-11 py-2 px-4 
              border rounded-lg border-gray-950/10 dark:border-white/10 
              text-gray-900 dark:text-gray-200
              focus:outline-none dark:focus:outline-none
            " placeholder={`My ${placeholderText} App`} value={name} onChange={e => setName(e.target.value)} />
          <Button onClick={handleSubmit}>{isLoading ? "作成中..." : "作成"}</Button>
        </div>
        {error && <p className="text-red-500">{error}</p>}
      </Card>;
  };
  const SignInForm = () => {
    return <Card className="items-center">
        <Button onClick={login}>ログイン</Button> <span>してアプリを作成してください</span>
      </Card>;
  };
  return isAuthenticated ? <CreateApplicationForm /> : <SignInForm />;
};

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****マスク済み*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const AuthCodeGroup = ({children, dropdown}) => {
  const [processedChildren, setProcessedChildren] = useState(children);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      unsubscribe = window.autorun(() => {
        const processChildren = node => {
          if (typeof node === "string") {
            let processedNode = node;
            for (const [key, value] of window.rootStore.variableStore.values.entries()) {
              const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
              processedNode = processedNode.replaceAll(new RegExp(escapedKey, "g"), value);
            }
            return processedNode;
          } else if (Array.isArray(node)) {
            return node.map(processChildren);
          } else if (node && node.props && node.props.children) {
            return {
              ...node,
              props: {
                ...node.props,
                children: processChildren(node.props.children)
              }
            };
          }
          return node;
        };
        setProcessedChildren(processChildren(children));
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  return <CodeGroup dropdown={dropdown}>{processedChildren}</CodeGroup>;
};

<HowToSchema />

<Note>
  **前提条件:** 始める前に、以下がインストールされていることを確認してください。

  * **[Go](https://go.dev/doc/install)** 1.25 以降
  * バージョン管理用の **[Git](https://git-scm.com/downloads)**

  インストールを確認するには、`go version` を実行します。
</Note>

<div id="get-started">
  ## はじめに
</div>

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

export const localEnvSnippet = `# Auth0テナントのドメインURL。
# カスタムドメインを使用している場合は、代わりにその値を設定してください。
AUTH0_DOMAIN={yourDomain}

# Auth0アプリケーションのクライアントID。
AUTH0_CLIENT_ID={yourClientId}

# Auth0アプリケーションのクライアントシークレット。
AUTH0_CLIENT_SECRET={yourClientSecret}

# アプリケーションのCallback URL。
AUTH0_CALLBACK_URL=http://localhost:3000/callback

# セッションクッキーの暗号化に使用する、長くランダムなシークレット。
SESSION_SECRET=a-long-random-secret-key-for-cookie-encryption`;

<Steps>
  <Step title="新規プロジェクトを作成" stepNumber={1}>
    Go アプリケーション用の新しいディレクトリを作成し、モジュールを初期化します。

    ```shellscript theme={null}
    mkdir myapp && cd myapp
    go mod init myapp
    ```

    必要な依存関係をインストールします。

    ```shellscript theme={null}
    go get github.com/auth0/go-auth0/v2
    go get github.com/gorilla/sessions
    go get github.com/joho/godotenv
    ```

    プロジェクト構成を作成します：

    ```shellscript theme={null}
    mkdir templates
    touch .env auth.go handlers.go main.go templates/home.html templates/user.html
    ```

    <Accordion title="想定されるgo.modを表示">
      ```go go.mod theme={null}
      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
      )
      ```
    </Accordion>
  </Step>

  <Step title="Auth0アプリケーションを設定する" stepNumber={2}>
    Auth0 のサービスを使用するには、Auth0 Dashboard でアプリケーションを設定する必要があります。Auth0 アプリケーションでは、プロジェクトの認証を構成します。

    アプリケーションの **Settings** タブから、次の情報を取得する必要があります。

    * **ドメイン**
    * **クライアントID**
    * **クライアントシークレット**

    <Frame>![アプリのDashboard](https://cdn2.auth0.com/docs/1.14550.0/media/articles/dashboard/client_settings.png)</Frame>

    Auth0 アプリケーションの設定方法は 3 つあります。Quick Setup ツール (推奨) を使用する方法、CLI コマンドを実行する方法、または Dashboard で手動設定する方法です。

    <Tabs>
      <Tab title="クイックセットアップ（推奨）">
        Auth0 アプリを作成し、適切な設定値があらかじめ入力された `.env` ファイルをコピーします。

        <CreateInteractiveApp placeholderText="Go" appType="regular_web" allowedCallbackUrls={["http://localhost:3000/callback"]} allowedLogoutUrls={["http://localhost:3000"]} />

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />
      </Tab>

      <Tab title="CLI">
        プロジェクトのルートディレクトリで次のコマンドを実行して、Auth0 アプリケーションを作成します。

        <CodeGroup>
          ```shellscript Mac theme={null}
          # Auth0 CLI をインストールします（まだインストールしていない場合）
          brew tap auth0/auth0-cli && brew install auth0

          # Auth0 アプリケーションを作成します
          auth0 apps create \
            --name "My Go App" \
            --type regular \
            --callbacks http://localhost:3000/callback \
            --logout-urls http://localhost:3000
          ```

          ```powershell Windows theme={null}
          # Auth0 CLI をインストールします（まだインストールしていない場合）
          scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
          scoop install auth0

          # Auth0 アプリケーションを作成します
          auth0 apps create `
            --name "My Go App" `
            --type regular `
            --callbacks http://localhost:3000/callback `
            --logout-urls http://localhost:3000
          ```
        </CodeGroup>

        作成後、**ドメイン**、**クライアントID**、**クライアントシークレット** の値をコピーし、`.env` ファイルを作成します。

        <AuthCodeBlock children={localEnvSnippet} language="shellscript" filename=".env" />

        <Note>
          このコマンドで実行される内容は次のとおりです。

          1. 認証済みかどうかを確認し、必要に応じてログインを求める
          2. `http://localhost:3000` 用に設定された Auth0 の Regular Web Application を作成する
          3. ドメイン、クライアントID、クライアントシークレットを含むアプリケーションの詳細を表示する
        </Note>
      </Tab>

      <Tab title="Dashboard">
        1. [Auth0 Dashboard](https://manage.auth0.com/dashboard/) に移動します
        2. **Applications** > **Applications** > **Create Application** をクリックします
        3. ポップアップでアプリの名前を入力し、アプリの種類として `Regular Web Applications` を選択して **Create** をクリックします
        4. Application Details ページで **Settings** タブに切り替えます
        5. **ドメイン**、**クライアントID**、**クライアントシークレット** の値を確認します

        ### コールバックURLを設定する

        コールバックURL は、ユーザーの認証後に Auth0 がリダイレクトするアプリケーション内の URL です。これを設定していない場合、ユーザーはログイン後にアプリケーションへ戻れません。

        **Allowed Callback URLs** を次のように設定します。

        ```
        http://localhost:3000/callback
        ```

        ### ログアウトURLを設定する

        ログアウトURL は、ユーザーのログアウト後に Auth0 がリダイレクトできるアプリケーション内の URL です。これを設定していない場合、ユーザーはアプリケーションからログアウトできず、エラーが表示されます。

        **Allowed Logout URLs** を次のように設定します。

        ```
        http://localhost:3000
        ```

        ### 環境ファイルを作成する

        Dashboard の値を使用して、プロジェクトのルートディレクトリに `.env` ファイルを作成します。

        ```shellscript .env theme={null}
        AUTH0_DOMAIN=YOUR_AUTH0_DOMAIN
        AUTH0_CLIENT_ID=YOUR_AUTH0_CLIENT_ID
        AUTH0_CLIENT_SECRET=YOUR_AUTH0_CLIENT_SECRET
        AUTH0_CALLBACK_URL=http://localhost:3000/callback
        SESSION_SECRET=a-long-random-secret-key-for-cookie-encryption
        ```

        <Warning>
          `YOUR_AUTH0_DOMAIN`、`YOUR_AUTH0_CLIENT_ID`、`YOUR_AUTH0_CLIENT_SECRET` は、アプリケーションの Settings タブに表示される実際の値に置き換えてください。
        </Warning>
      </Tab>
    </Tabs>

    <Tip>
      `.env` ファイルが存在することを確認します: `cat .env` (Mac/Linux) または `type .env` (Windows)
    </Tip>
  </Step>

  <Step title="Auth0 のクライアントを作成する" stepNumber={3}>
    `auth.go` ファイルを作成します。このファイルは `go-auth0` の認証クライアントをラップし、認可 URL を生成するためのヘルパーを提供します。

    ```go auth.go lines theme={null}
    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` ヘルパーを提供します
  </Step>

  <Step title="ルートハンドラーを作成" stepNumber={4}>
    ログイン、コールバック、ユーザープロフィール、ログアウト用のハンドラーを含む `handlers.go` ファイルを作成します。

    ```go handlers.go expandable lines theme={null}
    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 にリダイレクトします
    * **CallbackHandler** は `go-auth0` を使用して認可コードをトークンに交換し、その後 `UserInfo` を呼び出してユーザープロファイルを取得します
    * **LogoutHandler** はセッションを削除し、Auth0 の `/v2/logout` エンドポイントにリダイレクトします
    * **UserHandler** はセッションからユーザープロファイルを取得して、テンプレートをレンダリングします
  </Step>

  <Step title="HTMLテンプレートを作成" stepNumber={5}>
    <Tabs>
      <Tab title="home.html">
        ```html templates/home.html expandable lines theme={null}
        <!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>
        ```
      </Tab>

      <Tab title="user.html">
        ```html templates/user.html expandable lines theme={null}
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Profile</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%;
                }
                .avatar {
                    display: block;
                    width: 96px;
                    height: 96px;
                    border-radius: 50%;
                    border: 3px solid #635bff;
                    margin: 0 auto 1.5rem;
                    object-fit: cover;
                    background: #2d2d5e;
                }
                .avatar[src=""], .avatar:not([src]) {
                    display: none;
                }
                .avatar-fallback {
                    width: 96px;
                    height: 96px;
                    border-radius: 50%;
                    border: 3px solid #635bff;
                    margin: 0 auto 1.5rem;
                    background: linear-gradient(135deg, #635bff, #4f46e5);
                    display: none;
                    align-items: center;
                    justify-content: center;
                    font-size: 2rem;
                    font-weight: 700;
                    color: #fff;
                }
                .badge {
                    display: inline-block;
                    padding: 0.3rem 0.75rem;
                    background: rgba(0, 212, 170, 0.15);
                    color: #00d4aa;
                    border-radius: 20px;
                    font-size: 0.8rem;
                    font-weight: 600;
                    margin-bottom: 1rem;
                }
                h2 {
                    font-size: 1.75rem;
                    font-weight: 700;
                    color: #f8fafc;
                    margin-bottom: 0.25rem;
                }
                .email {
                    color: #94a3b8;
                    font-size: 1rem;
                    margin-bottom: 2rem;
                }
                .info-card {
                    background: #0f0f23;
                    border-radius: 10px;
                    padding: 1.25rem;
                    margin-bottom: 1.5rem;
                    text-align: left;
                }
                .info-row {
                    display: flex;
                    justify-content: space-between;
                    padding: 0.5rem 0;
                    border-bottom: 1px solid #1e293b;
                }
                .info-row:last-child { border-bottom: none; }
                .info-label { color: #64748b; font-size: 0.875rem; }
                .info-value { color: #e2e8f0; font-size: 0.875rem; font-weight: 500; }
                .btn-logout {
                    display: inline-block;
                    padding: 0.75rem 2rem;
                    background: transparent;
                    color: #f87171;
                    text-decoration: none;
                    border-radius: 8px;
                    font-size: 0.95rem;
                    font-weight: 600;
                    border: 1px solid #f87171;
                    transition: all 0.2s;
                }
                .btn-logout:hover {
                    background: #f87171;
                    color: #0a0a0a;
                }
            </style>
        </head>
        <body>
            <div class="container">
                <img class="avatar" src="{{ .picture }}" alt="{{ .name }}" onerror="this.style.display='none';document.getElementById('avatar-fallback').style.display='flex';" />
                <div id="avatar-fallback" class="avatar-fallback">{{ if .nickname }}{{ slice .nickname 0 1 }}{{ else }}?{{ end }}</div>
                <span class="badge">認証済み</span>
                <h2>{{ .nickname }}</h2>
                <p class="email">{{ .email }}</p>
                <div class="info-card">
                    <div class="info-row">
                        <span class="info-label">フルネーム</span>
                        <span class="info-value">{{ .name }}</span>
                    </div>
                    <div class="info-row">
                        <span class="info-label">ニックネーム</span>
                        <span class="info-value">{{ .nickname }}</span>
                    </div>
                    <div class="info-row">
                        <span class="info-label">メールアドレス</span>
                        <span class="info-value">{{ .email }}</span>
                    </div>
                </div>
                <a href="/logout" class="btn-logout">ログアウト</a>
            </div>
        </body>
        </html>
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="メインサーバーを作成" stepNumber={6}>
    `main.go` ですべてをまとめます:

    ```go main.go lines theme={null}
    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)
        }
    }
    ```

    <Accordion title="プロジェクト構成">
      ```
      myapp/
      ├── main.go          # アプリケーションのエントリーポイントとルート定義
      ├── auth.go          # Auth0 クライアントの設定と認可 URL
      ├── handlers.go      # HTTP ハンドラー（ログイン、コールバック、ログアウト、プロフィール）
      ├── templates/
      │   ├── home.html    # サインインリンク付きのホームページ
      │   └── user.html    # ユーザープロフィールページ
      ├── .env             # 環境変数（コミットしない）
      ├── go.mod
      └── go.sum
      ```
    </Accordion>
  </Step>

  <Step title="アプリケーションの実行とテスト" stepNumber={7}>
    開発サーバーを起動します。

    ```shellscript theme={null}
    go run .
    ```

    次のように表示されます: `Server listening on http://localhost:3000/`

    ブラウザーで [http://localhost:3000](http://localhost:3000) を開きます。**Sign In** をクリックすると、Auth0 の Universal Login ページにリダイレクトされます。認証後、アプリにリダイレクトされ、ユーザープロファイル情報が表示されます。

    <Info>
      ポート 3000 がすでに使用されている場合は、`.env` ファイルの `AUTH0_CALLBACK_URL` と、Auth0 の Application Settings にある **Allowed Callback URLs** および **Allowed Logout URLs** を、新しいポートを使用するように更新してください。
    </Info>
  </Step>
</Steps>

<Check>
  **チェックポイント**

  これで、Auth0 認証を備えた完全に動作する Go Web アプリケーションが [localhost](http://localhost:3000/) 上で実行されているはずです。このアプリでは、次のことができます。

  1. ユーザーを認証のために Auth0 の Universal Login にリダイレクトする
  2. `go-auth0` SDK を使用して認可コードをトークンに交換する
  3. ユーザープロフィール情報を取得して表示する
  4. ログアウト時にセッションをクリーンアップする
</Check>

***

<div id="advanced-usage">
  ## 高度な使用法
</div>

<Accordion title="ミドルウェアでルートを保護する">
  認証が必要なルートを保護するために、`IsAuthenticated` ミドルウェアを作成します。これを `handlers.go` に追加します。

  ```go handlers.go theme={null}
  // 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` に次を追加します。

  ```go main.go theme={null}
  mux.Handle("/user", IsAuthenticated(http.HandlerFunc(UserHandler)))
  ```
</Accordion>

<Accordion title="保護された API を呼び出す">
  認証後は、セッションに保存されたアクセストークンを使用して保護された API を呼び出せます。

  ```go theme={null}
  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)
  }
  ```

  <Note>
    API 用のアクセストークンをリクエストするには、`auth.go` の認可 URL に `audience` パラメーターを追加します。

    ```go theme={null}
    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},
    }
    ```
  </Note>
</Accordion>

<Accordion title="トークンを更新する">
  アプリケーションでリフレッシュトークンを使用している場合は、`go-auth0` SDK を使用して、リフレッシュトークンを新しいトークンセットと交換できます。

  ```go theme={null}
  newTokenSet, err := auth.OAuth.RefreshToken(r.Context(), oauth.RefreshTokenRequest{
      RefreshToken: storedRefreshToken,
  })
  ```

  リフレッシュトークンを受け取るには、認可 URL のスコープに `offline_access` を追加します。

  ```go theme={null}
  "scope": {"openid profile email offline_access"},
  ```
</Accordion>

***

<div id="troubleshooting">
  ## トラブルシューティング
</div>

<AccordionGroup>
  <Accordion title="よくある問題と解決策">
    ### 「認可コードをトークンに交換できませんでした」

    **問題:** コールバックハンドラーで認可コードを交換できません。

    **解決策:**

    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. ブラウザーの設定でクッキーがブロックされていないことを確認します
  </Accordion>
</AccordionGroup>

***

<div id="next-steps">
  ## 次のステップ
</div>

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

* **[ユーザープロフィール](/ja/docs/manage-users/user-accounts/user-profiles)** — 認証後に利用できるユーザー情報について確認します
* **[他のIDプロバイダーを設定する](/ja/docs/authenticate/identity-providers)** — ソーシャルログインやエンタープライズログインのオプションを追加します
* **[多要素認証を有効にする](/ja/docs/secure/multi-factor-authentication)** — セキュリティをさらに強化します
* **[アクセストークンのベストプラクティス](/ja/docs/secure/tokens/access-tokens)** — トークンのセキュリティについて確認します
* **[本番環境チェックリスト](/ja/docs/deploy-monitor/pre-deployment-checks/how-to-run-production-checks)** — リリース前のセキュリティレビュー

***

<div id="resources">
  ## リソース
</div>

* **[go-auth0 GitHub](https://github.com/auth0/go-auth0)** — ソースコードとドキュメント
* **[Go Web App Sample](https://github.com/auth0-samples/auth0-golang-web-app/tree/master/01-Login-Quickstart)** — 完全に動作するサンプル例
* **[Auth0 Community](https://community.auth0.com/)** — コミュニティでサポートを得る
