OAuth 2.0 ID プロバイダ API

GitLabは、OAuth 2.0プロトコルでサードパーティのサービスがユーザーに代わってGitLabリソースにアクセスするためのAPIを提供しています。

GitLabをこのように設定するには、OAuth 2.0認証IDプロバイダとしてGitLabを設定するをご覧ください。

この機能は、doorkeeper Ruby gem をベースにしています。

クロスオリジンリソース共有

GitLab 15.1で導入されたCORSプリフライトリクエストサポート。

多くの/oauth エンドポイントは、クロスオリジンリソース共有をサポートしています(CORS)。GitLab 15.1からは、以下のエンドポイントもCORSプリフライトリクエストをサポートしています:

  • /oauth/revoke
  • /oauth/token
  • /oauth/userinfo

プリフライトリクエストに使用できるのは特定のヘッダーのみです:

例えば、X-Requested-With ヘッダーはプリフライトリクエストには使えません。

サポートされる OAuth 2.0 フロー

GitLabは以下の作成者フローをサポートしています:

  • Proof Key for Code Exchangeによる認証コード(PKCE): 最もセキュリティが高いです。PKCEがなければ、モバイルクライアントにクライアントシークレットを含める必要があります。
  • 作成者コード:セキュアで一般的なフロー。セキュアなサーバーサイドアプリに推奨。
  • リソースオーナーのパスワード認証情報:セキュアにホストされたファーストパーティのサービスにのみ使用します。GitLabはこのフローを使用しないことを推奨します。

Device Authorization Grant はサポートされていません。イシュー332682ではサポートの追加を提案しています。

OAuth 2.1のドラフト仕様では、暗黙的グラントとリソースオーナーパスワードクレデンシャルの両方のフローが特に省略されています。

OAuthのRFCを参照し、これらのフローがどのように動作するのかを調べ、あなたのユースケースに適したものを選んでください。

作成者コード(PKCE の有無にかかわらず)フローでは、まずユーザーアカウントの/profile/applications ページからapplication を登録する必要があります。登録の際、適切なスコープを有効にすることで、application アクセス applicationできるリソースの範囲を制限することができます。application 作成時に application認証情報をapplication 取得 applicationします:アプリケーション ID_と_クライアントシークレット_です。_クライアントシークレットは セキュアに保たれなければなりません。また、アプリケーションアーキテクチャが許す限り、_アプリケーション ID_は秘密にしておく方が有利です。

GitLab のスコープ一覧については、プロバイダのドキュメントを参照してください。

CSRF攻撃を防ぐ

リダイレクトベースのフローを保護するために、OAuth 仕様では、/oauth/authorize エンドポイントへの各リクエストで、”ユーザーエージェントにセキュアにバインドされた、state パラメータに格納された One-time use CSRF トークン” を使用することを推奨しています。これによりCSRF 攻撃を防ぐことができます。

本番環境で HTTPS を使用

本番環境では、redirect_uri に HTTPS を使用してください。開発者向けには、GitLabは安全でないHTTPリダイレクトURIを許可しています。

OAuth 2.0のセキュリティは完全にトランスポート層に基づいているので、保護されていないURIを使うべきではありません。詳細については、OAuth 2.0 RFCと OAuth 2.0 Threat Model RFCを参照してください。

以下のセクションでは、各フローで作成者を認証する方法について詳しく説明します。

コード交換のためのプルーフキー付き作成者コード(PKCE)

PKCE RFCには、作成者からアクセストークンまでの詳細なフローが記述されています。以下のステップでは、このフローの実装を説明します。

PKCEフローを用いた認証コード(Authorization code with PKCE、略してPKCE)は、_クライアントシークレットへの_アクセスを一切必要とせずに、公開クライアント上でアクセストークンとクライアント認証情報のOAuth交換をセキュアに行うことを可能にします。このため、PKCEフローはシングルページのJavaScriptアプリや、ユーザーからシークレットを守ることが技術的に不可能なその他のクライアントサイドアプリに有利です。

フローを開始する前に、STATECODE_VERIFIERCODE_CHALLENGEを生成してください。

  • STATE は、リクエストとコールバックの間で状態をメンテナーするためにクライアントが使用する、予測できない値です。また、CSRFトークンとしても使われます。
  • CODE_VERIFIER は 43 文字から 128 文字の間のランダムな文字列で、A-Z,a-z,0-9,-,.,_,~を使用します。
  • CODE_CHALLENGE のSHA256ハッシュをURLセーフなbase64エンコードした文字列です。CODE_VERIFIER
    • SHA256ハッシュはエンコードする前にバイナリ形式にしておく必要があります。
    • Rubyでは、Base64.urlsafe_encode64(Digest::SHA256.digest(CODE_VERIFIER), padding: false).
    • 参考までに、CODE_VERIFIER の文字列ks02i3jdikdo2k0dkfodf3m39rjfjsdk0wk349rj3jrhf をハッシュ化し、上のスニペットでエンコードすると、CODE_CHALLENGE の文字列2i0WFA-0AerkjQm4X4oDEhqA17QIAKNjXpagHBXmO_U が生成されます。
  1. 作成者コードを要求します。そのためには、以下のクエリパラメータでユーザーを/oauth/authorize ページにリダイレクトしてください:

    https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
    

    このページでは、REQUESTED_SCOPES で指定されたスコープに基づき、アプリからのアカウントへのアクセス要求を承認するようユーザーに求めます。その後、ユーザーは指定されたREDIRECT_URI にリダイレクトされます。scope パラメータは、ユーザーに関連付けられたスコープのスペース区切りのリストです。た と えば、scope=read_user+profileread_userprofile のスコープを要求します。リダイレクトには、例えば、作成者codeが含まれます:

    https://example.com/oauth/redirect?code=1234567890&state=STATE
    
  2. 前のリクエストから返された作成者code (次の例ではRETURNED_CODE と表記) を使って、任意の HTTP クライアントでaccess_token をリクエストできます。次の例では Ruby のrest-client を使っています:

    parameters = 'client_id=APP_ID&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER'
    RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    応答例

    {
     "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
     "token_type": "bearer",
     "expires_in": 7200,
     "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1",
     "created_at": 1607635748
    }
    
  3. 新しいaccess_token を取得するには、refresh_token パラメータを使用します。リフレッシュトークンは、access_token 自身の有効期限が切れた後でも使用できます。このリクエスト

    • 既存のaccess_tokenrefresh_tokenを無効にします。
    • レスポンスで新しいトークンを送信します。
      parameters = 'client_id=APP_ID&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI&code_verifier=CODE_VERIFIER'
      RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    応答例

    {
      "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
      "token_type": "bearer",
      "expires_in": 7200,
      "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
      "created_at": 1628711391
    }
    
note
レスポンス例:作成者リクエストで使用されたものとredirect_uri 一致する必要があります redirect_uri

これで、アクセストークンを使ってAPIにリクエストできるようになります。

作成者コードの流れ

note
詳細なフローについては、RFC仕様書を確認してください。

作成者コードのフローは、PKCEによる作成者コードのフローと基本的に同じです、

フローを開始する前に、STATE を生成します。 これは、リクエストとコールバックの間で状態をメンテナーするためにクライアントが使用する、予測できない値です。これは CSRF トークンとしても使用されます。

  1. 作成者コードを要求します。そのためには、以下のクエリパラメータでユーザーを/oauth/authorize ページにリダイレクトしてください:

    https://gitlab.example.com/oauth/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&response_type=code&state=STATE&scope=REQUESTED_SCOPES
    

    このページでは、REQUESTED_SCOPES で指定されたスコープに基づき、アプリからのアカウントへのアクセス要求を承認するようユーザーに求めます。その後、ユーザーは指定されたREDIRECT_URI にリダイレクトされます。scope パラメータは、ユーザーに関連付けられたスコープのスペース区切りのリストです。た と えば、scope=read_user+profileread_userprofile のスコープを要求します。リダイレクトには、例えば、作成者codeが含まれます:

    https://example.com/oauth/redirect?code=1234567890&state=STATE
    
  2. 前のリクエストから返された作成者code (次の例ではRETURNED_CODE として示されています) を使って、任意の HTTP クライアントでaccess_token をリクエストできます。次の例では Ruby のrest-client を使っています:

    parameters = 'client_id=APP_ID&client_secret=APP_SECRET&code=RETURNED_CODE&grant_type=authorization_code&redirect_uri=REDIRECT_URI'
    RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    応答例

    {
     "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54",
     "token_type": "bearer",
     "expires_in": 7200,
     "refresh_token": "8257e65c97202ed1726cf9571600918f3bffb2544b26e00a61df9897668c33a1",
     "created_at": 1607635748
    }
    
  3. 新しいaccess_token を取得するには、refresh_token パラメータを使用します。リフレッシュトークンは、access_token 自身の有効期限が切れた後でも使用できます。このリクエスト

    • 既存のaccess_tokenrefresh_tokenを無効にします。
    • レスポンスで新しいトークンを送信します。
      parameters = 'client_id=APP_ID&client_secret=APP_SECRET&refresh_token=REFRESH_TOKEN&grant_type=refresh_token&redirect_uri=REDIRECT_URI'
      RestClient.post 'https://gitlab.example.com/oauth/token', parameters
    

    応答例

    {
      "access_token": "c97d1fe52119f38c7f67f0a14db68d60caa35ddc86fd12401718b649dcfa9c68",
      "token_type": "bearer",
      "expires_in": 7200,
      "refresh_token": "803c1fd487fec35562c205dac93e9d8e08f9d3652a24079d704df3039df1158f",
      "created_at": 1628711391
    }
    
note
レスポンス例:作成者リクエストで使用されたものとredirect_uri 一致する必要があります redirect_uri

これで、返されたアクセストークンを使ってAPIにリクエストできるようになります。

リソースオーナーのパスワード認証フロー

note
詳細なフローについては、RFC仕様書を確認してください。
note
リソースオーナーパスワード認証は、二要素認証が有効になっているユーザーには無効です。これらのユーザーは、代わりに個人アクセストークンを使用して API にアクセスできます。

このフローでは、リソースオーナーの認証情報 (ユーザー名とパスワード) と引き換えにトークンが要求されます。

このクレデンシャルは、以下の場合にのみ使用します:

  • リソースオーナーとクライアントの間に高度な信頼関係がある場合。例えば、クライアントがデバイスオペレーティングシステムの一部であったり、高度な特権を持つアプリケーションである場合。
  • 他の権限付与タイプは使用できません(権限コードなど)。
caution
ユーザーの認証情報を決して保存せず、クライアントが信頼できる環境にデプロイされる場合にのみこのグラントタイプを使用してください。

このグラントタイプでは、クライアントがリソースオーナーのクレデンシャルに直接アクセスする必要がありますが、 リソースオーナーのクレデンシャルは 1 回のリクエストで使用され、アクセストークンと交換されます。このグラントタイプでは、クライアントがリソースオーナー・クレデンシャルを保存しておく必要がありません。

アクセストークンをリクエストするには、/oauth/token に以下のパラメータを指定して POST リクエストを行う必要があります:

{
  "grant_type"    : "password",
  "username"      : "user@example.com",
  "password"      : "secret"
}

cURLリクエストの例:

echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --request POST "https://gitlab.example.com/oauth/token"

このグラントフローは、登録済みの OAuth アプリケーションで使用することもできます。この場合、アプリケーションのclient_idclient_secret で HTTP ベーシック認証を使用します:

echo 'grant_type=password&username=<your_username>&password=<your_password>' > auth.txt
curl --data "@auth.txt" --user client_id:client_secret \
     --request POST "https://gitlab.example.com/oauth/token"

その後、アクセストークンを含むレスポンスを受け取ります:

{
  "access_token": "1f0af717251950dbd4d73154fdf0a474a5c5119adad999683f5b450c460726aa",
  "token_type": "bearer",
  "expires_in": 7200
}

デフォルトでは、アクセストークンのスコープはapi で、完全な読み書きアクセスが可能です。

テストには、oauth2 Ruby gemを使うことができます:

client = OAuth2::Client.new('the_client_id', 'the_client_secret', :site => "https://example.com")
access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token

を使って GitLab API にアクセスします。access token

access token を使うと、ユーザーの代わりに API へのリクエストを行うことができます。トークンは GET パラメータとして渡すことができます:

GET https://gitlab.example.com/api/v4/user?access_token=OAUTH-TOKEN

として渡すか、Authorizationヘッダーにトークンを記述します:

curl --header "Authorization: Bearer OAUTH-TOKEN" "https://gitlab.example.com/api/v4/user"

を使って HTTPS で Git にアクセスします。access token

スコープが read_repository あるいはwrite_repository のアクセストークンは、HTTPS で Git にアクセスすることができます。トークンをパスワードとして使ってください。ユーザー名はoauth2でなければなりません:

https://oauth2:<your_access_token>@gitlab.example.com/project_path/project_name.git

別の方法として、Git credential helperを使ってOAuthでGitLabを認証することもできます。これは OAuth トークンの更新を自動的に処理します。

トークン情報の取得

トークンの詳細を確認するには、Doorkeeper gem が提供するtoken/info エンドポイントを使用します。詳細については、/oauth/token/infoを参照してください。

アクセストークンを提供する必要があります:

  • パラメータとして

     GET https://gitlab.example.com/oauth/token/info?access_token=<OAUTH-TOKEN>
    
  • 作成者ヘッダ:

     curl --header "Authorization: Bearer <OAUTH-TOKEN>" "https://gitlab.example.com/oauth/token/info"
    

以下はレスポンスの例です:

{
    "resource_owner_id": 1,
    "scope": ["api"],
    "expires_in": null,
    "application": {"uid": "1cb242f495280beb4291e64bee2a17f330902e499882fe8e1e2aa875519cab33"},
    "created_at": 1575890427
}

非推奨フィールド

scopesexpires_in_seconds フィールドはレスポンスに含まれます。

これらのフィールドはそれぞれscopeexpires_in のエイリアスであり、doorkeeper 5.0.2で導入された変更を破壊しないために含まれています。

これらのフィールドは後のリリースで削除される予定なので、当てにしないでください。

トークンの取り消し

トークンを失効させるには、revoke エンドポイントを使用します。APIは200レスポンスコードと空のJSONハッシュを返し、成功を示します。

parameters = 'client_id=APP_ID&client_secret=APP_SECRET&token=TOKEN'
RestClient.post 'https://gitlab.example.com/oauth/revoke', parameters

OAuth 2.0 トークンと GitLab レジストリ

標準的な OAuth 2.0 トークンは、GitLab レジストリへのさまざまな程度のアクセスをサポートしています: