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

# OIDC Basics

> OpenID Connect アイデンティティ・プロバイダーが Authlete API を活用する際の、しくみを理解するためのチュートリアルです。

<Note>
  このページは Authlete 3.0 用です。2.x については[こちらのページ](/ja/v2/get-started/core-concepts/oidc-basics)をご覧ください。
</Note>

# はじめに

このドキュメントは、[Authlete APIs](/api-reference) を使用して、[認可コードフロー](https://youtu.be/x2qtJZ3Wivc?feature=shared) をサポートする OpenID Connect (OIDC) アイデンティティプロバイダー (IdP) サーバーを実装する基本的な使用方法を説明するためのチュートリアルです。

このチュートリアルを完了すると、以下を実施できます：

* 認可サーバーから Authlete へのリクエストをシミュレーションしながら、Authlete API へのリクエストを送信します。
* 認可リクエストで追加のクレームを含む発行者識別子の値を Authlete 管理コンソールに設定し、それらが ID トークンからデコードされる様子を確認します。

<iframe width="560" height="315" src="https://www.youtube.com/embed/x2qtJZ3Wivc?si=OCXWzHwmkFt0NI9x" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen />

# 認可コードフローのコンポーネント

<Note>
  **Note**

  このチュートリアルでは便宜上、OIDC アイデンティティプロバイダーを「認可サーバー」、同リライングパーティを「クライアント」と表記します。
</Note>

一般的な Authlete の実装では、認可サーバーのミドルウェアが Authlete API へのリクエストの送信およびレスポンスの処理を担当します。このチュートリアルでは、bash/zsh の `curl` コマンドを使用してこれらのリクエストを直接行います。もちろん、PowerShell や Postman など、別の HTTP クライアントを使用することもできますが、その場合は手順を適宜調整する必要があります。

```mermaid theme={null}
flowchart LR
    subgraph EndUser [エンドユーザー]
        userAgent["ユーザーエージェント (N/A)"]
        resourceOwner["リソースオーナー (N/A)"]
    end

    subgraph APIClient [API クライアント]
        client["クライアント (N/A)"]
    end

    subgraph APIServer [API サーバー]
        authServer["認可サーバー/IdP (curl)"]
        resourceServer["リソースサーバー (curl)"]
        authAdmin["Authlete 管理者"]
    end

    subgraph Authlete [Authlete]
        mgmtConsole["Authlete 管理コンソール"]
        authAPI["Authlete API"]
    end

    resourceOwner --> userAgent
    userAgent -- "クライアントへのアクセス" --> client
    userAgent -- "認可リクエスト (ユーザー認証と同意)" --> authServer
    client -- "トークンリクエスト" --> authServer
    client -- "API リクエスト" --> resourceServer

    %% API サーバーの Authlete とのやり取り
    authAdmin -- "サービス/クライアント管理" --> mgmtConsole
    mgmtConsole -- "API リクエスト" --> authAPI

    authServer -- "API リクエスト" --> authAPI
    resourceServer -- "API リクエスト" --> authAPI
```

この OAuth フロー内で各コンポーネントがどのように連携するかを理解するために、各コンポーネントの完全修飾ドメイン名 (FQDN) を以下に示します。認可サーバーとクライアントの FQDN は参考用に作成されたものであり、実際の実装では適切な値に置き換える必要があります

| コンポーネント                 | FQDN                   |
| ----------------------- | ---------------------- |
| Authlete API US Cluster | `us.authlete.com`      |
| Authlete 管理コンソール        | `console.authlete.com` |
| 認可サーバー                  | `as.example.com`       |
| クライアント                  | `client.example.org`   |
| リソースサーバー                | N/A                    |

<Note>
  **Note**

  API エンドポイントはクラスターごとに異なります。他のクラスターのサービスを使用する場合は、そのクラスターの ISO コードを完全なドメイン名に置き換える必要があります (例: br, eu, jp)。
</Note>

# 環境設定

<Note>
  **Note**

  このセクションの設定は、Authlete API チュートリアル「[OAuth 2.0 の基本](/ja/get-started/quickstarts/oauth-2-0-basics)」に記載されている内容と同じです。このチュートリアルをすでに完了している場合は、このセクションをスキップできます。
</Note>

Authlete API サービスを作成し、サービスにクライアントを登録する方法については、「[API を使ってみる](/ja/get-started/quickstarts/getting-started)」を参照してください。

サービス ID, サービス アクセストークン, そしてクライアントシークレットの値を書き留めるのを忘れないようにしてください。

すでに他の Authlete チュートリアルを完了している場合、既存のサービスを再利用できます。そうでない場合は、以下の値を使用してサービスを作成し、他のすべてのフィールドはデフォルトのままにしてください。

| フィールド     | 値                             |
| --------- | ----------------------------- |
| API クラスター | 🇺🇸 US                       |
| サービス名     | Demo AS                       |
| サービスの詳細情報 | Example authorization service |

以下の値を使用してクライアントを作成し、他のすべてのフィールドはデフォルトのままにしてください。

| フィールド       | 値                                |
| ----------- | -------------------------------- |
| クライアント名     | Demo OIDC Client                 |
| クライアントの詳細情報 | Example client for OIDC tutorial |
| クライアントタイプ   | 機密                               |

<figure class="figure bg-transparent border-0">
  <img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/get-started/authlete-client-configs.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=432f34c5ec5bdc4500dbdd1f1f953ded" class="figure-img img-fluid rounded" alt="クライアント名、詳細、タイプを設定した Authlete クライアント" width="1636" height="792" data-path="get-started/authlete-client-configs.png" />
</figure>

クライアントを作成したら、クライアント設定 > エンドポイント > 基本設定に移動します。リダイレクト URI セクションで \[追加] をクリックし、クライアントが認可レスポンスを受け取る URI を入力して保存します。

以下はクライアントを作成したときに生成または指定されたプロパティの例です。これらの値は、認可コードフローの一環としてリクエストを行う際に必要となります。環境設定セクションで記録した値を使用してください。

| 項目           | 値                                                                                                  |
| ------------ | -------------------------------------------------------------------------------------------------- |
| サービス ID      | 自動生成 (例: `10738933707579`)                                                                         |
| サービスアクセストークン | 自動生成 (例: `Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ`)                                            |
| クライアント ID    | 自動生成 (例: `12898884596863`)                                                                         |
| クライアントシークレット | 自動生成 (例: `-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg`) |
| クライアントタイプ    | `機密`                                                                                               |
| リダイレクト URI   | `https://client.example.org/cb/example.com`                                                        |
| クライアント認証方式   | `CLIENT_SECRET_BASIC`                                                                              |

これで、この環境を使用して OIDC 認可コードフローを試す準備が整いました。

# ウォークスルー

以下のシーケンス図を提供して、コードフローに関与する各ステップを理解できるようにしています。このウォークスルー内の位置を確認するため、対応する番号を使用してステップを識別できます。

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant RO as リソースオーナー
    participant UA as ユーザーエージェント
    participant C as クライアント
    participant AS as 認可サーバー
    participant RS as リソースサーバー
    participant API as Authlete API

    RO ->> UA: 開始
    UA ->> C: リクエストを送信
    C -->> UA: 認可リクエスト
    UA ->> AS: リクエストを転送
    AS ->> API: POST /auth/authorization
    API -->> AS: 処理許可<br/>(チケットを含む)
    AS -->> UA: ユーザー認証と同意ページ
    RO <<->> UA: 資格情報を入力
    UA ->> AS: ログインと同意
    AS ->> API: POST /auth/authorization/issue<br/>(チケットを含む)
    API -->> AS: 認可レスポンスコンテンツ<br/>(認可コードを含む)
    AS -->> UA: 認可レスポンス
    UA ->> C: レスポンスを転送
    C ->> AS: トークンリクエスト<br/>(認可コード付き)
    AS ->> API: POST /auth/token
    API -->> AS: トークンレスポンスコンテンツ<br/>(ID トークンとアクセストークンを含む)
    AS -->> C: トークンレスポンス
```

## クライアントから認可サーバーへの認可リクエスト

以下の値を例としてリクエストのパラメータに使用します (前述の通り)。

| 項目             | 値                                           | 説明                                                                                                                         |
| -------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| client\_id     | `12898884596863`                            | 登録済みクライアント ID                                                                                                              |
| response\_type | `code`                                      | OIDC 認可コードフローを示す値 (`scope` に `openid` を含む場合)                                                                               |
| redirect\_uri  | `https://client.example.org/cb/example.com` | 登録済みリダイレクト URI の一つ                                                                                                         |
| scope          | `openid`                                    | OIDC 認証リクエストであることを示す値                                                                                                      |
| nonce          | `n-0S6_WzA2Mj`                              | ノンス値 ([3.1.2.1. 認証リクエスト - OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) を参照) |

リソースオーナーがクライアントを介して保護されたリソースにアクセスしようとすると (ステップ #1、#2)、クライアントはユーザーエージェントを介して認可リクエスト (OIDC 認証リクエスト) を認可サーバーに送信します (ステップ #3、#4)。

認可サーバーは、以下に示すように、ユーザーエージェントから HTTP `GET` クエリ文字列としてデータを受け取ります。

```shell theme={null}
redirect_uri=https://client.example.org/cb/example.com
 &response_type=code
 &client_id=12898884596863
 &scope=openid
 &nonce=n-0S6_WzA2Mj
```

認可サーバーの役割は、これらのパラメータを評価することです。以下は典型的な評価ルールです：

* クライアント ID `12898884596863` に関連付けられたクライアントが認可サーバーに登録されているかどうか。`scope=openid` の場合、このクライアントは OIDC リライングパーティである必要があります。
* リダイレクト URI `https://client.example.org/cb/example.com` の値が、クライアントに登録された URI の一つと一致しているかどうか。
* `response_type` や `scope` などの他のパラメータの値が、クライアントに対して許可されているかどうか。

その後、`scope` と `response_type` の値がそれぞれ `openid` と `code` であるため、認可サーバーは OIDC 認可コードフローを処理します。

ただし、Authlete アーキテクチャでは、認可サーバーは単に Authlete API によって処理される認可ロジックのプロキシとして機能します。このロジックは [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) エンドポイントで公開されています。リクエストを受け取ると、Authlete API が認可サーバーに代わって評価プロセスを実行します。

ここから、認可サーバーのリクエストをこの API にシミュレートします。

以下のように `curl` コマンドを実行してください (メッセージ #5)。前のステップで生成した `<サービス ID>` (`Service ID`)、`<サービス アクセス トークン>` (`Service Access Token`)、および `<クライアント ID>` (`Client ID`) を置き換えて使用します。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=<Client ID e.g. 12898884596863>&scope=openid&nonce=n-0S6_WzA2Mj" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=<Client ID e.g. 12898884596863>&scope=openid&nonce=n-0S6_WzA2Mj" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // authlete-java-common — initialize the client once and reuse it across the steps below.
  AuthleteConfiguration config = new AuthleteSimpleConfiguration()
          .setApiVersion("V3")
          .setBaseUrl("https://us.authlete.com")
          .setServiceApiKey("10738933707579")
          .setServiceAccessToken(System.getenv("SERVICE_ACCESS_TOKEN"));
  AuthleteApi authleteApi = AuthleteApiFactory.create(config);

  AuthorizationRequest request = new AuthorizationRequest()
          .setParameters("redirect_uri=https://client.example.org/cb/example.com"
                  + "&response_type=code"
                  + "&client_id=12898884596863"
                  + "&scope=openid"
                  + "&nonce=n-0S6_WzA2Mj");

  AuthorizationResponse response = authleteApi.authorization(request);
  System.out.println(response.getAction()); // INTERACTION
  System.out.println(response.getTicket());
  ```

  ```typescript TypeScript theme={null}
  import { Authlete } from "@authlete/typescript-sdk";

  // Initialize the client once and reuse it across the steps below.
  const authlete = new Authlete({
    serverURL: "https://us.authlete.com",
    bearer: process.env["SERVICE_ACCESS_TOKEN"] ?? "",
  });
  const SERVICE_ID = "10738933707579";

  const res = await authlete.authorization.processRequest({
    serviceId: SERVICE_ID,
    authorizationRequest: {
      parameters:
        "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj",
    },
  });
  console.log(res.action); // "INTERACTION"
  console.log(res.ticket);
  ```

  ```ruby Ruby theme={null}
  require "authlete_ruby_sdk"

  # Initialize the client once and reuse it across the steps below.
  client     = Authlete::Client.new(
    bearer:     ENV.fetch("SERVICE_ACCESS_TOKEN"),
    server_url: "https://us.authlete.com"
  )
  SERVICE_ID = "10738933707579"

  req = Authlete::Models::Components::AuthorizationRequest.new(
    parameters: "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj"
  )
  res = client.authorization.process_request(service_id: SERVICE_ID, authorization_request: req)

  puts res.authorization_response.action # "INTERACTION"
  puts res.authorization_response.ticket
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"log"
  	"os"

  	authlete "github.com/authlete/authlete-go-sdk"
  	"github.com/authlete/authlete-go-sdk/models/components"
  )

  func main() {
  	ctx := context.Background()

  	// Initialize the client once and reuse it across the steps below.
  	s := authlete.New(
  		authlete.WithServerURL("https://us.authlete.com"),
  		authlete.WithSecurity(os.Getenv("SERVICE_ACCESS_TOKEN")),
  	)
  	const serviceID = "10738933707579"

  	res, err := s.Authorization.ProcessRequest(ctx, serviceID, components.AuthorizationRequest{
  		Parameters: "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj",
  	})
  	if err != nil {
  		log.Fatal(err)
  	}
  	log.Printf("action=%v", *res.AuthorizationResponse.Action) // INTERACTION
  	log.Printf("ticket=%s", *res.AuthorizationResponse.Ticket)
  }
  ```
</CodeGroup>

成功すると、Authlete は次のようなレスポンスを返します (簡略化のため省略されています) (メッセージ #6)。

```json theme={null}
{
   "resultMessage" : "[A004001] Authlete has successfully issued a ticket to the service (API Key = 10738933707579) for the authorization request from the client (ID = 12898884596863). [response_type=code, openid=true]",
   "resultCode" : "A004001",
   "client" : { [...] },
   "ticket" : "bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU",
   "action" : "INTERACTION",
   [...]
   "service" : {
      [...]
      "supportedClaims" : null,
      "supportedScopes" : null,
   }
}
```

このレスポンスの `resultMessage`、`action`、`ticket` の 3 つのキー/値ペアに注意してください。

* `resultMessage`: リクエスト処理の結果を人間が読める形式で提供します（詳細は [Authlete の結果コードの解釈](/ja/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes) を参照）。`openid=true` は、リクエストが OIDC プロトコルに従って処理されることを示します。
* `action`: 認可サーバーが次に行うべきアクションを示します（詳細は [アクションハンドリング](/ja/get-started/concepts/action-handling) を参照）。
* `ticket`: 次のステップで別の API にリクエストを行うために必要です（詳細は [2 段階の API 呼び出し](/ja/get-started/concepts/two-step-api-calls) を参照）。

Authlete はまた、レスポンス内にサービスおよびクライアント情報を提供します。認可サーバーはこれを使用して、リソースオーナーがクライアントに対してアクセスを許可するかどうかを尋ねます。

## ユーザー認証と認証結果の共有確認

リソースオーナーと認可サーバー間の実際のやり取りは、このチュートリアルの範囲外です。通常、認可サーバーは以下のプロセスを実行します：

* 資格情報 (例: ID/パスワード) を使用してユーザーを認証します。
* ユーザーのロールや権限を決定します。
* ユーザーに、認証結果をクライアントと共有するかどうかを確認します。

これらのプロセスは、ステップ #7、#8、#9 に対応します。

## 認可コードの発行

次に進む前に、認可サーバーが以下の状態にあると仮定します：

* 認可サーバーがリソースオーナーを認証し、`subject` パラメータとして Authlete に共有されるリソースオーナーの識別子が `testuser01` であることを決定した。
* 認可サーバーがリソースオーナーの同意を取得した。

認可サーバーは、Authlete の [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) にリクエストを送信し、認可コードを発行します。このリクエストには、`subject` と `/auth/authorization` API のレスポンスに含まれる `ticket` の値が含まれます。

以下の `curl` コマンドを実行してください (メッセージ #10)。先ほど生成した `<Service ID>`、`<Service Access Token>`、および `<Ticket>` を置き換えて使用します。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization/issue \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "ticket": "<Ticket e.g. bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU>", "subject": "testuser01" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "ticket": "<Ticket e.g. bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU>", "subject": "testuser01" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // Reuse the authleteApi client created in the authorization step.
  AuthorizationIssueRequest request = new AuthorizationIssueRequest()
          .setTicket("bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU")
          .setSubject("testuser01");

  AuthorizationIssueResponse response = authleteApi.authorizationIssue(request);
  System.out.println(response.getAction());           // LOCATION
  System.out.println(response.getAuthorizationCode());
  System.out.println(response.getResponseContent());
  ```

  ```typescript TypeScript theme={null}
  // Reuse the `authlete` client and SERVICE_ID created in the authorization step.
  const res = await authlete.authorization.issue({
    serviceId: SERVICE_ID,
    authorizationIssueRequest: {
      ticket: "bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU",
      subject: "testuser01",
    },
  });
  console.log(res.action);            // "LOCATION"
  console.log(res.authorizationCode);
  console.log(res.responseContent);
  ```

  ```ruby Ruby theme={null}
  # Reuse the `client` and SERVICE_ID created in the authorization step.
  req = Authlete::Models::Components::AuthorizationIssueRequest.new(
    ticket:  "bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU",
    subject: "testuser01"
  )
  res = client.authorization.issue_response(service_id: SERVICE_ID, authorization_issue_request: req)

  puts res.authorization_issue_response.action            # "LOCATION"
  puts res.authorization_issue_response.authorization_code
  puts res.authorization_issue_response.response_content
  ```

  ```go Go theme={null}
  // Reuse the `s` client and serviceID created in the authorization step.
  res, err := s.Authorization.Issue(ctx, serviceID, components.AuthorizationIssueRequest{
  	Ticket:  "bi2Kxe2WW5mK_GZ_fDFOpK1bnY6xTy40Ap_8nxf-7AU",
  	Subject: "testuser01",
  })
  if err != nil {
  	log.Fatal(err)
  }
  log.Printf("action=%v", *res.AuthorizationIssueResponse.Action) // LOCATION
  log.Printf("authorizationCode=%s", *res.AuthorizationIssueResponse.AuthorizationCode)
  log.Printf("responseContent=%s", *res.AuthorizationIssueResponse.ResponseContent)
  ```
</CodeGroup>

成功すると、Authlete は次のようなレスポンスを返します (ステップ #11)。

```json theme={null}
{
   "action" : "LOCATION",
   "resultCode" : "A040001",
   "accessTokenDuration" : 0,
   "accessTokenExpiresAt" : 0,
   "resultMessage" : "[A040001] The authorization request was processed successfully.",
   "responseContent" : "https://client.example.org/cb/example.com?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U",
   "authorizationCode" : "GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U",
   ...
}
```

このレスポンスの `resultMessage`、`action`、`responseContent` の 3 つのキー/値ペアに注目してください。

* `resultMessage`: リクエスト処理の結果を人間が読める形式で提供します（詳細は [Authlete の結果コードの解釈](/ja/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes) を参照）。
* `action`: 認可サーバーが次に行うべきアクションを示します。このレスポンスの値は `LOCATION` であり、認可サーバーがユーザーエージェントにリダイレクトレスポンスを返すべきことを示しています（詳細は [アクションハンドリング](/ja/get-started/concepts/action-handling) を参照）。
* `responseContent`: 認可サーバーからのレスポンス内容を示します（詳細は [アクションハンドリング](/ja/get-started/concepts/action-handling) を参照）。

認可サーバーは、以下のようなレスポンスをユーザーエージェントに送信します (ステップ #12)。

```http theme={null}
HTTP/1.1 302 Found
Location: https://client.example.org/cb/example.com
 ?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U
```

## 認可コードの発行に失敗した場合

場合によっては、認可サーバーがリソースオーナーから認可を受けられないことがあります。この場合、クライアントにトークンを発行せず、認可フローが終了したことを通知する必要があります。Authlete の [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API は、クライアントに送信されるメッセージおよびレスポンスの転送方法の観点から、この終了プロセスをサポートします。

まとめると、通常の状況では、認可サーバーはユーザー認証と同意の結果に応じて、`/auth/authorization/issue` または `/auth/authorization/fail` API のいずれかにリクエストを送信します。

## トークンリクエスト

ここでは、ユーザーエージェントが認可サーバーからのリダイレクトレスポンスを受け取ったと仮定します。このリダイレクトレスポンスは以下のようにクライアントに転送されます (ステップ #13)。

```http theme={null}
GET /cb/example.com?code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U HTTP/1.1
Host: client.example.org
```

クライアントは `code` パラメータの値を抽出し、それを使用してトークンリクエストを構築し、認可サーバーに送信します。以下はその例です (改行を追加して可読性を向上させています)。`https://as.example.com/token` は、このチュートリアルにおけるトークンエンドポイント URI です (ステップ #14)。

```http theme={null}
POST /token HTTP/1.1
Host: as.example.com
Authorization: Basic base64(12898884596863:-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
 &code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U
 &redirect_uri=https://client.example.org/cb/example.com
```

認可サーバーは、リクエストのパラメータを評価し、クライアントにトークンレスポンスを返すために Authlete API にリクエストを送信します。

ここで、このステップをシミュレートするために Authlete の [`/auth/token`](/api-reference/token-endpoint/process-token-request) API を使用します。この API はリクエストを評価し、レスポンスを生成します。

以下の `curl` コマンドを実行してください (ステップ #15)。先ほど生成した `<Service ID>`、`<Service Access Token>`、`<Client ID>`、`<Client Secret>`、および `<Code>` を置き換えて使用します。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/token \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "clientId": "<Client ID e.g. 12898884596863>", "clientSecret": "<Client Secret e.g. -olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg>", "parameters": "grant_type=authorization_code&code=<Code e.g. GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U>&redirect_uri=https://client.example.org/cb/example.com" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "clientId": "<Client ID e.g. 12898884596863>", "clientSecret": "<Client Secret e.g. -olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg>", "parameters": "grant_type=authorization_code&code=<Code e.g. GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U>&redirect_uri=https://client.example.org/cb/example.com" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // Reuse the authleteApi client created in the authorization step.
  TokenRequest request = new TokenRequest()
          .setClientId("12898884596863")
          .setClientSecret("-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg")
          .setParameters("grant_type=authorization_code"
                  + "&code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U"
                  + "&redirect_uri=https://client.example.org/cb/example.com");

  TokenResponse response = authleteApi.token(request);
  System.out.println(response.getAction());          // OK
  System.out.println(response.getAccessToken());
  System.out.println(response.getResponseContent());
  ```

  ```typescript TypeScript theme={null}
  // Reuse the `authlete` client and SERVICE_ID created in the authorization step.
  const res = await authlete.token.process({
    serviceId: SERVICE_ID,
    tokenRequest: {
      clientId: "12898884596863",
      clientSecret:
        "-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg",
      parameters:
        "grant_type=authorization_code&code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U&redirect_uri=https://client.example.org/cb/example.com",
    },
  });
  console.log(res.action);          // "OK"
  console.log(res.accessToken);
  console.log(res.responseContent);
  ```

  ```ruby Ruby theme={null}
  # Reuse the `client` and SERVICE_ID created in the authorization step.
  req = Authlete::Models::Components::TokenRequest.new(
    client_id:     "12898884596863",
    client_secret: "-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg",
    parameters:    "grant_type=authorization_code&code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U&redirect_uri=https://client.example.org/cb/example.com"
  )
  res = client.tokens.process_request(service_id: SERVICE_ID, token_request: req)

  puts res.token_response.action           # "OK"
  puts res.token_response.access_token
  puts res.token_response.response_content
  ```

  ```go Go theme={null}
  // Reuse the `s` client and serviceID created in the authorization step.
  res, err := s.Token.Process(ctx, serviceID, components.TokenRequest{
  	ClientID:     types.String("12898884596863"), // types: github.com/authlete/authlete-go-sdk/types
  	ClientSecret: types.String("-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg"),
  	Parameters:   "grant_type=authorization_code&code=GrYz5vtk6VaF0jxfnDrB2yvmk4deIrnMkrGT07JdM5U&redirect_uri=https://client.example.org/cb/example.com",
  })
  if err != nil {
  	log.Fatal(err)
  }
  log.Printf("action=%v", *res.TokenResponse.Action) // OK
  log.Printf("accessToken=%s", *res.TokenResponse.AccessToken)
  log.Printf("responseContent=%s", *res.TokenResponse.ResponseContent)
  ```
</CodeGroup>

成功すると、Authlete は次のようなレスポンスを返します (メッセージ #16)。

```json theme={null}
{
   "resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
   "action" : "OK",
   "clientIdAliasUsed" : false,
   "subject" : "testuser01",
   "resultCode" : "A050001",
   "refreshTokenExpiresAt" : 1730552811449,
   "grantType" : "AUTHORIZATION_CODE",
   "accessToken" : "7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
   "idToken" : "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk",
   "responseContent" : "{\"access_token\":\"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY\",\"refresh_token\":\"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE\",\"scope\":\"openid\",\"id_token\":\"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk\",\"token_type\":\"Bearer\",\"expires_in\":86400}",
   "scopes" : [
      "openid"
   ],
   "accessTokenDuration" : 86400,
   "type" : "tokenResponse",
   "refreshToken" : "T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
   "accessTokenExpiresAt" : 1730552811449,
   "refreshTokenDuration" : 864000,
   "clientId" : 12898884596863
}
```

このレスポンスの `resultMessage`、`action`、`responseContent` の 3 つのキー/値ペアに注目してください。

* `resultMessage`: リクエスト処理の結果を人間が読める形式で提供します（詳細は [Authlete の結果コードの解釈](/ja/configuration-reference/error-handling-debugging/interpreting-authletes-result-codes) を参照）。
* `action`: 認可サーバーが次に行うべきアクションを示します。この場合の値は `OK` であり、認可サーバーがクライアントにトークンレスポンスを返すべきことを示しています（詳細は [アクションハンドリング](/ja/get-started/concepts/action-handling) を参照）。
* `responseContent`: 認可サーバーからクライアントへのレスポンス内容を示します（詳細は [アクションハンドリング](/ja/get-started/concepts/action-handling) を参照）。

認可サーバーは以下のレスポンスをクライアントに送信する必要があります (メッセージ #17)。

```http theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{
 "access_token":"7FfwOnGjVHwxXhs2Wr67XV1-ZhQaoy3ctKcGkLyKxuY",
 "refresh_token":"T1h7fJ6k55CyipDtXNPbzN8ta3FgAAf4QKjo36OVfIE",
 "scope":"openid",
 "id_token":"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk",
 "token_type":"Bearer",
 "expires_in":86400
}
```

クライアントはこのレスポンスを受信すると、必要に応じて ID トークンをデコードします。

## ID トークンのデコード

クライアントは、レスポンス内の `id_token` の値をデコードし、その有効性を確認する必要があります。

このチュートリアルでは、無料のオープンソースツール [Online JWT Verifier](https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html) を使用してトークンをデコードします。

以下のリンクを開き、JWT Verifier UI の *(Step 1) Set JWT (JSON Web Token) to verify* テキストエリアに `id_token` の値を貼り付けてください。このチュートリアルの例では  ID
トークンの値として `eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImlzcyI6Imh0dHBzOi8vYXV0aGxldGUuY29tIiwiZXhwIjoxNTU5MTA2ODE1LCJpYXQiOjE1NTkwMjA0MTUsIm5vbmNlIjoibi0wUzZfV3pBMk1qIn0.5uSFMTGnubyvtiExHc9l7HT9UsF8a_Qb0STtWzyclBk` を使用します。

その後、*(Step 3) Verify* セクション内の **Just Decode JWT** ボタンをクリックして、*Parsed JWT* セクションにデコードされた内容を確認してください。

<figure class="figure bg-transparent border-0">
  <img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/get-started/authlete-decoded-id-token-authlete-url.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=bc8f89d1a5607fdcbb10003a0f8eea48" class="figure-img img-fluid rounded" alt="デコードされた JWT トークン (ヘッダーとペイロード)" width="866" height="1233" data-path="get-started/authlete-decoded-id-token-authlete-url.png" />
</figure>

デコード結果は以下のようになります。

**ヘッダー:**

```json theme={null}
{
  "alg": "HS256"
}
```

**ペイロード:**

```json theme={null}
{
  "sub": "testuser01",
  "aud": [
    "12898884596863"
  ],
  "iss": "https://authlete.com",
  "exp": 1730552811,
  "iat": 1730552811,
  "nonce": "n-0S6_WzA2Mj"
}
```

上記の内容には、修正が必要な点がいくつかあります。

* `iss` が `https://authlete.com` となっていますが、これは Authlete のデフォルト値です。このチュートリアルでは、認可サーバーの識別子として `https://as.example.com` にする必要があります。
* `sub` はユーザーの識別に関する唯一の属性です。クライアントの利便性のために、他のユーザー属性を追加することが望ましいです。

次のセクションでは、`iss` の値を修正し、追加のクレームを追加します。

# ID トークンの修正

## 発行者識別子の設定

[https://console.authlete.com](https://console.authlete.com) にログインし、このチュートリアルで作成した *Demo AS* サービスを選択します。**サービス設定** ボタンをクリックして、サービス設定にアクセスします。

<figure class="figure bg-transparent border-0">
  <img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/get-started/authlete-service-click-service-settings.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=99462b893f75a8e6805e36d275b86c89" class="figure-img img-fluid rounded" alt="サービス設定ボタンを強調表示した Authlete サービス" width="1659" height="392" data-path="get-started/authlete-service-click-service-settings.png" />
</figure>

*一般* タブにある *発行者識別子* のデフォルト値が `https://authlete.com` であることに注意してください。この値を `https://as.example.com` に変更し、ページ下部の **変更を保存** ボタンをクリックして保存します。
確認ダイアログが表示されたら **OK** をクリックします。

<figure class="figure bg-transparent border-0">
  <img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/get-started/authlete-service-issuer-id-url.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=58a1a563aaf6aa6fade54df58d6c9b70" class="figure-img img-fluid rounded" alt="更新された発行者識別子 URL を表示する Authlete サービス設定" width="1659" height="970" data-path="get-started/authlete-service-issuer-id-url.png" />
</figure>

これで、ID トークンの発行者識別子 `iss` が修正されました。

## 認可リクエストの送信

先ほどと同じ認可リクエストを送信します (便宜上、同じ `nonce` 値を使用します)。このリクエストは Authlete の [`/auth/authorization`](/api-reference/authorization-endpoint/process-authorization-request) API に送信されます (ステップ #5)。以下のように `<Service ID>`、`<Service Access Token>`、および `<Client ID>` を置き換えて使用してください。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=<Client ID e.g. 12898884596863>&scope=openid&nonce=n-0S6_WzA2Mj" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "parameters": "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=<Client ID e.g. 12898884596863>&scope=openid&nonce=n-0S6_WzA2Mj" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // Reuse the authleteApi client created earlier — the same authorization request as before.
  AuthorizationRequest request = new AuthorizationRequest()
          .setParameters("redirect_uri=https://client.example.org/cb/example.com"
                  + "&response_type=code"
                  + "&client_id=12898884596863"
                  + "&scope=openid"
                  + "&nonce=n-0S6_WzA2Mj");

  AuthorizationResponse response = authleteApi.authorization(request);
  System.out.println(response.getAction()); // INTERACTION
  System.out.println(response.getTicket());
  ```

  ```typescript TypeScript theme={null}
  // Reuse the `authlete` client and SERVICE_ID created earlier.
  const res = await authlete.authorization.processRequest({
    serviceId: SERVICE_ID,
    authorizationRequest: {
      parameters:
        "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj",
    },
  });
  console.log(res.action); // "INTERACTION"
  console.log(res.ticket);
  ```

  ```ruby Ruby theme={null}
  # Reuse the `client` and SERVICE_ID created earlier.
  req = Authlete::Models::Components::AuthorizationRequest.new(
    parameters: "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj"
  )
  res = client.authorization.process_request(service_id: SERVICE_ID, authorization_request: req)

  puts res.authorization_response.action # "INTERACTION"
  puts res.authorization_response.ticket
  ```

  ```go Go theme={null}
  // Reuse the `s` client and serviceID created earlier.
  res, err := s.Authorization.ProcessRequest(ctx, serviceID, components.AuthorizationRequest{
  	Parameters: "redirect_uri=https://client.example.org/cb/example.com&response_type=code&client_id=12898884596863&scope=openid&nonce=n-0S6_WzA2Mj",
  })
  if err != nil {
  	log.Fatal(err)
  }
  log.Printf("action=%v", *res.AuthorizationResponse.Action) // INTERACTION
  log.Printf("ticket=%s", *res.AuthorizationResponse.Ticket)
  ```
</CodeGroup>

以下のようなレスポンスを受け取るはずです (簡略化のため省略されています)。

```json theme={null}
{
   [...]
   "action" : "INTERACTION",
   "resultCode" : "A004001",
   "resultMessage" : "[A004001] Authlete has successfully issued a ticket to the service (API Key = 10738933707579) for the authorization request from the client (ID = 12898884596863). [response_type=code, openid=true]",
   "ticket" : "JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A",
}

```

## 追加クレームの追加

次に、認可コードを発行するために Authlete の [`/auth/authorization/issue`](/api-reference/authorization-endpoint/issue-authorization-response) API にリクエストを送信します。このリクエストに含まれるクレームには以下の追加項目を含めます。

| 項目               | 値                        |
| ---------------- | ------------------------ |
| `name`           | `Test User`              |
| `email`          | `testuser01@example.com` |
| `email_verified` | true                     |

以下の `claims` パラメータを使用してクレームを追加します。リクエストは以下のように構築されます。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/authorization/issue \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "ticket": "<Ticket e.g. JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A>", "subject": "testuser01", "claims": "{\"name\": \"Test User\", \"email\": \"testuser01@example.com\", \"email_verified\": true}" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "ticket": "<Ticket e.g. JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A>", "subject": "testuser01", "claims": "{\"name\": \"Test User\", \"email\": \"testuser01@example.com\", \"email_verified\": true}" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // Reuse the authleteApi client created earlier. `claims` is a JSON string.
  AuthorizationIssueRequest request = new AuthorizationIssueRequest()
          .setTicket("JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A")
          .setSubject("testuser01")
          .setClaims("{\"name\":\"Test User\","
                  + "\"email\":\"testuser01@example.com\","
                  + "\"email_verified\":true}");

  AuthorizationIssueResponse response = authleteApi.authorizationIssue(request);
  System.out.println(response.getAction());           // LOCATION
  System.out.println(response.getAuthorizationCode());
  System.out.println(response.getResponseContent());
  ```

  ```typescript TypeScript theme={null}
  // Reuse the `authlete` client and SERVICE_ID created earlier. `claims` is a JSON string.
  const res = await authlete.authorization.issue({
    serviceId: SERVICE_ID,
    authorizationIssueRequest: {
      ticket: "JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A",
      subject: "testuser01",
      claims: JSON.stringify({
        name: "Test User",
        email: "testuser01@example.com",
        email_verified: true,
      }),
    },
  });
  console.log(res.action);            // "LOCATION"
  console.log(res.authorizationCode);
  console.log(res.responseContent);
  ```

  ```ruby Ruby theme={null}
  # Reuse the `client` and SERVICE_ID created earlier. `claims` is a JSON string.
  req = Authlete::Models::Components::AuthorizationIssueRequest.new(
    ticket:  "JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A",
    subject: "testuser01",
    claims:  '{"name":"Test User","email":"testuser01@example.com","email_verified":true}'
  )
  res = client.authorization.issue_response(service_id: SERVICE_ID, authorization_issue_request: req)

  puts res.authorization_issue_response.action            # "LOCATION"
  puts res.authorization_issue_response.authorization_code
  puts res.authorization_issue_response.response_content
  ```

  ```go Go theme={null}
  // Reuse the `s` client and serviceID created earlier. Claims is a *string of JSON.
  res, err := s.Authorization.Issue(ctx, serviceID, components.AuthorizationIssueRequest{
  	Ticket:  "JjQ_Th1UvZyU5MsdKTLIfLv3VlKwEiYnnULmW6l_d9A",
  	Subject: "testuser01",
  	Claims:  types.String(`{"name":"Test User","email":"testuser01@example.com","email_verified":true}`), // types: github.com/authlete/authlete-go-sdk/types
  })
  if err != nil {
  	log.Fatal(err)
  }
  log.Printf("action=%v", *res.AuthorizationIssueResponse.Action) // LOCATION
  log.Printf("authorizationCode=%s", *res.AuthorizationIssueResponse.AuthorizationCode)
  log.Printf("responseContent=%s", *res.AuthorizationIssueResponse.ResponseContent)
  ```
</CodeGroup>

以下のようなレスポンスを受け取るはずです。

```json theme={null}
{
   "responseContent" : "https://client.example.org/cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
   "accessTokenDuration" : 0,
   "authorizationCode" : "ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI",
   "accessTokenExpiresAt" : 0,
   "type" : "authorizationIssueResponse",
   "resultMessage" : "[A040001] The authorization request was processed successfully.",
   "resultCode" : "A040001",
   "action" : "LOCATION"
}
```

認可サーバーは以下のリダイレクトレスポンスをユーザーエージェントに送信する必要があります。その後、ユーザーエージェントは以下のような HTTP `GET` リクエストをクライアントに送信します。

```shell theme={null}
GET /cb/example.com?code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI HTTP/1.1
Host: client.example.org
```

## トークンリクエストの送信

クライアントは以下のようにトークンリクエストを認可サーバーに送信します (改行は可読性向上のため追加されています)。

```http theme={null}
POST /token HTTP/1.1
Host: as.example.com
Authorization: Basic base64(12898884596863:-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
 &code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI
 &redirect_uri=https://client.example.org/cb/example.com
```

認可サーバーは、Authlete の [`/auth/token`](/api-reference/token-endpoint/process-token-request) API を呼び出します。

<CodeGroup>
  ```text cURL theme={null}
  curl -s -X POST https://us.authlete.com/api/<Service ID e.g. 10738933707579>/auth/token \
  -H 'Authorization: Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>' \
  -H 'Content-Type: application/json' \
  -d '{ "clientId": "<Client ID e.g. 12898884596863>", "clientSecret": "<Client Secret e.g. -olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg>", "parameters": "grant_type=authorization_code&code=<Code e.g. ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI>&redirect_uri=https://client.example.org/cb/example.com" }'
  ```

  ```powershell PowerShell theme={null}
  $headers = @{ Authorization = "Bearer <Service Access Token e.g. Xg6jVpJCvsaXvy2ks8R5WzjdMYlvQqOym3slDX0wNhQ>" }
  $body = '{ "clientId": "<Client ID e.g. 12898884596863>", "clientSecret": "<Client Secret e.g. -olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg>", "parameters": "grant_type=authorization_code&code=<Code e.g. ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI>&redirect_uri=https://client.example.org/cb/example.com" }'
  Invoke-RestMethod -Method Post `
    -Uri "https://us.authlete.com/api/<Service" `
    -Headers $headers -ContentType "application/json" -Body $body
  ```

  ```java Java theme={null}
  // Reuse the authleteApi client created earlier.
  TokenRequest request = new TokenRequest()
          .setClientId("12898884596863")
          .setClientSecret("-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg")
          .setParameters("grant_type=authorization_code"
                  + "&code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI"
                  + "&redirect_uri=https://client.example.org/cb/example.com");

  TokenResponse response = authleteApi.token(request);
  System.out.println(response.getAction());          // OK
  System.out.println(response.getAccessToken());
  System.out.println(response.getResponseContent());
  ```

  ```typescript TypeScript theme={null}
  // Reuse the `authlete` client and SERVICE_ID created earlier.
  const res = await authlete.token.process({
    serviceId: SERVICE_ID,
    tokenRequest: {
      clientId: "12898884596863",
      clientSecret:
        "-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg",
      parameters:
        "grant_type=authorization_code&code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI&redirect_uri=https://client.example.org/cb/example.com",
    },
  });
  console.log(res.action);          // "OK"
  console.log(res.accessToken);
  console.log(res.responseContent);
  ```

  ```ruby Ruby theme={null}
  # Reuse the `client` and SERVICE_ID created earlier.
  req = Authlete::Models::Components::TokenRequest.new(
    client_id:     "12898884596863",
    client_secret: "-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg",
    parameters:    "grant_type=authorization_code&code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI&redirect_uri=https://client.example.org/cb/example.com"
  )
  res = client.tokens.process_request(service_id: SERVICE_ID, token_request: req)

  puts res.token_response.action           # "OK"
  puts res.token_response.access_token
  puts res.token_response.response_content
  ```

  ```go Go theme={null}
  // Reuse the `s` client and serviceID created earlier.
  res, err := s.Token.Process(ctx, serviceID, components.TokenRequest{
  	ClientID:     types.String("12898884596863"), // types: github.com/authlete/authlete-go-sdk/types
  	ClientSecret: types.String("-olDIKD9BihRfB8O1JxobUEKBZ7PIV5Z6oaqxAshmoUtUZgB-wjmmxTYDiDV6vM_Mgl267PeNrRftq8cWplvmg"),
  	Parameters:   "grant_type=authorization_code&code=ILePyGjraVgeU_fzaQRfd0gv10pzxgcpHY_vHT2dsPI&redirect_uri=https://client.example.org/cb/example.com",
  })
  if err != nil {
  	log.Fatal(err)
  }
  log.Printf("action=%v", *res.TokenResponse.Action) // OK
  log.Printf("accessToken=%s", *res.TokenResponse.AccessToken)
  log.Printf("responseContent=%s", *res.TokenResponse.ResponseContent)
  ```
</CodeGroup>

Authlete は以下のようなレスポンスを返します。

```json theme={null}
{
   "grantType" : "AUTHORIZATION_CODE",
   "responseContent" : "{\"access_token\":\"R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro\",\"refresh_token\":\"k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U\",\"scope\":\"openid\",\"id_token\":\"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0\",\"token_type\":\"Bearer\",\"expires_in\":86400}",
   "resultMessage" : "[A050001] The token request (grant_type=authorization_code) was processed successfully.",
   "accessTokenExpiresAt" : 1730554257440,
   "accessToken" : "R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro",
   "resultCode" : "A050001",
   "scopes" : [
      "openid"
   ],
   "refreshTokenExpiresAt" : 1730554257440,
   "subject" : "testuser01",
   "action" : "OK",
   "refreshTokenDuration" : 864000,
   "accessTokenDuration" : 86400,
   "refreshToken" : "k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U",
   "clientIdAliasUsed" : false,
   "idToken" : "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0",
   "clientId" : 12898884596863
}
```

認可サーバーは以下のレスポンスをクライアントに返すことが期待されます (メッセージ #17)。

```json theme={null}
HTTP/1.1 200 OK
Content-Type: application/json

{
 "access_token":"R4sd3s02Y1Gj72iI5Md6ZkGapXZ6mSnIEdihTvrM_Ro",
 "refresh_token":"k4WqWw2tcDOHxXXo29NxOCwQJyeDOtZ6aw_Y9Ugy-6U",
 "scope":"openid",
 "id_token":"eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0",
 "token_type":"Bearer",
 "expires_in":86400
}
```

クライアントがこのレスポンスを受信した後、再び [Online JWT Verifier](https://kjur.github.io/jsrsasign/tool/tool_jwtveri.html) を使用してトークンをデコードしてみましょう。

上記リンクを開き、レスポンス内の `id_token` の値を *Step 1* のテキストエリアに貼り付けます。このチュートリアルでは ID トークンの値として `eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiVGVzdCBVc2VyIiwiZW1haWwiOiJ0ZXN0dXNlcjAxQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImlzcyI6Imh0dHBzOi8vYXMuZXhhbXBsZS5jb20iLCJzdWIiOiJ0ZXN0dXNlcjAxIiwiYXVkIjpbIjEyODk4ODg0NTk2ODYzIl0sImV4cCI6MTU1OTEzNzMwMSwiaWF0IjoxNTU5MDUwOTAxLCJub25jZSI6Im4tMFM2X1d6QTJNaiJ9.8ngbBoGLUvHXIO4VyGN0-txJfE5Yq86xElMSxqGlLv0` を使用します。

その後、*Step 3* の **Just Decode JWT** ボタンをクリックし、*Parsed JWT* セクションでデコードされた内容を確認してください。

<figure class="figure bg-transparent border-0">
  <img src="https://mintcdn.com/authlete/6e8OPufcWwl4fTPw/get-started/authlete-decoded-id-token-example-url.png?fit=max&auto=format&n=6e8OPufcWwl4fTPw&q=85&s=e43b9b49cf5ff79957fed950a852337a" class="figure-img img-fluid rounded" alt="追加クレームを含む JWT トークンのデコード結果" width="866" height="1277" data-path="get-started/authlete-decoded-id-token-example-url.png" />
</figure>

デコード結果は以下の通りです。

**ヘッダー:**

```json theme={null}
{
  "alg": "HS256"
}
```

**ペイロード:**

```json theme={null}
{
  "name": "Test User",
  "email": "testuser01@example.com",
  "email_verified": true,
  "iss": "https://as.example.com",
  "sub": "testuser01",
  "aud": [
    "12898884596863"
  ],
  "exp": 1559137301,
  "iat": 1559050901,
  "nonce": "n-0S6_WzA2Mj"
}
```

これにより、以下の点が確認できます。

* `iss` の値が正しく設定されています。
* `name`、`email`、および `email_verified` といった追加クレームが含まれています。

# まとめ

このチュートリアルでは、Authlete API を使用して OpenID Connect プロバイダーとして動作する認可サーバーをシミュレートし、認可コードフローを用いて ID トークンを発行する方法を学びました。以下の重要なステップを完了しました。

* Authlete API を使用して認可コードフローを認可サーバー (OIDC アイデンティティプロバイダー) に実装する方法
* Authlete 管理コンソールで発行者識別子や追加クレームを設定し、それらを ID トークンからデコードする方法

このウォークスルーでは一部のプロセスを簡略化しましたが、Authlete をバックエンドサービスとして利用することで、独自のインフラストラクチャ上でホストされる認可サーバーがどのように動作するかを深く理解できたはずです。独自の認可サーバーを構築する場合、複雑なロジックやコード、シークレットの安全な取り扱いに細心の注意を払う必要がありますが、Authlete API を利用することで、これらの実装を大幅に簡素化できます。

# 次のステップ

以下の機能を試し、Authlete をさらに深く理解しましょう。

* [`/auth/authorization/fail`](/api-reference/authorization-endpoint/fail-authorization-request) API (参考: [“fail” API を用いたエラーレスポンスの生成](/ja/configuration-reference/error-handling-debugging/generating-error-response-using-fail-api))
* [`/auth/userinfo`](/api-reference/userinfo-endpoint/process-userinfo-request) API (参照: [Access token verification in Userinfo API](/ja/configuration-reference/endpoints/access-token-verification-in-userinfo-api))
* [トークン管理](/api-reference/token-operations/list-issued-tokens) API (参照: [ユーザーがクライアントに付与した認可の取得・変更・取り消し](/ja/configuration-reference/tokens-and-claims/managing-authorizations-issued-tokens-granted-for-a-client-by-a-user))
* [認可サーバーの実装](https://github.com/authlete) (参照: [Authorization Server Implementation - Getting Started](/ja/get-started/quickstarts/getting-started))
* 公開鍵暗号を使用した ID トークンの署名 (参照: [ID トークンの署名鍵の変更](/ja/configuration-reference/tokens-and-claims/changing-signing-key-for-id-tokens))
