Device flow: Logto での認証 (Authentication)
このガイドは、Logto コンソールで「ネイティブ」タイプかつ device flow を認可フローとして作成したアプリケーションがあることを前提としています。
はじめに
OAuth 2.0 デバイス認可グラント(device flow)は、スマート TV、ゲームコンソール、CLI ツール、IoT デバイスなど、入力機能が制限されたデバイス向けに設計されています。ユーザーはデバイス上でサインインプロセスを開始し、別のブラウザ搭載デバイス(スマートフォンやノート PC など)で認証 (Authentication) を完了できます。
デバイス自体がブラウザベースのサインインフローを処理できないため、デバイスは短いコードと URL を表示します。ユーザーは別のデバイスでその URL にアクセスし、コードを入力してサインインします。その間、元のデバイスは認可 (Authorization) 完了まで Logto にポーリングします。
アプリケーションのクレデンシャルを取得
Logto コンソールでアプリケーション詳細ページに移動し、次のクレデンシャルを取得します:
- App ID:アプリケーションの一意の識別子(
client_idとも呼ばれます)。 - Logto エンドポイント:Logto 認可サーバーのエンドポイント。Logto コンソールの「アプリケーション詳細」で確認できます。
Logto Cloud の場合、エンドポイントは https://{your-tenant-id}.logto.app です。
Device flow アプリはパブリッククライアントのため、App Secret は不要です。
デバイスコードのリクエスト
device flow を開始するには、デバイス認可エンドポイントに POST リクエストを送信します:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile'
レスポンスには次が含まれます:
| フィールド | 説明 |
|---|---|
device_code | トークンエンドポイントをポーリングする際にアプリで使用する一意のコード。 |
user_code | ユーザーがブラウザで入力するために表示する短いコード。 |
verification_uri | ユーザーが user_code を入力するための URL。 |
verification_uri_complete | user_code があらかじめ入力された URL。ユーザーはこの URL に直接アクセスして手動入力を省略できます(QR コードやクリック可能なリンクなどで提示可能)。 |
expires_in | device_code および user_code の有効期間(秒)。この時間を過ぎたらポーリングを停止してください。 |
ユーザーに認証 (Authentication) URL を表示
デバイス画面に user_code と verification_uri を表示します。
または、コードがあらかじめ入力された verification_uri_complete を利用することもできます。ユーザーは確認するだけで済みます。QR コードやクリック可能なリンクなど、表示方法は自由です。
トークンのポーリング
ユーザーがブラウザで認証 (Authentication) を完了する間、デバイスはトークンエンドポイントをポーリングします。アプリはポーリングリクエストの間隔を 5 秒以上 空けてください:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:device_code' \
--data-urlencode 'device_code=DEVICE_CODE'
DEVICE_CODE はデバイス認可レスポンスの device_code に置き換えてください。
ポーリングを停止する条件:
- 正常なトークンレスポンスを受信した場合
- device code レスポンスの
expires_in時間が経過した場合 expired_tokenやaccess_deniedなど再試行不可のエラーを受信した場合
トークンレスポンス
ユーザーが承認すると、レスポンスには次が含まれます:
| フィールド | 説明 |
|---|---|
access_token | アクセス トークン (Access token)。デフォルトでは不透明トークン (Opaque token) ですが、resource を指定した場合は aud がリソース URI の JWT です。 |
id_token | ユーザーのアイデンティティクレームを含む ID トークン (ID token)。openid スコープをリクエストした場合のみ含まれます。 |
refresh_token | 再認証なしで新しいトークンを取得するためのリフレッシュ トークン (Refresh token)。offline_access スコープをリクエストした場合のみ含まれます。 |
token_type | 常に Bearer。 |
expires_in | トークンの有効期間(秒)。 |
scope | 認可サーバーによって付与されたスコープ。 |
チェックポイント:デバイスフローのテスト
デバイスフロー連携をテストしましょう:
- アプリを実行し、device flow をトリガーして
device_codeとuser_codeを取得します。 - ブラウザで
verification_uriを開き、user_codeを入力するか、verification_uri_completeを使って手動入力を省略します。 - ブラウザでサインインプロセスを完了します。
- アプリがポーリング後にトークンを受信できることを確認します。
ユーザー情報の取得
ID トークン (ID token) クレームのデコード
トークンレスポンスで返される id_token は標準の JSON Web Token (JWT) です。Base64URL エンコードされたペイロード(JWT の 2 番目の部分、. で区切られた部分)をデコードすることで、追加のネットワークリクエストなしで基本的なユーザークレームにアクセスできます。
デコードされたペイロードには、リクエストしたスコープに応じて sub(ユーザー ID)、name、email などのクレームが含まれます。
本番環境では、JWT のクレームを信頼する前に署名検証を行ってください。Logto エンドポイント(https://your.logto.endpoint/oidc/jwks)の JWKS を使ってトークンを検証できます。
userinfo エンドポイントから取得
ID トークンにはリクエストしたスコープに基づく基本的なクレームが含まれます。一部の拡張クレーム(custom_data や identities など)は OIDC UserInfo エンドポイント からのみ取得できます:
curl --request GET 'https://your.logto.endpoint/oidc/me' \
--header 'Authorization: Bearer ACCESS_TOKEN'
ACCESS_TOKEN には、トークンレスポンスで取得した不透明トークン (Opaque token)(JWT リソーストークンではありません)を指定してください。レスポンスは、付与されたスコープに基づくユーザークレームを含む JSON オブジェクトです。
追加クレームのリクエスト
ID トークンに一部のユーザー情報が含まれていない場合があります。これは OAuth 2.0 および OpenID Connect (OIDC) が最小権限の原則(PoLP)に従って設計されており、Logto もこれらの標準に基づいているためです。
デフォルトでは、限られたクレーム (Claims) が返されます。より多くの情報が必要な場合は、追加のスコープ (Scopes) をリクエストして、より多くのクレーム (Claims) にアクセスできます。
「クレーム (Claim)」はサブジェクトについての主張であり、「スコープ (Scope)」はクレーム (Claims) のグループです。現在のケースでは、クレーム (Claim) はユーザーに関する情報の一部です。
スコープ (Scope) とクレーム (Claim) の関係の非規範的な例を示します:
「sub」クレーム (Claim) は「サブジェクト (Subject)」を意味し、ユーザーの一意の識別子(つまり、ユーザー ID)です。
Logto SDK は常に 3 つのスコープ (Scopes) をリクエストします:openid、profile、および offline_access。
追加のスコープをリクエストするには、デバイス認可リクエストの scope パラメーターに含めてください。たとえば、ユーザーのメールアドレスや電話番号をリクエストする場合:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access profile email phone'
スコープとクレーム
こちらはサポートされているスコープと対応するクレーム (Claims) の一覧です:
標準 OIDC スコープ
openid(デフォルト)
| Claim name | Type | 説明 |
|---|---|---|
| sub | string | ユーザーの一意の識別子 |
profile(デフォルト)
| Claim name | Type | 説明 |
|---|---|---|
| name | string | ユーザーのフルネーム |
| username | string | ユーザー名 |
| picture | string | エンドユーザーのプロフィール画像の URL。この URL は画像ファイル(例:PNG、JPEG、GIF 画像ファイル)を指す必要があり、画像を含む Web ページではありません。この URL は、エンドユーザーを説明する際に表示するのに適したプロフィール写真を特に参照するべきであり、エンドユーザーが撮影した任意の写真ではありません。 |
| created_at | number | エンドユーザーが作成された時刻。Unix エポック(1970-01-01T00:00:00Z)からのミリ秒数で表されます。 |
| updated_at | number | エンドユーザー情報が最後に更新された時刻。Unix エポック(1970-01-01T00:00:00Z)からのミリ秒数で表されます。 |
その他の 標準クレーム (Standard Claims) には、family_name、given_name、middle_name、nickname、preferred_username、profile、website、gender、birthdate、zoneinfo、locale などがあり、これらも profile スコープに含まれます(userinfo エンドポイントをリクエストする必要はありません)。上記のクレームとの違いは、これらのクレームは値が空でない場合のみ返される点です。一方、上記のクレームは値が空の場合 null が返されます。
標準クレーム (Standard Claims) とは異なり、created_at および updated_at クレームは秒ではなくミリ秒を使用しています。
email
| Claim name | Type | 説明 |
|---|---|---|
string | ユーザーのメールアドレス | |
| email_verified | boolean | メールアドレスが認証済みかどうか |
phone
| Claim name | Type | 説明 |
|---|---|---|
| phone_number | string | ユーザーの電話番号 |
| phone_number_verified | boolean | 電話番号が認証済みかどうか |
address
アドレスクレームの詳細については OpenID Connect Core 1.0 を参照してください。
(デフォルト) と記載されたスコープは常に Logto SDK によってリクエストされます。標準 OIDC スコープ下のクレーム (Claims) は、対応するスコープがリクエストされた場合、常に ID トークン (ID token) に含まれます — 無効化できません。
拡張スコープ
以下のスコープは Logto によって拡張されており、userinfo エンドポイント を通じてクレーム (Claims) を返します。これらのクレームは Console > Custom JWT を通じて ID トークン (ID token) に直接含めるよう設定することもできます。詳細は カスタム ID トークン を参照してください。
custom_data
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| custom_data | object | ユーザーのカスタムデータ |
identities
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| identities | object | ユーザーのリンク済みアイデンティティ | |
| sso_identities | array | ユーザーのリンク済み SSO アイデンティティ |
roles
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| roles | string[] | ユーザーのロール | ✅ |
urn:logto:scope:organizations
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| organizations | string[] | ユーザーが所属する組織 ID | ✅ |
| organization_data | object[] | ユーザーが所属する組織データ |
これらの組織クレーム (Organization Claims) は、不透明トークン (Opaque token) を使用している場合でも userinfo エンドポイント経由で取得できます。ただし、不透明トークン (Opaque token) は組織トークン (Organization token) として組織固有リソースへのアクセスには使用できません。詳細は 不透明トークン (Opaque token) と組織 (Organizations) を参照してください。
urn:logto:scope:organization_roles
| Claim name | Type | 説明 | デフォルトで ID トークンに含まれるか |
|---|---|---|---|
| organization_roles | string[] | ユーザーが所属する組織ロール(<organization_id>:<role_name> 形式) | ✅ |
API リソースと組織 (Organizations)
まず 🔐 ロールベースのアクセス制御 (RBAC) を読むことをお勧めします。これにより、Logto の RBAC の基本概念と API リソースを適切に設定する方法を理解できます。
API リソースへのアクセスリクエスト
特定の API リソースにアクセスするには、デバイス認可リクエストに resource パラメーターを含めます:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access' \
--data-urlencode 'resource=https://your-api-resource-indicator'
ユーザーが認可 (Authorization) を完了し、リフレッシュ トークン (Refresh token) を受信したら、API リソース用の JWT アクセス トークン (Access token) を取得できます:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'resource=https://your-api-resource-indicator'
レスポンスには、aud が API リソースインジケーターに設定された JWT の access_token が含まれます。
refresh_token は、初回のデバイス認可リクエストで offline_access スコープを含めた場合のみ利用可能です。Logto はトークンローテーションを行うため、常に最新の refresh_token を保存・利用してください。
組織トークン (Organization tokens) の取得
組織 (Organizations) が初めての場合は、🏢 組織 (マルチテナンシー) をご覧ください。
組織関連の情報をリクエストするには、デバイス認可リクエストに urn:logto:scope:organizations スコープを追加します:
curl --request POST 'https://your.logto.endpoint/oidc/device/auth' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'scope=openid offline_access urn:logto:scope:organizations' \
--data-urlencode 'resource=urn:logto:resource:organizations'
ユーザーがサインインしたら、リフレッシュ トークン (Refresh token) を使って組織トークン (Organization token) を取得できます:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id'
レスポンスには、指定した組織にスコープされたアクセス トークン (Access token) が含まれます。
組織 API リソース
組織内の API リソース用のアクセス トークン (Access token) を取得するには、resource と organization_id の両方のパラメーターを含めます:
curl --request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=your-application-id' \
--data-urlencode 'grant_type=refresh_token' \
--data-urlencode 'refresh_token=REFRESH_TOKEN' \
--data-urlencode 'organization_id=your-organization-id' \
--data-urlencode 'resource=https://your-api-resource-indicator'