OpenID Connect (OIDC) の開発サービスおよび UI
このガイドでは、OpenID Connect(OIDC)のKeycloakプロバイダのDev ServicesとUIを取り上げ、他のOpenID ConnectプロバイダのDevサービスとUIをサポートする方法について説明します。また、Quarkusを開発モードで起動する前にすでに開始されているすべてのOpenID ConnectプロバイダのDev UIについても説明しています。
はじめに
Quarkus は、実験的な Dev Services For Keycloak 機能を導入しています。これは、 quarkus-oidc エクステンションが開発モードで起動され、統合テストがテストモードで実行されている場合にデフォルトで有効になります。ただし、 quarkus.oidc.auth-server-url プロパティが設定されいていない場合に限ります。開発モードかテストモード、あるいはその両方で Keycloak コンテナーを起動し、既存の Keycloak レルムを登録するか、クライアントとユーザーで新しいレルムを作成して初期化することで、Keycloak で保護された Quarkus アプリケーションの開発をすぐに開始します。 application.properties またはレルムファイルの変更が検出されると、コンテナーが再起動します。
さらに、 /q/dev で入手可能な Dev UI は、Dev UI ページでこの機能を補完します。これは、Keycloak からトークンを取得して Quarkus アプリケーションをテストする際に役立ちます。
quarkus.oidc.auth-server-url がすでに設定されている場合は、すべての OpenIDConnect プロバイダーで使用できる汎用 OpenIDConnectDevConsole がアクティブ化されます。詳細は すべての OpenID Connect プロバイダーの Dev UI を参照してください。
Dev Services for Keycloak
次のコマンドを使用して、 application.properties の quarkus.oidc プロパティーを設定せずにアプリケーションを起動します。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
コンソールには以下のように表示されます。
KeyCloak Dev Services Starting:
2021-11-02 17:14:24,864 INFO [org.tes.con.wai.str.HttpWaitStrategy] (build-10) /unruffled_agnesi: Waiting for 60 seconds for URL: http://localhost:32781 (where port 32781 maps to container port 8080)
2021-11-02 17:14:44,170 INFO [io.qua.oid.dep.dev.key.KeycloakDevServicesProcessor] (build-10) Dev Services for Keycloak started.
|
Keycloak 管理コンソールにログインする場合、ユーザー名は |
デフォルトでは、 Dev Services for Keycloak は quarkus-dev-service-keycloak ラベルの付いたコンテナーを見つけると新しいコンテナーを起動せず、このラベルの値が quarkus.keycloak.devservices.service-name プロパティーの値 (デフォルト値は quarkus) と一致する場合はこれに接続することに注意してください。その場合、以下の実行中はわずかに異なる出力が表示されます。
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
2021-08-27 18:42:43,530 INFO [io.qua.dev.com.ContainerLocator] (build-15) Dev Services container found: 48fee151a31ddfe32c39965be8f61108587b25ed2f66cdc18bb926d9e2e570c5 (quay.io/keycloak/keycloak:14.0.0). Connecting to: 0.0.0.0:32797.
2021-08-27 18:42:43,600 INFO [io.qua.oid.dep.dev.key.KeycloakDevServicesProcessor] (build-15) Dev Services for Keycloak started.
...
quarkus.keycloak.devservices.shared=false でコンテナーの共有を無効にできることに注意してください。
次に、メインの Dev UI ページ を開くと、 `OpenID Connect Card`が Keycloak のページにリンクしているのが確認できます。
Provider: Keycloak リンクをクリックすると、Keycloak ページが表示されます。このページは、 Dev ServicesforKeycloak 機能がどのように設定されているかによって少し異なります。
サービスアプリケーションの開発
デフォルトでは、Keycloak ページを使用して、Quarkus OIDC サービスアプリケーション の開発をサポートできます。
認可コード グラント
application.properties で quarkus.oidc.devui.grant.type=code (デフォルト値) を設定すると、 authorization_code 許可を使用してアクセストークンと ID トークンの両方が取得されます。この許可を使用して、 Single Page Application がトークンを取得し、それらを使用して Quarkus サービスにアクセスする一般的なフローをエミュレートすることをお勧めします。
まず、 Log into Single Page Application オプションが表示されます。
認証プロセスで使用されるKeycloakレルムとクライアントIDを選択します。
|
このSPAは、OpenId Connectのpublicクライアントを表しています。したがって、入力するクライアントIDは、シークレットのないKeycloakのpublicクライアントを指す必要があります。これは、SPAがウェブアプリケーションではないため、認可コードフローを完了するために必要なクライアントシークレットを安全に扱うことができないためです。 シークレットが必要なクライアントは、デフォルトのレルムが作成されている場合、または |
次に、 Log into Single Page Application を選択すると、Keycloakにリダイレクトされ、例えば、 alice:alice として認証され、SPAを表すページに戻ります:
取得したアクセストークンやIDトークンなどを確認することができます。
このビューでは、左側にエンコードされた JWT トークンが表示され、ヘッダー (赤色)、ペイロード/クレーム (緑色)、および署名 (青色) が強調表示されます。また、右側にデコードされた JWT トークンが表示され、ヘッダーとクレームの名前とその値を確認できます。
次に、相対サービスパスを入力し、トークンを送信してサービスをテストします。SPAは通常、アプリケーションのエンドポイントにアクセストークンを送信するので、例えば、 Test with Access Token のオプションを選択します:
右下の eraser のマークで、テスト結果エリアをクリアすることができます。
エンドポイントがSPAに現在ログインしているユーザーを認識するため、または帯域外のトークン検証を実行するために、IDトークンがベアラートークンとしてアプリケーションフロントエンドに転送されることがあります。このような場合は、 Test with ID Token オプションを選択してください。
Manually entering the service paths is not ideal, so please see the Test with Swagger UI or GraphQL UI section about enabling Swagger or GraphQL UI for testing the service with the access token already acquired by OIDC Dev UI.
最後に、ログアウトして別のユーザーとして Keycloak で認証する場合は、 Log Out image::dev-ui-keycloak-logout.png オプションを選択できます。
シングルページアプリケーションにログインしようとすると、Keycloak がエラーを返す場合があることに注意してください。たとえば、 quarkus.oidc.client-id が Keycloak にインポートされたレルムのクライアント ID と一致しない場合や、このレルムのクライアントが認可コードフローをサポートするように正しく設定されていない場合などです。このような場合、Keycloak は error_description クエリーパラメーターと DevUI も、このエラーの説明を表示します。以下はその例です。
エラーが発生した場合は、 Keycloak Admin オプションを使用して Keycloak にログインし、必要に応じてレルム設定を更新し、 application.properties も確認します。
Swagger UI または GraphQL UI を使用したテスト
プロジェクトで quarkus-smallrye-openapi や quarkus-smallrye-graphql が使用されている場合、サービスパスを手動で入力することを避け、 Swagger UI または GraphQL UI を使用してサービスをテストできます。たとえば、 quarkus-smallrye-openapi と quarkus-smallrye-graphql の両方の依存関係を使用して開発モードで Quarkus を起動すると、Keycloak にログインした後に次のオプションが表示されます。
例えば、 Swagger UI をクリックすると、新しいブラウザ タブで Swagger UI が開き、Dev UI for Keycloak が取得したトークンを使用してサービスをテストできます。 Swagger UI は再認証を試みません。OIDC Dev UIが認証を行い、Swagger UIがテストに使用するアクセストークンを提供しているため、Swagger UIに入った後は Swagger UI Authorize オプションを選択しないようにしてください。
GraphQL UI との統合も同様に機能し、Keycloak 用に Dev UI によって取得されたアクセストークンが使用されます。
|
Keycloak は、認証されたユーザーが設定されたリダイレクト URI にのみリダイレクトされるように強制する場合があるため、Keycloak が機能するためには Dev UI によって開始される認可コードフローのリダイレクト URI を登録する必要がある場合があります。認証要求内の正しい Keycloak がそれを強制する場合は、 この場合、右上隅の コンテナーが異なるポートで実行されている複数のアプリケーション間で共有されている場合は、これらのアプリケーションごとに 特にコンテナーが複数のアプリケーション間で共有されている場合は、テスト目的でのみ カスタムレルムがインポートされていない場合、 |
インプリシット・グラント
application.properties で quarkus.oidc.devui.grant.type=implicit を設定すると、アクセストークンと ID トークンの両方を取得するために implicit グラントが使用されます。認可コードグラントが機能しない場合に限り (暗示的グラントをサポートするためにクライントが Keycloak に設定されている場合など)、このグラントを使用して Single Page Application をエミュレートします。
パスワード・グラント
application.properties で quarkus.oidc.devui.grant.type=password を設定すると、次のような画面が表示されます。
レルムを選択し、クライアントIDとシークレット、ユーザー名とユーザーパスワード、サービスのエンドポイントの相対パスを入力し、 Test Service をクリックすると、 200 `, `403 `, `401 または 404 のようなステータスコードが表示されます。ユーザー名とパスワードを含む quarkus.keycloak.devservices.users マッププロパティにユーザー名も設定されている場合は、サービスをテストする際にパスワードを設定する必要はありません。ただし、パスワードグラントを使用してサービスをテストするために、 quarkus.keycloak.devservices.users を初期化する必要はないことに注意してください。
また、Dev UI コンソールには以下のように表示されます。
2021-07-19 17:58:11,407 INFO [io.qua.oid.dep.dev.key.KeycloakDevConsolePostHandler] (security-openid-connect-quickstart-dev.jar) (DEV Console action) Using password grant to get a token from 'http://localhost:32818/realms/quarkus/protocol/openid-connect/token' for user 'alice' in realm 'quarkus' with client id 'quarkus-app'
2021-07-19 17:58:11,533 INFO [io.qua.oid.dep.dev.key.KeycloakDevConsolePostHandler] (security-openid-connect-quickstart-dev.jar) (DEV Console action) Test token: eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ6Z2tDazJQZ1JaYnVlVG5kcTFKSW1sVnNoZ2hhbWhtbnBNcXU0QUt5MnJBIn0.ey...
2021-07-19 17:58:11,536 INFO [io.qua.oid.dep.dev.key.KeycloakDevConsolePostHandler] (security-openid-connect-quickstart-dev.jar) (DEV Console action) Sending token to 'http://localhost:8080/api/admin'
2021-07-19 17:58:11,674 INFO [io.qua.oid.dep.dev.key.KeycloakDevConsolePostHandler] (security-openid-connect-quickstart-dev.jar) (DEV Console action) Result: 200
トークンは password グラントを使用して Keycloak から取得され、サービスエンドポイントに送信されます。
OpenID Connect Web アプリケーションの開発
Quarkus OIDC web-app アプリケーション を開発する場合は、アプリケーションを開始する前に application.properties で quarkus.oidc.application-type=web-app を設定する必要があります。
次のような画面が表示されます。
相対的なサービスエンドポイントパスを設定し、 Sign In To Service をクリックすると Keycloak にリダイレクトされます。新しいブラウザータブにユーザー名とパスワードを入力し、Quarkus アプリケーションからレスポンスを取得します。
この場合、Dev UI は認可コードフローを制御してトークンを取得する Quarkus OIDC の web-app アプリケーションであるため、開発エクスペリエンスを充実させるものではないことに注意してください。
Dev UI が OIDC web-app アプリケーションの開発をより便利にサポートできるようにするために、 quarkus.oidc.application-type のプロファイル固有値を設定することを検討してください。
%prod.quarkus.oidc.application-type=web-app
%test.quarkus.oidc.application-type=web-app
%dev.quarkus.oidc.application-type=service
これにより、開発モードで web-app アプリケーションが実行されている際に、Developing OpenID Connect Service Applications で説明されているすべての Dev UI オプションが確実に使用可能なります。このアプローチの制限は、コードフローで返され、Dev UI で取得されたアクセストークンと ID トークンの両方が、HTTP Bearer トークンとしてエンドポイントに送信されることです。これは、エンドポイントで IdToken の挿入が必要な場合はうまく機能しません。ただし、 web-app アプリケーションがアクセストークンのみを使用する場合 (たとえばロールのソースとして、または UserInfo の取得に)、devmode の service アプリケーションであると想定される場合でも期待どおり機能します。
さらに良いオプションは、devmode で「ハイブリッド」アプリケーション タイプを使用することです:
%prod.quarkus.oidc.application-type=web-app
%test.quarkus.oidc.application-type=web-app
%dev.quarkus.oidc.application-type=hybrid
これにより、OIDC DevUI を使用せずにブラウザーから開発モードでアプリケーションにアクセスすると、Quarkus OIDC もプロダクション モードと同様に認可コード フローを実行するようになります。 しかし、 hybrid アプリケーションはベアラー アクセス トークンも受け入れることができるため、OIDC DevUI もより便利になります。
テストの実行
テストモードで起動した Keycloak コンテナーに対して、継続的テスト モードでテストを実行できます。
また、 Dev Services for Keycloak を使用して、Keycloak に対して統合テストを実行することをお勧めします。詳細は、Dev Services を使用した OpenID Connect サービスアプリケーションのテスト および Dev Services を使用した OpenID Connect WebApp アプリケーションのテスト を参照してください。
Keycloak 初期化
The quay.io/keycloak/keycloak:21.0.1 image which contains a Keycloak distribution powered by Quarkus is used to start a container by default.
quarkus.keycloak.devservices.image-name can be used to change the Keycloak image name. For example, set it to quay.io/keycloak/keycloak:19.0.3-legacy to use a Keycloak distribution powered by WildFly.
Note that only a Quarkus based Keycloak distribution is available starting from Keycloak 20.0.0.
Dev Services for Keycloak は、起動された Keycloak サーバーを次に初期化します。
デフォルトでは、 quarkus、 secret パスワードを持つ quarkus-app クライアント、 alice および bob ユーザー (名前と一致するパスワードを持つ)、 user および admin ロールが作成されます。このとき、 alice には admin と user の両方のロールが与えられ、 bob には user のロールが与えられます。
ユーザー名、シークレット、およびそれらのロールは、 quarkus.keycloak.devservices.users (ユーザー名とシークレットを含むマップ) および quarkus.keycloak.devservices.roles (ユーザー名とコンマ区切りのロール値を含むマップ) でカスタマイズできます。
例えば、以下のようになります。
%dev.quarkus.keycloak.devservices.users.duke=dukePassword
%dev.quarkus.keycloak.devservices.roles.duke=reader
%dev.quarkus.keycloak.devservices.users.john=johnPassword
%dev.quarkus.keycloak.devservices.roles.john=reader,writer
この設定により、2人のユーザーが作成されます:
* dukePassword パスワードと reader ロールを持つ duke
* johnPassword パスワードと reader および writer ロールを持つ john
quarkus.oidc.client-id と quarkus.oidc.credentials.secret を使用して、クライアント ID とシークレットをカスタマイズできます。
ただし、Keycloak の設定はこれよりも複雑で、より多くのプロパティーを設定する必要があると思われます。
このため、デフォルトまたは設定されたレルム、クライアント、ユーザー、ロールのプロパティでKeycloakを初期化しようとする前に、 quarkus.keycloak.devservices.realm-path が常に最初にチェックされます。realmファイルがファイルシステムまたはクラスパスに存在する場合、このrealmだけがKeycloakを初期化するために使用されます。例:
quarkus.keycloak.devservices.realm-path=quarkus-realm.json
quarkus.keycloak.devservices.realm-path を使って、カンマで区切られたファイルのリストを提供することで、複数のレルムファイルでKeycloakを初期化することができます:
quarkus.keycloak.devservices.realm-path=quarkus-realm1.json,quarkus-realm2.json
また、Keycloak ページには、右上隅にある KeycloakAdmin オプションを使用して、Keycloak にサインインしてレルムを設定 (SignIn To Keycloak To Configure Realms) するオプションがあります。
レルムのプロパティーのカスタマイズ、新規レルムの作成またはインポート、レルムのエクスポートを実行するには、Keycloak に admin:admin としてサインインします。
Keycloak の Dev サービスを無効にする
quarkus.oidc.auth-server-url がすでに初期化されているか、デフォルトの OIDC テナントが quarkus.oidc.tenant.enabled=false で無効になっている場合、Keycloak を使用するかどうかにかかわらず、 Dev Services ForKeycloak はアクティブ化されません。
Dev Services for Keycloak コンテナーを起動させない、または使用しない場合も、 quarkus.keycloak.devservices.enabled=false を使用してこの機能を無効化できます。これは、 quarkus.oidc.auth-server-url なしで quarkus:dev を起動する予定がある場合にのみ必要です。
Dev Services for Keycloak が無効で quarkus.oidc.auth-server-url プロパティーが初期化されていない場合、メインの Dev UI ページには空の OpenIDConnectCard が含まれます。
quarkus.oidc.auth-server-url がすでに設定されている場合は、すべての OpenIDConnect プロバイダーで使用できる汎用 OpenIDConnectDevConsole がアクティブ化されます。詳細は Dev UI for all OpenID Connect Providers を参照してください。
すべての OpenID Connect プロバイダーの Dev UI
quarkus.oidc.auth-server-url が、すでに起動されている OpenID Connect プロバイダー (Keycloak または他のプロバイダーも可) を指し、 quarkus.oidc.auth-server-url が service (デフォルト値) に設定され、少なくとも quarkus.oidc.client-id が設定されている場合、すべての OpenID Connect プロバイダーの Dev UI (Dev UI for all OpenID Connect Providers) がアクティブになります。
Keycloak や その他のプロバイダーでは、 quarkus.oidc.client-id で指定されたクライアントが使用している OpenID Connect プロバイダーの管理コンソールでパブリッククライアントとして設定されていない限り、Dev UI から開始された認可コードフローを完了するには quarkus.oidc.credentials.secret の設定が必要です。
実行:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
以下のメッセージが表示されます。
...
2021-09-07 15:53:42,697 INFO [io.qua.oid.dep.dev.OidcDevConsoleProcessor] (build-41) OIDC Dev Console: discovering the provider metadata at http://localhost:8180/realms/quarkus/.well-known/openid-configuration
...
プロバイダーメタデータの検出が成功した場合、メインの Dev UI ページ を開くと、 Dev Console にリンクされた OpenID Connect Card ページが表示されます。
リンクをたどると、プロバイダーにログインしてトークンを取得し、アプリケーションをテストできます。特に Keycloakを使用している場合、これは Dev Services for Keycloak コンテナーを起動した Keycloakに対する認可コードグラント セクションで説明したとおりです。
OpenId Connect プロバイダーが Dev Console へのリダイレクトをサポートするように設定する必要があるようです。 http://localhost:8080/q/dev/io.quarkus.quarkus-oidc/provider をリダイレクトとログアウトの URL として追加してください。
他のプロバイダーと連携している場合、 Keycloakに対する認可コードグラント セクションで説明した Dev UI とは若干異なる場合があります。たとえば、アクセストークンは JWT 形式ではない可能性があるため、内部コンテンツは表示できませんが、すべてのプロバイダーは ID トークンを JWT として返す必要があります。
|
デフォルトでは、現在のアクセストークンを使用して、 |
Auth0 などの一部のプロバイダーは標準の RP 主導型ログアウトをサポートしていないため、プロバイダー固有のログアウトプロパティーを設定しなければ、ログアウトオプションは表示されません。詳細は、 OpenID Connect ユーザー主導型ログアウト を参照してください。
同様に、Dev UI に password または client_credentials グラントを使用してトークンを取得する場合は、次のような追加のプロバイダー固有プロパティーを設定する必要があります。
quarkus.oidc.devui.grant.type=password
quarkus.oidc.devui.grant-options.password.audience=http://localhost:8080
その他の OpenID Connect プロバイダーの Dev Services と UI サポート
カスタムエクステンションは、 quarkus-oidc を拡張し、プロバイダーをサポートするために必要な依存関係をエクステンションの deployment モジュールにのみ追加する必要があります。
Dev Services を処理するビルドステップでは、 OpenID Connect Card をプロバイダーを示す Dev UI ページにリンクするために、2 つのランタイムプロパティーを "io.quarkus.quarkus-oidc" 名前空間に追加で登録する必要があります: oidcProviderName (例: Google) と oidcProviderUrlBase (例: mycompany.devservices-google)。
package io.quarkus.oidc.okta.runtime;
import java.util.function.Supplier;
import io.quarkus.runtime.annotations.Recorder;
// This simple recorder is the only code which will be located in the extension's `runtime` module
@Recorder
public class OktaDevServicesRecorder {
public Supplier<String> getProviderName() {
return new Supplier<String>() {
@Override
public String get() {
return "OKTA";
}
};
}
public Supplier<String> getProviderUrlBase() {
return new Supplier<String>() {
@Override
public String get() {
return "io.quarkus" + "." + "quarkus-oidc-okta";
}
};
}
}
package io.quarkus.oidc.okta.deployment.devservices;
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import java.util.Optional;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.RuntimeConfigSetupCompleteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRouteBuildItem;
import io.quarkus.devconsole.spi.DevConsoleRuntimeTemplateInfoBuildItem;
public class OktaDevConsoleProcessor {
@BuildStep(onlyIf = IsDevelopment.class)
@Record(value = RUNTIME_INIT)
public void setOidcProviderProperties(BuildProducer<DevConsoleRuntimeTemplateInfoBuildItem> provider,
OktaDevServicesRecorder recorder,
Optional<DevServicesConfigBuildItem> configProps) {
if (configProps.isPresent()) {
provider.produce(new DevConsoleRuntimeTemplateInfoBuildItem("io.quarkus", "quarkus-oidc", "oidcProviderName",
recorder.getProviderName()));
provider.produce(new DevConsoleRuntimeTemplateInfoBuildItem("io.quarkus", "quarkus-oidc", "oidcProviderUrlBase",
recorder.getProviderUrlBase()));
}
}
}
さらに、ユーザーが quarkus.keycloak.devservices.enabled=false を入力するのではなく、エクステンションが io.quarkus.oidc.deployment.devservices.OidcProviderBuildItem を生成してデフォルトの Dev Services for Keycloak を無効にする必要があります。
Dev UI チュートリアルと extensions/oidc/deployment ソースで他のアイデアも確認してください。
アプリケーション以外のルートパスに関する考慮事項
このドキュメントでは、繰り返し http://localhost:8080/q/dev Dev UI URL を参照していますが、この q はデフォルトの非アプリケーションルートパスです。 quarkus.http.root-path や quarkus.http.non-application-root-path プロパティーをカスタマイズした場合、 q を適宜置き換えてください。詳細については、 Quarkus を使用したパスの解決 を参照してください。