-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improve OIDC bearer token concept doc
- Loading branch information
1 parent
af6d3d7
commit 39b509a
Showing
1 changed file
with
65 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,6 +108,27 @@ SecurityIdentity roles can be mapped from the verified JWT access tokens as foll | |
* If `groups` claim is available then its value is used | ||
* If `realm_access/roles` or `resource_access/client_id/roles` (where `client_id` is the value of the `quarkus.oidc.client-id` property) claim is available then its value is used. | ||
This check supports the tokens issued by Keycloak | ||
|
||
For example, the following JWT token has a complex `groups` claim which contains an array `roles` containing roles: | ||
Check warning on line 112 in docs/src/main/asciidoc/security-oidc-bearer-token-authentication.adoc GitHub Actions / Linting with Vale
|
||
|
||
[source,json] | ||
---- | ||
{ | ||
"iss": "https://server.example.com", | ||
"sub": "24400320", | ||
"upn": "[email protected]", | ||
"preferred_username": "jdoe", | ||
"exp": 1311281970, | ||
"iat": 1311280970, | ||
"groups": { | ||
"roles": [ | ||
"microprofile_jwt_user" | ||
], | ||
} | ||
} | ||
---- | ||
|
||
`microprofile_jwt_user` role has to be mapped to SecurityIdentity roles and you can do it with this configuration: `quarkus.oidc.roles.role-claim-path=groups/roles`. | ||
|
||
If the token is opaque (binary) then a `scope` property from the remote token introspection response will be used. | ||
|
||
|
@@ -123,6 +144,7 @@ SecurityIdentity permissions are mapped in the form of the `io.quarkus.security. | |
|
||
[source, java] | ||
---- | ||
import java.util.List; | ||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
|
@@ -152,6 +174,20 @@ public class ProtectedResource { | |
return List.of(new Order(1)); | ||
} | ||
public static class Order { | ||
String id; | ||
public Order() { | ||
} | ||
public Order(String id) { | ||
this.id = id; | ||
} | ||
public String getId() { | ||
return id; | ||
} | ||
public void setId() { | ||
this.id = id; | ||
} | ||
} | ||
} | ||
---- | ||
<1> Only requests with OpenID Connect scope `email` are going to be granted access. | ||
|
@@ -262,6 +298,7 @@ In such cases you may want to consider skipping the issuer verification by setti | |
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.container.ContainerRequestContext; | ||
import jakarta.ws.rs.container.ContainerRequestFilter; | ||
import jakarta.ws.rs.core.Response; | ||
import jakarta.ws.rs.ext.Provider; | ||
import org.eclipse.microprofile.jwt.JsonWebToken; | ||
|
@@ -739,7 +776,6 @@ and finally write the test code, for example: | |
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; | ||
|
@@ -753,7 +789,7 @@ public class BearerTokenAuthorizationTest { | |
@Test | ||
public void testBearerToken() { | ||
RestAssured.given().auth().oauth2(getAccessToken("alice")))) | ||
RestAssured.given().auth().oauth2(getAccessToken("alice")) | ||
.when().get("/api/users/preferredUserName") | ||
.then() | ||
.statusCode(200) | ||
|
@@ -782,7 +818,7 @@ quarkus.oidc.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlivFI8qB4D0y | |
smallrye.jwt.sign.key.location=/privateKey.pem | ||
---- | ||
|
||
copy `privateKey.pem` from the `integration-tests/oidc-tenancy` in the `main` Quarkus repository and use a test code similar to the one in the `Wiremock` section above to generate JWT tokens. You can use your own test keys if preferred. | ||
copy link:https://github.com/quarkusio/quarkus/tree/main/integration-tests/oidc-tenancy/src/main/resources/privateKey.pem[privateKey.pem] from the `integration-tests/oidc-tenancy` in the `main` Quarkus repository and use a test code similar to the one in the `Wiremock` section above to generate JWT tokens. You can use your own test keys if preferred. | ||
|
||
This approach provides a more limited coverage compared to the Wiremock approach - for example, the remote communication code is not covered. | ||
|
||
|
@@ -857,8 +893,14 @@ where `ProtectedResource` class may look like this: | |
|
||
[source, java] | ||
---- | ||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import io.quarkus.oidc.OidcConfigurationMetadata; | ||
import io.quarkus.oidc.UserInfo; | ||
import io.quarkus.security.Authenticated; | ||
import org.eclipse.microprofile.jwt.JsonWebToken; | ||
@Path("/service") | ||
|
@@ -929,7 +971,12 @@ where `ProtectedResource` class may look like this: | |
|
||
[source, java] | ||
---- | ||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.GET; | ||
import jakarta.ws.rs.Path; | ||
import io.quarkus.oidc.TokenIntrospection; | ||
import io.quarkus.security.Authenticated; | ||
import io.quarkus.security.identity.SecurityIdentity; | ||
@Path("/service") | ||
|
@@ -1040,6 +1087,7 @@ package org.acme.quickstart.oidc; | |
import static jakarta.ws.rs.core.HttpHeaders.AUTHORIZATION; | ||
import jakarta.inject.Inject; | ||
import jakarta.ws.rs.HeaderParam; | ||
import jakarta.ws.rs.POST; | ||
import jakarta.ws.rs.Path; | ||
import io.vertx.core.eventbus.EventBus; | ||
|
@@ -1053,7 +1101,18 @@ public class OrderResource { | |
@POST | ||
public void order(String product, @HeaderParam(AUTHORIZATION) String bearer) { | ||
String rawToken = bearer.substring("Bearer ".length()); <1> | ||
eventBus.publish("product-order", new Product(product, 1, rawToken)); | ||
eventBus.publish("product-order", new Product(product, rawToken)); | ||
} | ||
public static class Product { | ||
public String product; | ||
public String customerAccessToken; | ||
public Product() { | ||
} | ||
public Product(String product, String customerAccessToken) { | ||
this.product = product; | ||
this.customerAccessToken = customerAccessToken; | ||
} | ||
} | ||
} | ||
---- | ||
|
@@ -1086,9 +1145,8 @@ public class OrderService { | |
@Blocking | ||
@ConsumeEvent("product-order") | ||
void processOrder(Product product) { | ||
String rawToken = product.customerAccessToken; | ||
AccessTokenCredential token = new AccessTokenCredential(rawToken); | ||
SecurityIdentity = identityProvider.authenticate(token).await().indefinitely(); <2> | ||
AccessTokenCredential tokenCredential = new AccessTokenCredential(product.customerAccessToken); | ||
SecurityIdentity securityIdentity = identityProvider.authenticate(tokenCredential).await().indefinitely(); <2> | ||
... | ||
} | ||
|
@@ -1120,7 +1178,6 @@ For more information, see xref:security-code-flow-authentication#oidc-request-fi | |
|
||
* xref:security-oidc-configuration-properties-reference.adoc[OIDC configuration properties] | ||
* xref:security-oidc-bearer-token-authentication-tutorial.adoc[Protect a service application by using OIDC Bearer token authentication] | ||
* xref:security-protect-service-applications-by-using-oidc-bearer-authentication-how-to.adoc[Protect service applications by using OIDC Bearer token authentication] | ||
* https://www.keycloak.org/documentation.html[Keycloak Documentation] | ||
* https://openid.net/connect/[OpenID Connect] | ||
* https://tools.ietf.org/html/rfc7519[JSON Web Token] | ||
|