Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Keycloak Test Module to support code flow tests #16213

Merged
merged 1 commit into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion docs/src/main/asciidoc/security-jwt.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -823,7 +823,16 @@ mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus
smallrye.jwt.sign.key.location=privateKey.jwk
----

It will ensure that the `smallrye-jwt` remote key resolution code also works as expected.
=== Keycloak

If you work with Keycloak and configure `mp.jwt.verify.publickey.location` to point to HTTPS or HTTP based JsonWebKey (JWK) set then you can use the same approach as described in the link:security-openid-connect#integration-testing-keycloak[OpenId Connect Bearer Token Integration testing] `Keycloak` section but only change the `application.properties` to use MP JWT configuration properties instead:

[source, properties]
----
# keycloak.url is set by OidcWiremockTestResource
mp.jwt.verify.publickey.location=${keycloak.url}/realms/quarkus/protocol/openid-connect/certs
mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus
----

=== Local Public Key

Expand All @@ -839,6 +848,10 @@ mp.jwt.verify.issuer=${keycloak.url}/realms/quarkus
smallrye.jwt.sign.key.location=privateKey.pem
----

=== TestSecurity annotation

Please see link:security-testing#testing-security[TestingSecurity Annotation] section how to do simple tests with the `TestSecurity` annotation.

[[generate-jwt-tokens]]
== Generate JWT tokens with SmallRye JWT

Expand Down
33 changes: 20 additions & 13 deletions docs/src/main/asciidoc/security-openid-connect-client.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ Using `private_key_jwt` or `private_key_jwt` authentication methods ensures that
[[integration-testing-oidc-client]]
=== Testing

Add the following dependencies to your test project:
Start by adding the following dependencies to your test project:

[source,xml]
----
Expand All @@ -340,18 +340,22 @@ Add the following dependencies to your test project:
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
----

[[integration-testing-wiremock]]
==== Wiremock

Add the following dependencies to your test project:

[source,xml]
----
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<scope>test</scope>
</dependency>
----
Expand Down Expand Up @@ -400,8 +404,7 @@ public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycl


Map<String, String> conf = new HashMap<>();
conf.put("quarkus.oidc-client.auth-server-url", server.baseUrl());
conf.put("keycloak-url", server.baseUrl());
conf.put("keycloak.url", server.baseUrl());
return conf;
}

Expand All @@ -425,7 +428,7 @@ Set `application.properties`, for example:
quarkus.oidc-client.auth-server-url=${keycloak.url}
quarkus.oidc-client.discovery-enabled=false
quarkus.oidc-client.token-path=/tokens
quarkus.oidc-client.client-id=quarkus-app
quarkus.oidc-client.client-id=quarkus-service-app
quarkus.oidc-client.credentials.secret=secret
quarkus.oidc-client.grant.type=password
quarkus.oidc-client.grant-options.password.username=alice
Expand All @@ -434,6 +437,10 @@ quarkus.oidc-client.grant-options.password.password=alice

and finally write the test code. Given the Wiremock-based resource above, the first test invocation should return `access_token_1` access token which will expire in 4 seconds. Use `awaitility` to wait for about 5 seconds, and now the next test invocation should return `access_token_2` access token which confirms the expired `access_token_1` access token has been refreshed.

=== Keycloak

If you work with Keycloak then you can use the same approach as described in the link:security-openid-connect#integration-testing-keycloak[OpenId Connect Bearer Token Integration testing] `Keycloak` section.

== Token endpoint configuration

By default the token endpoint address is discovered by adding a `/.well-known/openid-configuration` path to the configured `quarkus.oidc-client.auth-server-url`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -576,15 +576,10 @@ Please see link:security-openid-connect-client#token-propagation[Token Propagati
[[integration-testing]]
== Testing

Add the following dependencies to your test project:
Start by adding the following dependencies to your test project:

[source,xml]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-oidc-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
Expand All @@ -601,10 +596,19 @@ Add the following dependencies to your test project:
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
----

[[integration-testing-wiremock]]
=== Wiremock

Add the following dependency:

[source,xml]
----
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-oidc-server</artifactId>
<scope>test</scope>
</dependency>
----

Expand Down Expand Up @@ -666,9 +670,66 @@ public class CodeFlowAuthorizationTest {
----

`OidcWiremockTestResource` recognizes `alice` and `admin` users. The user `alice` has the `user` role only by default - it can be customized with a `quarkus.test.oidc.token.user-roles` system property. The user `admin` has the `user` and `admin` roles by default - it can be customized with a `quarkus.test.oidc.token.admin-roles` system property.

Additionally, `OidcWiremockTestResource` set token issuer and audience to `https://service.example.com` which can be customized with `quarkus.test.oidc.token.issuer` and `quarkus.test.oidc.token.audience` system properties.

`OidcWiremockTestResource` will be enhanced going forward to support more complex authorization code flow test scenarios.
`OidcWiremockTestResource` can be used to emulate all OpenId Connect providers.

=== Keycloak

If you work with Keycloak then you can test against a live Keycloak instance by adding the following dependency:

[source,xml]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-keycloak-server</artifactId>
<scope>test</scope>
</dependency>
----

and configure `maven.surefire.plugin` as follows:

[source,xml]
----
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- or, alternativey, configure 'keycloak.version' -->
<keycloak.docker.image>${keycloak.docker.image}</keycloak.docker.image>
<!--
Disable HTTPS if required:
<keycloak.use.https>false</keycloak.use.https>
-->
</systemPropertyVariables>
</configuration>
</plugin>
----

(and similarly `maven.failsafe.plugin` when testing in native image).

And now set the configuration and write the test code the same way as it is described in the <<integration-testing-wiremock, Wiremock>> section above.
The only difference is the name of `QuarkusTestResource`:

[source, java]
----
import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager;

@QuarkusTest
@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class)
public class CodeFlowAuthorizationTest {
}
----

`KeycloakTestResourceLifecycleManager` registers `alice` and `admin` users. The user `alice` has the `user` role only by default - it can be customized with a `keycloak.token.user-roles` system property. The user `admin` has the `user` and `admin` roles by default - it can be customized with a `keycloak.token.admin-roles` system property.

By default, `KeycloakTestResourceLifecycleManager` uses HTTPS to initialize a Keycloak instance which can be disabled with `keycloak.use.https=false`.
Default realm name is `quarkus` and client id - `quarkus-web-app` - set `keycloak.realm` and `keycloak.web-app.client` system properties to customize the values if needed.

=== TestSecurity annotation

Please see link:security-testing#testing-security[TestingSecurity Annotation] section how to do simple tests with the `TestSecurity` annotation.

== Configuration Reference

Expand Down
116 changes: 108 additions & 8 deletions docs/src/main/asciidoc/security-openid-connect.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -434,26 +434,39 @@ Please see link:security-openid-connect-client#token-propagation[Token Propagati
[[integration-testing]]
== Testing

=== Wiremock

Add the following dependencies to your test project:
Start by adding the following dependencies to your test project:

[source,xml]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-oidc-server</artifactId>
<groupId>net.sourceforge.htmlunit</groupId>
<artifactId>htmlunit</artifactId>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
----

[[integration-testing-wiremock]]
=== Wiremock

Add the following dependencies to your test project:

[source,xml]
----
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-oidc-server</artifactId>
<scope>test</scope>
</dependency>
----

Expand Down Expand Up @@ -516,6 +529,89 @@ public class BearerTokenAuthorizationTest {
Testing your `quarkus-oidc` `service` application with `OidcWiremockTestResource` provides the best coverage as even the communication channel is tested against the Wiremock HTTP stubs.
`OidcWiremockTestResource` will be enhanced going forward to support more complex Bearer token test scenarios.

[[integration-testing-keycloak]]
=== Keycloak

If you work with Keycloak then you can test against a live Keycloak instance by adding the following dependency:

[source,xml]
----
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-keycloak-server</artifactId>
<scope>test</scope>
</dependency>
----

and configure `maven.surefire.plugin` as follows:

[source,xml]
----
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- or, alternativey, configure 'keycloak.version' -->
<keycloak.docker.image>${keycloak.docker.image}</keycloak.docker.image>
<!--
Disable HTTPS if required:
<keycloak.use.https>false</keycloak.use.https>
-->
</systemPropertyVariables>
</configuration>
</plugin>
----

(and similarly `maven.failsafe.plugin` when testing in native image).

Prepare the REST test endpoint, set `application.properties`, for example:

[source, properties]
----
# keycloak.url is set by KeycloakTestResourceLifecycleManager
quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus/
quarkus.oidc.client-id=quarkus-service-app
quarkus.oidc.credentials=secret
quarkus.oidc.application-type=service
----

and finally write the test code, for example:

[source, java]
----
import static io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager.getAccessToken;
import static org.hamcrest.Matchers.equalTo;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager;
import io.restassured.RestAssured;

@QuarkusTest
@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class)
public class BearerTokenAuthorizationTest {

@Test
public void testBearerToken() {
RestAssured.given().auth().oauth2(getAccessToken("alice"))))
.when().get("/api/users/preferredUserName")
.then()
.statusCode(200)
// the test endpoint returns the name extracted from the injected SecurityIdentity Principal
.body("userName", equalTo("alice"));
}

}
----

`KeycloakTestResourceLifecycleManager` registers `alice` and `admin` users. The user `alice` has the `user` role only by default - it can be customized with a `keycloak.token.user-roles` system property. The user `admin` has the `user` and `admin` roles by default - it can be customized with a `keycloak.token.admin-roles` system property.

By default, `KeycloakTestResourceLifecycleManager` uses HTTPS to initialize a Keycloak instance which can be disabled with `keycloak.use.https=false`.
Default realm name is `quarkus` and client id - `quarkus-service-app` - set `keycloak.realm` and `keycloak.service.client` system properties to customize the values if needed.

=== Local Public Key

You can also use a local inlined public key for testing your `quarkus-oidc` `service` applications:
Expand All @@ -532,6 +628,10 @@ copy `privateKey.pem` from the `integration-tests/oidc-tenancy` in the `main` Qu

This approach provides a more limited coverage compared to the Wiremock approach - for example, the remote communication code is not covered.

=== TestSecurity annotation

Please see link:security-testing#testing-security[TestingSecurity Annotation] section how to do simple tests with the `TestSecurity` annotation.

== References

* https://www.keycloak.org/documentation.html[Keycloak Documentation]
Expand Down
Loading