JetBrains Space ヘルプ

Space からのリクエストを検証する

アプリケーションが Space からリクエストを受信することになっている場合 (たとえば、エンドポイントでリクエストをリッスンするチャットボット)、受信したリクエストが本物かどうかを検証できる必要があります。Space では、さまざまな検証方法が提供されます。

(推奨) 公開鍵

この検証方法は非対称暗号化に基づいています。最も安全なものとして使用することをお勧めします。

Space SDK は、この検証タイプをそのままサポートしています。Space クライアントは、署名検証プロセス全体を処理する verifyWithPublicKey() メソッドを提供します。署名を検証するには、このメソッドにはリクエスト本文と X-Space-Public-Key-Signature および X-Space-Timestamp HTTP ヘッダーの内容が必要です。

例: このメソッドを使用してアプリケーション内のリクエストを検証する方法は次のとおりです。

import io.ktor.client.* import space.jetbrains.api.runtime.SpaceHttpClient import space.jetbrains.api.runtime.helpers.verifyWithPublicKey import space.jetbrains.api.runtime.withServiceAccountTokenSource import io.ktor.application.* import io.ktor.http.* import io.ktor.request.* import io.ktor.response.* import io.ktor.routing.* // other imports private val url = "https://mycompany.jetbrains.space/" private val clientId = "client-id-assigned-to-app-during-registration" private val clientSecret = "client-secret-issued-to-app-during-registration" val spaceClient by lazy { SpaceHttpClient(HttpClient(CIO)) .withServiceAccountTokenSource( clientId = clientId, clientSecret = clientSecret, serverUrl = url ) } suspend fun verifyRequestWithPublicKey(body: String, signature: String, timestamp: String): Boolean { // verify the request using a public key return spaceClient.verifyWithPublicKey(body, timestamp.toLong(), signature) } fun Routing.backToSpace() { post("/api/myapp") { // get request body val body = call.receiveText() // get hash calculated by Space val signature = call.request.header("X-Space-Public-Key-Signature") val timestamp = call.request.header("X-Space-Timestamp") val verified = signature != null && timestamp != null && verifyRequestWithPublicKey(body, signature, timestamp) if (!verified) { call.respond(HttpStatusCode.Unauthorized) return@post } // here goes your code } }

サンプルコードへ移動

一般的なワークフローは次のようになります。

  1. アプリケーションにリクエストを送信する前に、Space はリクエストハッシュを計算します。

  2. Space はリクエスト署名を生成します。計算されたハッシュを取得し、秘密鍵で暗号化します。署名は、X-Space-Public-Key-Signature HTTP ヘッダー内の要求とともに送信されます。例: これはヘッダーを含む Space リクエストのサンプルです。

    POST /api/myapp HTTP/1.1 X-Forwarded-For : ::ffff:123.123.123.123 X-Forwarded-Proto : https X-PageKite-Port : 443 X-Space-Timestamp : 1632844347462 X-Space-Public-Key-Signature : iOjFEi5EpW+FbU1CJl+oc0QOJbIrv7kaV/VTEMa5i0ot6418N3ObQGz2C0tB8e2N4vHKQ7LWMZZ+OJexeHHoJGvL0XN7nwWb5k7Hn1DOPMMkWgSi6kL5orkyOHqIDPaHgZzX6IGbl3LP/aPms7E1NXaFRIfxuUQGdsDeA7yCjTIv/Rq8AbV9VqVI5TudAhHmaxh6R19wgtzWxaud27rsXV8INAvwXAQLMf5Ld+E1Mzi67qrS79Jxa7U34SJjC9xDe1wYMCZGc+G+6L4zdqVB08C2MCfI1IVYzqiHLyJgCHxhBmvjJ8JivoqadbRKOrbKShvSzHqFD5geEBovO0HqKA== User-Agent : Space (81383) Ktor http-client Accept-Charset : UTF-8 Accept : */* Content-Length : 196 Content-Type : application/json Host: 12345abcdef.ngrok.io Connection : Keep-Alive {"className":"ListCommandsPayload","clientId":"f6df3d26-d9fc-41c5-9fbd-0e7896f2cfb0","userId":"2BgVYn24Jx6u"}
  3. アプリケーションはリクエストを受け取ります。ここで、リクエストの署名を検証する必要があります。

    まず、アプリケーションは Space から公開鍵を取得する必要があります。これを行うには、アプリケーションは HTTP リクエストを applications/public-keys Space エンドポイントに送信する必要があります。たとえば、リクエストは次のようになります。

    GET https://mycompany.jetbrains.space/api/http/applications/clientId:abc1234/public-keys Authorization: Bearer abc1234 Accept: application/json

    ここで、clientId は、登録時にアプリケーションに割り当てられたクライアント ID です。API Playground はリクエストの生成に役立ちます。

    Public keys HTTP API
  4. 返信で、Space は JSON Web キーセット(英語)を送信します。通常、セットにはキーが 1 つだけ含まれます。現在の公開鍵が古くなると、セットに 2 つの鍵 (現在の鍵と新しい鍵) が含まれる期間が発生します。

    セット内のキーごとに、アプリケーションは次のことを行う必要があります。

    1. X-Space-Timestamp ヘッダーとリクエストボディの値を取得します。

    2. タイムスタンプと本文で構成される文字列を生成します。コロン : を区切り文字として使用します。たとえば、Kotlin では次のようになります。

      val str = "$timestamp:$body"

      上記のサンプルリクエストの場合、str 値は次のようになります。

      1632844347462:{"className":"ListCommandsPayload", "clientId":"f6df3d26-d9fc-41c5-9fbd-0e7896f2cfb0","userId":"2BgVYn24Jx6u"}
    3. SHA512 を使用して文字列ハッシュを計算します。

    4. RSA アルゴリズムを使用して、X-Space-Public-Key-Signature ヘッダーの内容を復号化します。

    5. 復号化された署名と計算されたハッシュを比較します。それらが等しい場合、アプリケーションはリクエストの処理を許可されます。そうでない場合、アプリケーションは HTTP 401 Unauthorized 応答コードを返す必要があります。

      セットから 1 つのキーだけが正しいハッシュ結果を返す必要があることに注意してください。

重要: 一定の期間が経過すると、公開鍵は古くなります。アプリケーションは公開鍵をキャッシュできますが (毎回 Space に要求することを避けるため)、キャッシュされたキーが正しい署名を生成しなくなったら、キャッシュをクリアする必要があります。Space SDK は、このようなキャッシュを内部で実行します。

サンプルコード

次の Kotlin コードスニペットは、X-Space-Public-Key-Signature ヘッダーの署名を確認する方法を示しています。

val publicKeySignature = call.request.header("X-Space-Public-Key-Signature") val timestamp = call.request.header("X-Space-TimeStamp") ?.toLong() ?: error("Header X-Space-TimeStamp is not provided") val publicKeyString = appClient.api.applications().getPublicKeys(ApplicationIdentifier.Me) val publicKey = JWKSet.parse(publicKeyString).keys .map { it as RSAKey } .map { it.toRSAPublicKey() } .first() val rsaSignature = Signature.getInstance("SHA512withRSA") rsaSignature.initVerify(publicKey) rsaSignature.update("$timestamp:$body".toByteArray()) val signatureVerified = rsaSignature.verify(Base64.decodeBytes(publicKeySignature)) if (!signatureVerified) { // respond with 401 }

署名キー

このメソッドの考え方は、Space が特別な署名キーを使用して、アプリケーションに送信するすべてのリクエストのハッシュを生成するというものです。計算されたハッシュは、X-Space-Signature HTTP ヘッダーで送信されます。次に、アプリケーションは同じ署名キーを使用して、受信したリクエストのハッシュを計算する必要があります。次に、計算されたハッシュ値とリクエスト内のハッシュ値を比較する必要があります。

Space SDK にはこのメソッド用のヘルパー関数がないため、私たちのタスクは Space からのリクエストを検証するで説明されている検証ロジックを実装することです。ハッシュを計算するには、Apache Commons Codec ライブラリを使用できます。Gradle プロジェクトから参照するには、次の行を build.gradle に追加します。

  • repositories へ:

    repositories { jcenter() // ... other repos }
  • dependencies へ:

    dependencies { compile group: 'commons-codec', name: 'commons-codec', version: '1.15' // ... other dependencies }

このメソッドの単純な実装は次のようになります。

import org.apache.commons.codec.digest.HmacAlgorithms import org.apache.commons.codec.digest.HmacUtils // ... other imports // signing key issued during app registration val signingKey = "abc123" // calculate hash and compare it to hash from request fun verifyPayloadWithSigningKey(body: String, signature: String, timestamp: String) : Boolean { val checkedSignature = HmacUtils(HmacAlgorithms.HMAC_SHA_256, signingKey). hmacHex("$timestamp:$body") return signature == checkedSignature } fun Routing.backToSpace() { // the endpoint that handles Space requests post("api/myapp") { // get payload as text val body = call.receiveText() // get timestamp and message hash calculated by Space val signature = call.request.header("X-Space-Signature") val timestamp = call.request.header("X-Space-Timestamp") // check hash val verified = signature != null && timestamp != null && verifyPayloadWithSigningKey(body, signature, timestamp) if (!verified) { call.respond(HttpStatusCode.Unauthorized) return@post } // ... } }

一般的な検証ワークフローは次のようになります。

  1. アプリケーションの登録中に、Space はアプリケーションに署名キーを発行できます。キーを取得するには、アプリケーション設定のエンドポイントタブを開き、署名キー生成をクリックする必要があります。

  2. 署名キーを、たとえば文字列定数としてアプリケーションに保存します。

  3. Space はアプリケーションにリクエストを送信する前に、生成された署名キーを使用してリクエストハッシュを計算し、それを X-Space-Signature ヘッダーに入れます。例: これはヘッダーを含む Space リクエストのサンプルです。

    POST /api/myapp HTTP/1.1 Host: 12345abcdef.ngrok.io User-Agent: Space (61355) Ktor http-client Content-Length: 163 Accept: */* Accept-Charset: UTF-8 Content-Type: application/json X-Forwarded-For: 123.456.123.456 X-Forwarded-Proto: https X-Space-Signature: 2aa8cba6217a28686de0ca8dcfe2a1d0795e343d744a0c5307308e43777593a5 X-Space-Timestamp: 1607623492912 Accept-Encoding: gzip {"className":"ListCommandsPayload","accessToken":"","verificationToken":"d415ca5965b37f4f0cac59fd33de7b94e396284e897d0fb8a070d0a5e1b7f2d3","userId":"2kawvQ4F6GM6"}
  4. 次に、アプリケーションがリクエストハッシュを計算する番です。

    1. X-Space-Timestamp ヘッダーとリクエストボディの値を取得します。

    2. タイムスタンプと本文で構成される文字列を生成します。コロン : を区切り文字として使用します。たとえば、Kotlin では次のようになります。

      val str = "$timestamp:$body"

      上記のサンプルリクエストの場合、str 値は次のようになります。

      1607623492912:{"className":"ListCommandsPayload","accessToken":"","verificationToken":"d415ca5965b37f4f0cac59fd33de7b94e396284e897d0fb8a070d0a5e1b7f2d3","userId":"2kawvQ4F6GM6"}
    3. HMAC SHA256 を使用して文字列をハッシュします。

  5. 計算されたハッシュを X-Space-Signature ヘッダーから取得したハッシュと比較します。それらが等しい場合、アプリケーションはリクエストの処理を許可されます。そうでない場合、アプリケーションは HTTP 401 Unauthorized 応答コードを返す必要があります。

(廃止された) 検証トークン

このメソッドの背後にある考え方は、リクエスト本文の検証トークンを、Space への登録中にアプリケーションが取得したリクエストと比較することです。検証トークンはペイロードの一部であるため、SDK は ApplicationPayload クラスの拡張メソッドを提供します。

ApplicationPayload.verifyWithToken(verificationToken: String): Boolean
verificationToken がペイロード内のトークンと等しい場合、メソッドは true を返します。

このメソッドの単純な実装は次のようになります。

// token issued during app registration val verificationToken = "abc123" fun verifyPayload(payload: ApplicationPayload): Boolean { return payload.verifyWithToken(verificationToken) } fun Routing.backToSpace() { // the endpoint that handles Space requests post("api/myapp") { // get payload as text val body = call.receiveText() // deserialize payload into ApplicationPayload val payload = readPayload(body).also { if (!verifyPayload(it)) { call.respond(HttpStatusCode.Unauthorized) return@post } } // ... } }

一般的な検証ワークフローは次のようになります。

  1. アプリケーションの登録中に、Space はアプリケーションに検証トークンを発行できます。トークンを取得するには、アプリケーション設定のエンドポイントタブを開き、検証トークン生成をクリックする必要があります。

  2. このトークンを、たとえば文字列定数としてアプリケーションに保存します。

  3. Space がアプリケーションにリクエストを送信すると、この検証トークンがリクエスト本文に追加されます。例: スラッシュコマンドリクエストの本文は次のようになります (ユーザーがチャットボットのチャネルで / を押した場合)。

    { "className": "ListCommandsPayload", "accessToken": "", "verificationToken": "d415ca5965b37f4f0cac59fd33de7b94e396284e897d0fb8a070d0a5e1b7f2d3", "userId": "2kawvQ4F6GM6" }
  4. アプリケーションのタスクは、リクエストペイロードから verificationToken を取得し、それをアプリケーションに保存されているトークンと比較することです。それらが等しい場合、アプリケーションはリクエストの処理を許可されます。そうでない場合、アプリケーションは HTTP 401 Unauthorized 応答コードを返す必要があります。

SSL クライアント証明書

この検証方法は、Space からアプリケーションへのすべてのリクエストが SSL クライアントキーで暗号化されていることを意味します。検証はアプリケーションではなく、アプリケーションをホストする Web サーバーによって処理されます。通常、この検証方法を設定するには、次のことを行う必要があります。

  1. 秘密鍵と公開鍵を含む SSL キーストアファイルを生成します

  2. SSL キーストアファイルをアップロードする Space へ。

  3. アプリケーションの登録中に、アップロードされたキーストアを選択します。

  4. アプリケーションをホストする Web サーバーで、SSL クライアント証明書認証を構成します。正確な手順については、Web サーバーの公式ドキュメントを参照してください。

HTTP 認証

この方法は、Authorization 要求ヘッダーに基づく標準の HTTP 認証の使用を意味します。検証を実行するには 2 つの方法があります。ベアラートークンを使用する方法と、ユーザー名とパスワードを提供する基本認証を使用する方法です。

Space SDK には、この検証方法を実行するためのヘルパーメソッドが提供されていません。最新のフレームワークのほとんどは、すぐに使用できる HTTP 認証のサポートを提供します。例: ここに Ktor の手順があります。

無記名トークン

  1. アプリケーションの登録中に、HTTP 認証を選択し、次にベアラーを選択します。

  2. トークンで、検証トークンを指定します。

  3. リクエストを送信するとき、Space はこのトークンを Authorization ヘッダーに追加します。例:

    POST /api/myapp HTTP/1.1 Host: 12345abcdef.ngrok.io User-Agent: Space (61355) Ktor http-client X-Space-Timestamp: 1624376380652 Authorization: Bearer abc1234 X-Space-DeliveryID: e90ffc27-87dc-43f3-a13a-ac3860d53770 Accept-Charset: UTF-8 Accept: /
  4. アプリケーションのタスクは、Authorization: Bearer ヘッダー値を取得し、それをアプリケーションに保存されているトークンと比較することです。それらが等しい場合、アプリケーションはリクエストの処理を許可されます。そうでない場合、アプリケーションは HTTP 401 Unauthorized 応答コードを返す必要があります。

基本認証

  1. アプリケーションの登録中に、HTTP 認証を選択し、次に基本を選択します。

  2. ユーザー名およびパスワードを指定します。

  3. Space は、指定された資格情報を使用して単一の文字列 username:password を作成します (コロン : が区切り文字として使用されます)。次に、Base64 エンコーディング(英語)を使用して文字列をエンコードします。

    リクエストを送信すると、Space は生成された文字列を Authorization ヘッダーに追加します。例: johndoe:pwd1234 の場合:

    POST /api/myapp HTTP/1.1 Host: 12345abcdef.ngrok.io User-Agent: Space (61355) Ktor http-client X-Space-Timestamp: 1624449426984 Authorization: Basic am9obmRvZTpwd2QxMjM0 X-Space-DeliveryID: 2cd74c76-9cbb-4da5-81ab-7ce578145ccc Accept-Charset: UTF-8 Accept: /

  4. アプリケーションのタスクは、Authorization: Basic ヘッダー値を取得し、それをデコードして文字列に戻し、アプリケーションに保存されている資格情報と比較することです。それらが等しい場合、アプリケーションはリクエストの処理を許可されます。そうでない場合、アプリケーションは HTTP 401 Unauthorized 応答コードを返す必要があります。

関連ページ:

Space SDK

Space SDK は、JetBrains Space HTTP API を操作できるようにし、Space アプリケーションの開発を簡素化するライブラリです。クイックスタート:1. プロジェクト内の Space SDK を参照する次の依存関係をプロジェクトの / ファイルに追加します。// the example is given for build.gradle.kts repositories { // here go other project repos ... maven(...

単一組織アプリケーションの登録

アプリケーションの登録は、単一組織アプリケーションを Space インスタンスにインストールする主な方法です。アプリケーションを登録するときは、認可フロー、必要な権限、アプリケーションのエンドポイントなどの設定を手動で指定します。アプリケーションを Space インスタンスに追加する:メインメニューで、「拡張」をクリックし、「インストール済み」を選択します。新しいアプリをクリックします。指定: ユニークなアプリケーション名前。アプリケーションメール。アプリケーションが Space リポジトリにコ...

Space HTTP API

Space HTTP API を使用すると、チャット、チームディレクトリ、プロジェクト、ドキュメント、パッケージなどの任意の Space モジュールにプログラムでアクセスできます。特定の API リクエストの実行の詳細については、API 参照を参照するか、API Playground(下記を参照) を使用してください。HTTP API をすぐに使い始めるには、Space SDK を使用します。API Playground:API Playground を使用すると、次のことが可能になります。Spac...

権限のリクエスト

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