4.2. クライアント開始アカウントリンク
アプリケーションによっては、Facebook などのソーシャルプロバイダーと統合しても、このようなソーシャルプロバイダー経由でログインするオプションを提供しないようにする場合もあります。Red Hat build of Keycloak は、アプリケーションが既存のユーザーアカウントを特定の外部 IDP にリンクするために使用できるブラウザーベースの API を提供します。これは、クライアント開始アカウントリンク と呼ばれます。アカウントリンクは OIDC アプリケーションによってのみ開始できます。
アプリケーションがユーザーのブラウザーを Red Hat build of Keycloak サーバーの URL に転送する方法は、ユーザーのアカウントを特定の外部プロバイダー (Facebook など) にリンクするように要求することです。サーバーは外部プロバイダーでログインを開始します。ブラウザーは外部プロバイダーにログインし、サーバーにリダイレクトされます。サーバーはリンクを確立し、確認でアプリケーションにリダイレクトします。
このプロトコルを開始する前に、クライアントアプリケーションが満たさなければならない前提条件があります。
- 必要なアイデンティティープロバイダーは、管理コンソールでユーザーのレルムに対して設定して有効にする必要があります。
- ユーザーアカウントは、OIDC プロトコルを使用して既存のユーザーとしてログインしている必要があります。
-
ユーザーに
account.manage-account
またはaccount.manage-account-links
ロールマッピングが必要です。 - アプリケーションには、アクセストークン内で上記のロールのスコープが付与される必要があります。
- アプリケーションは、リダイレクト URL 生成にその情報を必要とするため、アクセストークンへのアクセスが必要です。
ログインを開始するには、アプリケーションは URL を作成し、ユーザーのブラウザーをこの URL にリダイレクトする必要があります。URL は以下のようになります。
/{auth-server-root}/realms/{realm}/broker/{provider}/link?client_id={id}&redirect_uri={uri}&nonce={nonce}&hash={hash}
以下は、各パスおよびクエリーパラメーターの説明です。
- provider
-
これは、管理コンソールの
Identity Provider
セクションで定義した外部 IDP のプロバイダーエイリアスです。 - client_id
- これは、アプリケーションの OIDC クライアント ID です。アプリケーションを管理コンソールでクライアントとして登録する場合は、このクライアント ID を指定する必要があります。
- redirect_uri
- これは、アカウントリンクの確立後にリダイレクトするアプリケーションのコールバック URL です。有効なクライアントのリダイレクト URI パターンである必要があります。つまり、管理コンソールでクライアント登録時に定義した有効な URL パターンのいずれかと一致する必要があります。
- nonce
- これは、アプリケーションが生成する必要のあるランダムな文字列です。
- hash
-
これは、Base64 URL でエンコードされたハッシュです。このハッシュは、Base64 URL で
nonce
+token.getSessionState()
+token.getIssuedFor()
+provider
の SHA_256 ハッシュをエンコードすることで生成されます。トークン変数は OIDC アクセストークンから取得されます。基本的に、アクセスするアイデンティティープロバイダーエイリアス、nonce、ユーザーセッション ID、クライアント ID、およびアイデンティティープロバイダーエイリアスを無作為にハッシュ化します。
以下は、アカウントリンクを確立するために URL を生成する Java Servlet コードの例になります。
KeycloakSecurityContext session = (KeycloakSecurityContext) httpServletRequest.getAttribute(KeycloakSecurityContext.class.getName()); AccessToken token = session.getToken(); String clientId = token.getIssuedFor(); String nonce = UUID.randomUUID().toString(); MessageDigest md = null; try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } String input = nonce + token.getSessionState() + clientId + provider; byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8)); String hash = Base64Url.encode(check); request.getSession().setAttribute("hash", hash); String redirectUri = ...; String accountLinkUrl = KeycloakUriBuilder.fromUri(authServerRootUrl) .path("/realms/{realm}/broker/{provider}/link") .queryParam("nonce", nonce) .queryParam("hash", hash) .queryParam("client_id", clientId) .queryParam("redirect_uri", redirectUri).build(realm, provider).toString();
このハッシュを含める理由認証サーバーで、クライアントアプリケーションが要求を開始したことを認識し、その他の不正なアプリが、特定のプロバイダーにリンクされるユーザーアカウントを無作為に要求しないように、ハッシュを含めます。認証サーバーは、最初にログイン時に設定された SSO クッキーをチェックしてユーザーがログインしているかどうかを確認します。その後、現在のログインに基づいてハッシュを再生成し、アプリケーションによって送信されるハッシュにマッチします。
アカウントのリンク後、認証サーバーは redirect_uri
にリダイレクトします。リンク要求の処理に問題がある場合は、認証サーバーが redirect_uri
にリダイレクトされない場合があります。ブラウザーは、アプリケーションにリダイレクトされるのではなく、エラーページで終了できます。エラー条件があり、認証サーバーがクライアントアプリケーションにリダイレクトするのに十分な安全でない場合には、追加の error
クエリーパラメーターが redirect_uri
に追加されます。
この API は、アプリケーションで確実に要求を開始されるようにしますが、この操作に対する CSRF 攻撃を完全に防ぐことはできません。このアプリケーションは、CSRF 攻撃ターゲットに対して自己防衛します。
4.2.1. 外部トークンのリフレッシュ
プロバイダーにログインして生成される外部トークン (つまり Facebook または GitHub トークン) を使用している場合は、アカウントリンク API を再度初期化してこのトークンを更新できます。