JetBrains Space ヘルプ

認証コードフロー

基本

  • Space ユーザーに代わって承認を行います。

  • サーバー側でクライアントが実行される Web アプリケーション、または完全にクライアントブラウザー (静的 Web ページ) で実行される Web アプリケーションに適しています。

  • リソース所有者は、リソース所有者が使用するデバイス上のユーザーエージェントでレンダリングされる HTML ユーザーインターフェースを介してアプリケーションにアクセスします。

  • アプリケーション資格情報とアプリケーションに発行されたアクセストークンは Web サーバー (静的リソースの場合は Web ブラウザー) に保存され、リソース所有者に公開されたり、リソース所有者がアクセスしたりすることはありません。

  • アプリケーションは、必要なリソースの範囲も含まれるリンクを介してユーザーを Space に送信します。ユーザーが Space にログインすると、Space は指定されたリダイレクト URI を使用してユーザーをアプリケーションにリダイレクトします。リダイレクトには認証コードも含まれています。アプリケーションは認証コードを使用して、Space からアクセストークンを取得します

  • Space では、認可コードフローによって取得されたトークンは、限られた期間のみ有効です。トークンの有効期限が切れたら、アプリケーションはリフレッシュトークンフローを使用してトークンをリフレッシュする必要があります。

  • アプリケーションで Space SDK を使用する場合は、Space クラスのヘルパーメソッドを使用してフローを実装できます。詳細

  • フローの詳細については、「認証コードフロー」を参照してください。以下を参照してください。

PKCE

Space の認証コードフローは、PKCE 拡張機能をサポートしています。

コード交換用証明キー (PKCE) は、認証フローをさらに強化する追加の保護コードです。これは、デスクトップおよびモバイルクライアントアプリケーションにより安全で堅牢なサインインエクスペリエンスを提供するために作成され、OAuth 2.0 のベストプラクティスの中でも IETF (インターネットエンジニアリングタスク強制) によって推奨されています。

PKCE はどのように機能しますか:

  1. クライアント (アプリケーション) は、「コード検証」と呼ばれるランダムな文字列を作成します。

  2. クライアントは、ハッシュアルゴリズムを使用してコード検証子のハッシュである「コードチャレンジ」を作成します。

  3. クライアントは、認可リクエストでコードチャレンジを Space に送信します。

  4. Space はコードチャレンジを保存し、それをこのリクエストに対して生成する認証コードに関連付けます。

  5. ユーザーが認可リクエストを承認すると、クライアントは認可コードを受け取ります。次に、クライアントは Space にアクセストークンを要求します。クライアントは、認証コードとともに、元のコード検証ツールも送信します。

  6. Space は、受信したコード検証器からコードチャレンジを再度生成します。次に、この新しく生成されたコードチャレンジを以前に保存したコードチャレンジと比較します。それらが一致すると、Space はクライアントにアクセストークンを発行します。

詳細 (英語)

実装方法

以下の例は、PKCE を使用して認証コードフローを実装する方法を示しています。これを機能させるには、認証タブのアプリケーション設定で認証コードフローPKCE が必要を有効にする必要があります。

クライアント IDクライアントシークレットをアプリケーションに提供する必要もあります。アプリケーションがシークレットを提供せずに認証コードフローを開始できるようにする場合 (安全性は低くなります)、パブリック (信頼できない) クライアントを許可するを有効にします。

package com.example import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.html.* import io.ktor.server.netty.* import io.ktor.server.response.* import io.ktor.server.routing.* import kotlinx.html.* import space.jetbrains.api.runtime.* import space.jetbrains.api.runtime.resources.teamDirectory import space.jetbrains.api.runtime.types.GlobalPermissionContextIdentifier import space.jetbrains.api.runtime.types.PermissionIdentifier import space.jetbrains.api.runtime.types.ProfileIdentifier import java.util.* import java.util.concurrent.ConcurrentHashMap // store these in your database, securely and persistently. val codeVerifiersByAuthProcessId: MutableMap<UUID, String> = ConcurrentHashMap() // base Ktor client val ktorClient = ktorClientForSpace() const val spaceUrl = "https://mycompany.jetbrains.space" const val appUrl = "https://myapp.url" const val showUsernameRoute = "/show-username" const val authorizeInSpaceRoute = "/authorize-in-space" const val redirectUri = "$appUrl$showUsernameRoute" // class that describes your app val spaceAppInstance = SpaceAppInstance( // 'clientId' and 'clientSecret' are generated when you // [[[register the application|https://www.jetbrains.com/help/space/single-org-applications.html#specify-authentication-options]]] in Space // Do not store id and secret in plain text clientId = System.getenv("JB_SPACE_CLIENT_ID"), clientSecret = System.getenv("JB_SPACE_CLIENT_SECRET"), spaceServerUrl = spaceUrl, ) fun Application.module() { routing { // Index page get("/") { call.respondHtml { body { button { onClick = "window.location.href = '$authorizeInSpaceRoute'" +"Authorize in Space and show your username" } } } } // When the user presses the button, they are redirected to this page. // There, an ID of the auth process is generated, and a code verifier for this auth process is stored. // Then the user is redirected to Space for the authentication and authorization. get(authorizeInSpaceRoute) { val authProcessId = UUID.randomUUID() val codeVerifier = Space.generateCodeVerifier() codeVerifiersByAuthProcessId[authProcessId] = codeVerifier call.respondRedirect( Space.authCodeSpaceUrl( appInstance = spaceAppInstance, // Specify the scope of resources that the application needs to access. // In our case, we need to access user profiles. // [[[Learn more about permissions|https://www.jetbrains.com/help/space/request-permissions.html]]] scope = PermissionScope.build( PermissionScopeElement( context = GlobalPermissionContextIdentifier, permission = PermissionIdentifier.ViewMemberProfiles ) ), redirectUri = redirectUri, /** [OAuthAccessType.OFFLINE] for offline access on behalf of user */ accessType = OAuthAccessType.ONLINE, state = authProcessId.toString(), codeVerifier = codeVerifier, ) ) } // After the user is authenticated in Space, they are redirected back to our app at this page. // With this redirect, we receive the auth code, which we can exchange for an auth token. // We retrieve the stored code verifier and send it to Space, so that we can be sure that the user is the same // person that started the auth process. get(showUsernameRoute) { suspend fun respondAuthError() { call.respondHtml(HttpStatusCode.Unauthorized) { head { title("Authentication error") } body { p { +"Authentication error." } button { onClick = "window.location.href = '$authorizeInSpaceRoute'" +"Try again" } } } } val authProcessIdString = call.parameters["state"] ?: run { respondAuthError() return@get } val authProcessId = try { UUID.fromString(authProcessIdString) } catch (_: IllegalArgumentException) { respondAuthError() return@get } val authCode = call.parameters["code"] ?: run { respondAuthError() return@get } val spaceAccessTokenInfo = try { Space.exchangeAuthCodeForToken( ktorClient = ktorClient, appInstance = spaceAppInstance, authCode = authCode, redirectUri = redirectUri, codeVerifier = codeVerifiersByAuthProcessId[authProcessId] ?: run { respondAuthError() return@get }, ) } catch (_: PermissionDeniedException) { respondAuthError() return@get } /** If you requested [OAuthAccessType.OFFLINE] access, you can use [SpaceTokenInfo.refreshToken] * with [SpaceAuth.RefreshToken]. */ val spaceClient = SpaceClient(ktorClient, spaceAppInstance, SpaceAuth.Token(spaceAccessTokenInfo.accessToken)) val profile = spaceClient.teamDirectory.profiles.getProfile(ProfileIdentifier.Me) val username = profile.username val msg = "Hello ${username}!" call.respondHtml { head { title(msg) } body { p { +msg } button { onClick = "window.location.href = '/'" +"To home page" } } } } } } fun main() { embeddedServer(Netty, port = 8080, module = Application::module).start(wait = true) }

最初のリクエスト

認証プロセスを開始するには、アプリケーションは次の形式でユーザーのブラウザーを認証エンドポイント <Space service URL>/oauth/auth にリダイレクトする必要があります。

${Space Service URL}/oauth/auth?response_type=code&state=${State}&redirect_uri=${Client redirect URI}&request_credentials=${Request credentials mode}&client_id=${Client ID}&scope=${Scope}&access_type={online|offline}&code_challenge={code_challenge}&code_challenge_method={code_challenge_method}

このリクエストでは:

  • PKCE 拡張機能を使用する場合は、code_challenge が必要です。

  • code_challenge_method はオプションです。リクエストに存在しない場合は、デフォルトで「プレーン」になります。

例:

https://mycompany.jetbrains.space/oauth/auth?response_type=code&state=9b8fdea0-fc3a-410c-9577-5dee1ae028da&redirect_uri=https%3A%2F%2Fmyservice.company.com%2Fauthorized&request_credentials=skip&client_id=98071167-004c-4ddf-ba37-5d4599fdf319&scope=0-0-0-0-0%2098071167-004c-4ddf-ba37-5d4599fdf319&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256

リクエストに access_type=offline パラメーターを追加すると、認可コードはアクセストークンだけでなくリフレッシュトークンも返します。リフレッシュトークンを使用すると、元のアクセストークンの有効期限が切れた場合に、アプリケーションはユーザーに同意を再度求めることなく、新しいアクセストークンを取得できます。

https://mycompany.jetbrains.space/oauth/auth?response_type=code&state=${State}&redirect_uri=${Client redirect URI}&request_credentials=${Request credentials mode}&client_id=${Client ID}&scope=${Scope}&access_type=offline&code_challenge={code_challenge}&code_challenge_method={code_challenge_method}

Space からトークンを取得するには、アプリケーションは認可リクエストのいくつかのパラメーターの値を提供する必要があります。パラメーターの説明を参照してください。

ハンドル応答 (認証コード)

リソース所有者がアクセスを許可すると、Space は認証コードを発行し、application/x-www-form-urlencoded 形式を使用してリダイレクト URI のクエリコンポーネントに次のパラメーターを追加することによって、それをアプリケーションに配信します。

パラメーター

説明

コード

Space によって生成された認証コード。認証コードは、漏洩のリスクを軽減するために、発行後すぐに期限切れになります。アプリケーションは認証コードを複数回使用してはなりません。認証コードが複数回使用された場合、Space はリクエストを拒否します。認可コードはアプリケーション識別子とリダイレクト URI にバインドされます。

状態

Space は、この値をパラメーターとして redirectUri に追加します。アプリケーションはこの値を使用して認証プロセスを識別し、続行する必要があります。

サンプル: Space は、次の HTTP 応答を送信してブラウザーをリダイレクトします。

HTTP/1.1 302 Found Location: https://myservice.company.com/authorized?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz

アプリケーションは、認識できない応答パラメーターを無視する必要があります。

ハンドルエラー応答

リソース所有者がアクセス要求を拒否した場合、またはリダイレクト URI の欠落または無効以外の理由で要求が失敗した場合、Space は application/x-www-form-urlencoded 形式を使用してリダイレクト URI のクエリコンポーネントに次のパラメーターを追加することでアプリケーションに通知します。

エラー

以下からの単一の ASCII [USASCII] エラーコード:

  • invalid_request — リクエストに必要なパラメーターがない、無効なパラメーター値が含まれている、パラメーターが複数回含まれている、その他の形式が正しくない。

  • unauthorized_client — アプリケーションには、このメソッドを使用して認証コードを要求する権限がありません。

  • access_denied — リソース所有者または Space がリクエストを拒否しました。

  • unsupported_response_type — Space は、この方法を使用した認証コードの取得をサポートしていません。

  • invalid_scope — 要求されたスコープは無効、不明、不正な形式です。

error_description

追加情報を提供する人間が読める ASCII [USASCII] テキスト。アプリケーション開発者が何が課題になったのかを理解できます。

トークンの認証コードを交換する

アプリケーションはコードを受信すると、それをアクセストークンと交換できます。最初のリクエストで access_type=offline の場合、Space はリフレッシュトークンも返します。

アプリケーションは、HTTP リクエストエンティティ本体で文字エンコードが UTF-8 の application/x-www-form-urlencoded 形式を使用して次のパラメーターを送信することにより、Space トークンエンドポイントにリクエストを作成します。

POST /oauth/token Host: ${Space Service URL} Accept: application/json Authorization: Basic ${base64(${Client ID} + ":" + ${Client secret})} Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=${Code received on a previous step}&redirect_uri=${Client redirect URI}&code_verifier={code_verifier}

このリクエストでは:

  • PKCE 拡張機能を使用する場合は、code_verifier が必要です。

  • client_id は、パブリッククライアント (信頼できないサービス) の PKCE 拡張機能を使用する場合、および認証ヘッダーが省略される場合に必要です。

例:

POST /oauth/token Host: Space.company.com Accept: application/json Authorization: Basic OTgwNzExNjctMDA0Yy00ZGRmLWJhMzctNWQ0NTk5ZmRmMzE5OmVBVXlLZ1ZmaFNiVg0K Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fmyservice.company.com%2Fauthorized&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

パラメーターの説明を参照してください。

ハンドルトークン応答

トークン要求が有効で承認されている場合、Space はアクセストークンを発行し、要求された場合はリフレッシュトークンを発行します。リクエストが失敗した場合、または無効な場合、Space サーバーはエラー応答を返します。

成功した応答の例:

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 { "access_token":"1443459450185.0-0-0-0-0.98071167-004c-4ddf-ba37-5d4599fdf319.0-0-0-0-0%3B1.MCwCFC%2FYWvLjHdzOdpLleDLITJn4Mz9rAhRklCoZ2dlMkh2aCd1K5QQ89ibsxg%3D%3D", "expires_in":600, "refresh_token": "mF36POk6yJV_adQssw5c_RJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" }

認可コードフローパラメーター

response_type

OAuth 2.0 リクエストの許可タイプを指定します。code に値を設定します

状態

現在のアプリケーション状態の識別子。例: アプリケーション内の現在のユーザーの位置に関する情報を含むローカルストレージオブジェクトのキーにすることができます。

redirect_uri

Space は、承認後にユーザーをこの URI にリダイレクトします。アプリケーションは、この URI へのリクエストを処理し、リクエストパラメーターから認可コードを取得する必要があります。

request_credentials

すでに同意されている場合でも、毎回ユーザーをログインページにリダイレクトするかどうかを指定します。次の値が有効です。

  • required — ユーザーを Space からログアウトし、ログインページにリダイレクトします。アプリケーションのログアウト要求に対する応答として使用します。

  • default — アプリケーションが匿名アクセスを許可しない場合に使用します。

    • ユーザーがすでに Space にログインしている場合、ユーザーにはアプリケーションへのアクセスが許可されます。

    • ユーザーが Space にログインしていない場合、ユーザーはログインページにリダイレクトされます。

client_id

Space にアプリケーションを登録するときにアプリケーションに割り当てられる ID。クライアント ID を取得するには、administration.png 管理→ アプリケーションに移動し、リストからアプリケーションを選択します。

client_secret

アプリケーションを Space に登録するときにアプリケーションに割り当てられるプライベート ID。クライアントシークレットを取得するには、administration.png 管理→ アプリケーションに移動し、リストからアプリケーションを選択します。

範囲

Space 内の特定のリソースにアクセスするために必要な権限の Space 区切りのリスト。

権限スコープの形式の詳細については、こちらを参照します

access_type

ユーザーがオンラインでないときにアプリケーションが Space にアクセスする必要があるかどうかを示します。許可される値: online (デフォルトで使用) および offline ユーザーがオンラインでないときにアプリケーションでアクセストークンのリフレッシュが必要な場合は、offline 値を使用します。この場合、Space は初めてユーザーの認証コードを交換するときに、アプリケーションのリフレッシュトークンを発行します。詳細については、「リフレッシュトークン」ページを参照してください。

code_verifier

最小長 43 文字、最大長 128 文字の、予約されていない文字 [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~" を使用する高エントロピー暗号化ランダム文字列。

クライアントは、OAuth 2.0 認証リクエストごとにコードベリファイアを作成します。

適切な乱数ジェネレーターの出力を使用して、32 オクテットのシーケンスを作成することをお勧めします。次に、オクテットシーケンスが base64url でエンコードされ、コードベリファイアとして使用する 43 オクテットの URL セーフ文字列が生成されます。詳細については、RFC7636 のセクション 4.1(英語) を参照してください。

code_challenge

コードチャレンジは、plain または S256 変換を使用して、クライアントによってコードベリファイアから導出されます。

  • plain :

    code_challenge = code_verifier
  • S256 :

    code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

詳細は、RFC7636 のセクション 4.2(英語) を参照してください。

code_challenge_method

このパラメーターは、code_challenge の作成に使用される変換の型(plain または S256)を定義します。

クライアントが S256 を使用できる場合は、S256 を使用する必要があります。クライアントは、技術的な理由で S256 をサポートできない場合にのみ、plain 変換を使用できます。

詳細は、RFC7636 のセクション 4.2(英語) を参照してください。

アクセストークンをリフレッシュする

アプリケーションは、UTF-8 文字エンコーディングを使用した application/x-www-form-urlencoded 形式で HTTP 要求エンティティ本体に次のパラメーターを追加することにより、トークンエンドポイント <Space service URL>/oauth/token にリフレッシュ要求を作成します。

grant_type

OAuth 2.0 リクエストの許可タイプを指定します。必須。値を refresh_token に設定します。

refresh_token

必須。アプリケーションに発行されたリフレッシュトークン。

範囲

必須。Space 内の特定のリソースにアクセスするために必要な権限の Space 区切りのリスト。

権限スコープの形式の詳細については、こちらを参照します

リフレッシュトークンは通常、追加のアクセストークンを要求するために使用される長期間持続する資格情報であるため、リフレッシュトークンは発行先のアプリケーションにバインドされます。アプリケーションの種類が機密である場合、またはアプリケーションにアプリケーション資格情報が発行されている (または他の認証要件が割り当てられている) 場合、アプリケーションは Space サーバーで RFC6749 で説明されています(英語)として認証する必要があります。

アプリケーションは、トランスポート層セキュリティを使用して、次の形式で HTTP リクエストを作成します。

POST /oauth/token Host: ${Space Service URL} Authorization: Basic ${base64(${Client ID} + ":" + ${Client secret})} Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=${Refresh token received from Space}

例 (表示のみを目的として余分な改行が含まれています):

POST /oauth/token Host: mycompany.jetbrains.space Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

Space は次のことを行います。

  1. 機密アプリケーション、またはアプリケーション資格情報が発行されたアプリケーションに対してアプリケーション認証を要求する (または他の認証要件)

  2. アプリケーション認証が含まれている場合はアプリケーションを認証し、認証されたアプリケーションにリフレッシュトークンが発行されたことを確認します。

  3. リフレッシュトークンを検証します。

リフレッシュトークンが有効で承認されている場合、Space はアクセストークンを発行します。

新しいアクセストークンとともに、Space は新しいリフレッシュトークンを発行できます。その場合、アプリケーションは古いリフレッシュトークンを破棄し、新しいリフレッシュトークンに置き換える必要があります。Space は、アプリケーションに新しいリフレッシュトークンを発行した後、古いリフレッシュトークンを取り消すことができます。新しいリフレッシュトークンが発行される場合、リフレッシュトークンのスコープは、アプリケーションによってリクエストに含まれるリフレッシュトークンのスコープと同一である必要があります。

リフレッシュトークンフローエラー

リクエストが検証に失敗した場合、または無効な場合、Space はエラー応答を返します。

エラー

以下からの単一の ASCII [USASCII] エラーコード:

  • invalid_request — リクエストに必須のパラメーターが欠落している、サポートされていないパラメーター値(許可型以外)が含まれている、パラメーターが繰り返されている、複数の資格情報が含まれている、アプリケーションを認証するために複数のメカニズムを利用している、またはその他の形式が不正です。

  • invalid_client — アプリケーション認証に失敗しました (例: 不明なアプリケーション、アプリケーション認証が含まれていない、サポートされていない認証方法)。Space は、サポートされている HTTP 認証スキームを示すために、HTTP 401 (Unauthorized) ステータスコードを返すことができます。アプリケーションが「Authorization」要求ヘッダーフィールドを介して認証を試行した場合、Space サーバーは HTTP 401 (Unauthorized) ステータスコードで応答し、アプリケーションが使用する認証スキームに一致する「WWW-Authenticate」応答ヘッダーフィールドを含めます。

  • invalid_grant — 提供された認可付与 (認可コード、リソース所有者の資格情報など) またはリフレッシュトークンが無効であるか、期限切れであるか、取り消されているか、認可リクエストで使用されたリダイレクト URI と一致しないか、別のアプリケーションに発行されました。

  • unauthorized_client — 認証されたアプリケーションには、この認可付与タイプを使用する権限がありません。

  • unsupported_grant_type — 認可付与タイプは、Space ではサポートされていません。

  • invalid_scope — 要求されたスコープが無効、不明、不正な形式であるか、リソース所有者によって付与されたスコープを超えています。

error_description

追加情報を提供する人間が判読可能な ASCII [USASCII] テキスト。アプリケーション開発者が発生したエラーを理解できます。

error_uri

エラーに関する情報を含む人間が判読できる Web ページを識別する URI。アプリケーション開発者にエラーに関する追加情報を提供するために使用されます。

パラメーターは、「application/json」メディア型を使用して HTTP レスポンスのエンティティ本体に含まれます。パラメーターは、最上位の構造レベルで各パラメーターを追加することにより、JSON 構造にシリアル化されます。パラメーター名と文字列値は JSON 文字列として含まれています。数値は JSON 番号として含まれています。パラメーターの順序は重要ではなく、変化する可能性があります。

例:

HTTP/1.1 400 Bad Request Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "error":"invalid_request" }

関連ページ:

複数組織アプリケーションの配布

マルチ組織アプリケーションは、複数の Space 組織にインストールできるアプリケーションです。組織ユーザーは、直接リンクをクリックするか、JetBrains マーケットプレイスを使用してアプリケーションをインストールできます。アプリケーションを複数の組織で使用できるようにするには、Space API 呼び出しを介して特定の組織内でアプリケーション自体を構成できる必要があります。アプリケーション構成の詳細については、こちらを参照します。複数組織アプリケーションの詳細:クライアントクレデンシャル...

権限のリクエスト

特定の Space エンドポイントにアクセスするには、アプリケーションはまず対応するアクセス許可を取得する必要があります。例: アプリケーションがプロジェクトの課題を作成する場合、アプリケーションには課題の作成権限が必要です。課題の詳細を表示するには、課題を表示する権限が必要です。アプリケーションに必要な権限のセット全体は、権限スコープと呼ばれます。アプリケーションがアクセス許可を要求する方法は、アプリケーション自体の代わりに動作するアプリケーションと、Space ユーザーの代わりに動作するアプ...

永久トークン

永久トークンを使用すると、OAuth 2.0 認証フローを実装しなくても、Space でアプリケーションを認証および認可できます。特定の権限スコープを持つ新しいトークンを作成し、それをどこからでも認証に使用するだけです。永久トークンを要求ヘッダーのパラメーターとして使用します。永久トークンは、OAuth 2.0 認証で使用される一時アクセストークンよりも本質的に安全性が低いことに注意してください。アプリケーションの認可には 2 種類の永久トークンを使用できます。アプリケーション永久トークン: ア...