web-dev-qa-db-ja.com

keycloak - OAuth-2認証フロー

私たちは、私のいくつかの通信でのOAuth2認証を(私&私の同僚)を統合しようとしているRESTをREST。認証プロバイダとマネージャーとして、私たちはKeycloakを使用したいと思います。我々はいくつかを読んでドキュメントと我々はそれがどのように動作するか理解だと思います。今、私たちは、フロー認証我々は点にのOAuth2プロトコルの原則を設計した場合、あなたからしてください知っていると思います。我々は、使用することを決めフロー認証トークンとその検証の生成を含みますは:

AUTH-フローのグラフィック表現

のは、使用のOAuth2が互いを認証するために、2つのアプリケーション(アプリケーションAとアプリケーションB)との間のデータ交換の一例の説明に進みましょう。 Aアプリケーションはトークンを要求するKeycloakを照会方法を示す上記画像。その後、アプリケーションAは、それがKeycloakから受信したトークンを入力してアプリケーションBにリクエストを送信します。この時点で、アプリケーションBは、トークンを受信した後、Keycloakを照会し、受信したトークンを検証することを要求します。最後に、アプリケーションBは、トークンと、この応答に基づいて、アプリケーションAから受信した要求を受け入れることや、それを拒否するかどうかを決定することができるようになりますの妥当性についての答えを受け取ることになります。

のは、交換の技術的な詳細を見てみましょう:

1 - アプリケーションA、新たなトークンを生成するために、認証を通じて、Keycloakを尋ねる。

HTTPリクエストの例:

POST /auth/realms/OMS/protocol/openid-connect/token HTTP/1.1
Host: local-keycloak.it:8080
Content-Type: application/x-www-form-urlencoded
cache-control: no-cache

client_id=oms-test&client_secret=039a6b94-44a7-4dae-b8a4-e7b673eee8e5&grant_type=client_credentials&scope=openid
 _
  • / AUTH /レルム/ {REALM_NAME} /プロトコル/ OpenIDのコネクト/トークン:Keycloakエンドポイントコールを生成するためのトークンを取得します。

HTTPメッセージのボディは、サーバに送信され、本質的に1つの巨大なクエリ文字列である - 名前/値ペアをアンパサンド(&)によって分離され、名前が等しい記号(=)での値から分離されます。実際には、本体のコンテンツタイプは、「アプリケーション/ x-www-form-urlencodedで」として示されています。

2 - Keycloak、アプリケーションAの認証トークンを生成し、それに転送し、成功した場合。

HTTPレスポンスの例:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2NzhjY2M0NC03ZTY3LTRmODAtOTk5NC1hOTA0NmI3NGY2YTgifQ.eyJqdGkiOiJiYTU3NzZjYi03Zjg1LTRhNTAtOGM5Ni1kYWQ3OTRlZGRjZWIiLCJleHAiOjE1NTc5OTY4NTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL09NUyIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJvbXMtdGVzdCIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6ImVhNWUxYzQyLWI5MzUtNGRhMC05ZDY2LWEwMjlmZGY3NzdiMiIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSJ9.BkXWMLmuf1c0OBUeg2P2262LLvTmhXg46y4-rrvebNE",
    "token_type": "bearer",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiIyYjdjZGI2Ny1kYjM3LTQ5MTQtYWNiYi0xNmU5MDA4YzQ4N2IiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoib21zLXRlc3QiLCJzdWIiOiI2MGU4YjY1Yy02NTk2LTQzMmEtYTY2OC0xMzk5YzE2N2QzODYiLCJ0eXAiOiJJRCIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImNsaWVudEhvc3QiOiIxMjcuMC4wLjEiLCJjbGllbnRJZCI6Im9tcy10ZXN0IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX25hbWUiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3QiLCJjbGllbnRBZGRyZXNzIjoiMTI3LjAuMC4xIiwiZW1haWwiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3RAcGxhY2Vob2xkZXIub3JnIn0.C0YkK-B4LnKH3NBCxHjuAkVZKVikh3FaUPIUpToCVFKkgefZRF7JS2yddC4ejxn4_B4y56TBMdVSXg5dEk-ghkz_f1VOR1whRY0HAC6Z5izEJBOHesASWoxJE43QJHXoDYzNWJK1S4JQ6W_BF5KobVHrXL2fmb-ypLBJCc8EAMTYEC-fpxT_T3NkDbsAjmnoCTl1YmRiDkV0sqUKerx5irIZJ3S297Z0Ub4Ahal8ObX7t3JbpJ-SBEvRvNo0PriZdk7C1DZQEhc77v2qnpeyqkwcRkAhZ0uXb5QF32J6dxhKh8-gZHYCauMdzeNmkh-962RnWXqyhGOYipLarnmzjg",
    "not-before-policy": 0,
    "session_state": "ea5e1c42-b935-4da0-9d66-a029fdf777b2",
    "scope": "openid email profile"
}
 _

私たちが見ることができるように、私たちは私たちのプラットフォーム間でのトークン認証および承認として使用するアクセストークンを与えられています。アクセストークンは、これでリフレッシュトークン有効期限よりも早く、アクセストークンを再生成することが可能であるがあります。

3 - アプリケーションAがトークン(access_tokenは)受信を使用してアプリケーションBに要求を送信する。

HTTPリクエストの例:

POST /omsesb/order/placeOrder HTTP/1.1
Host: application-b.it:8081
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg
Client-Id: oms-test
cache-control: no-cache

  {
    "orders": 
        {
            "order": {
                    ...
            }
        }
   }
 _

私たちが見ることができるように、「承認」ヘッダがKeycloakから受信し、そのアプリケーションAアクセストークンが含まれています。我々は、AがKeycloakからトークンの生成を要求しているアプリケーションとユーザIDを含むであろう他の「クライアントID」ヘッダを追加しました。

この例では、アプリケーションAは、新しい注文を入力するために、アプリケーションBを求めているが、最初に自分自身を認証する必要があり、したがって、許可されなければなりません。

4 - アプリケーションBは、アプリケーションAから受信したアクセストークンを検証するために、要求に、Keycloakを尋ねる。

HTTPリクエストの例:

POST /auth/realms/OMS/protocol/openid-connect/token/introspect HTTP/1.1
Host: local-keycloak.it:8080
Content-Type: application/x-www-form-urlencoded
cache-control: no-cache

client_id=account&client_secret=d67da47e-387a-4930-a89a-eda0296c4896&token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg
 _
  • / AUTH /レルム/ {REALM_NAME} /プロトコル/ OpenIDのコネクト/トークン/ INTROSPECT:Keycloakエンドポイントコールの検証Aトークン。

5 - Keycloakは、この時点で、応答として検証の結果を与えることによって応答します。結果は、正、または負とすることができる。

検証の肯定的な結果を含むHTTPレスポンスの例:

{
    "jti": "26b6794d-3d83-446a-a106-10dfb14793c3",
    "exp": 1557997077,
    "nbf": 0,
    "iat": 1557996777,
    "iss": "http://local-keycloak:8080/auth/realms/OMS",
    "aud": "account",
    "sub": "60e8b65c-6596-432a-a668-1399c167d386",
    "typ": "Bearer",
    "azp": "oms-test",
    "auth_time": 0,
    "session_state": "88a63302-b148-42e4-81d0-5cb81c446903",
    "preferred_username": "service-account-oms-test",
    "email": "[email protected]",
    "email_verified": false,
    "acr": "1",
    "allowed-origins": [
        "http://local-keycloak:8080"
    ],
    "realm_access": {
        "roles": [
            "offline_access",
            "uma_authorization"
        ]
    },
    "resource_access": {
        "account": {
            "roles": [
                "manage-account",
                "manage-account-links",
                "view-profile"
            ]
        }
    },
    "scope": "openid email profile",
    "clientHost": "application-a.it",
    "clientId": "oms-test",
    "user_name": "service-account-oms-test",
    "clientAddress": "80.10.10.1",
    "client_id": "oms-test",
    "username": "service-account-oms-test",
    "active": true
}
 _

Keycloakは、トークン(まだ切れていない)を認識し、アプリケーションBには、アプリケーションAの要求を受け入れるか否かを判断することができたことにより、情報のセットを返しました:私たちが見ることができるように、検証が成功した(アクティブ=真)でした。

この情報によって、アプリケーションBは、クライアント、IP、アドレスやその他の情報のIDによって、以前にトークンを要求したユーザーを認識することができます。

この場合、可能なman-in-the-middle攻撃を回避するために、認証プロバイダによって受信されたものを使用してアプリケーションAから受信したデータを一致させることができますBのアプリケーション:それは確認することができますが、クライアントIDとIPアドレスが一致していること。

検証の否定的な結果を含むHTTPレスポンスの例:

{
    "active": false
}
 _

トークンが存在しない場合、結果は陰性であるトークンの有効期限が切れたか、トークンの検証を要求しているアプリケーションは、要求の検証に必要な権限を持たないユーザを使用します。

6 - アプリケーションAにKeycloak、アプリケーションBが通信から受信した結果に基づいて、その要求が受け入れられない、またはされていた場合。

だから、あなたの意見では、この認証フローが正しい可能性があり、したがって、のOAuth2の原則を尊重しますか?

6
Berat Rahmani

私はスタックユーザーからの応答を受け取っていませんが、私の同僚との流れを見てOAuth2を勉強しています。私たちは、私たちが実装したフローが効率的で、OAuth2の規則と特性を尊重することに気付きました。さらに、IPとクライアントのユーザー名に基づいて最終的な制御を導入することで、トークンを要求したクライアントが同じものであることを確認してから、それを認証として提示したという事実を保証できます。

2
Berat Rahmani

ステップ4は不要で、キーコックサーバーの過負荷をかけるだろうと思います。アプリケーションBはそれ自体でアクセストークンを検証できます。

最初にそれが公開鍵のためにkeycloakを尋ねる(そして、それらを各要求でそれらを照会する必要はないように)。

それからアクセストークンから子供を取り、対応する公開鍵を取得します。

トークンの整合性が安全であることを確認してください(トークンの署名を検証します)。

トークンが期限切れになっていないことを確認してください。

ユーザーのロール/アクセス許可のようなトークンに追加できる他のカスタム情報(そうである場合)を確認してください。

0
Paco Abato

これが有効な認証フローであるかどうかを確認できましたか?私はここで似たものを見つけました: keycloakを使って "APIキーを"発行されます

0
lianc