diff --git a/docs/src/main/asciidoc/security-testing.adoc b/docs/src/main/asciidoc/security-testing.adoc
index 31c92f70d360e..4726cf34b4bf3 100644
--- a/docs/src/main/asciidoc/security-testing.adoc
+++ b/docs/src/main/asciidoc/security-testing.adoc
@@ -9,6 +9,7 @@ include::./attributes.adoc[]
This document describes how to test Quarkus Security.
+[[configuring-user-information]]
== Configuring User Information
You can use link:security-properties[quarkus-elytron-security-properties-file] for testing security. This supports both embedding user info in `application.properties` and standalone properties files.
diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc
index 6a1cd600fc200..e45a19b21d801 100644
--- a/docs/src/main/asciidoc/security.adoc
+++ b/docs/src/main/asciidoc/security.adoc
@@ -96,6 +96,7 @@ For example, `quarkus-oidc` uses its own `IdentityProvider` to convert a token t
If you use `Basic` or `Form` HTTP-based authentication then you have to add an `IdentityProvider` which can convert a user name and password to `SecurityIdentity`.
See link:security-jpa[JPA IdentityProvider] and link:security-jdbc[JDBC IdentityProvider] for more information.
+You can also use link:security-testing#configuring-user-information[User Properties IdentityProvider] for testing.
== Combining Authentication Mechanisms
diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java
index 6df9def69bb99..34dfbefb2f183 100644
--- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java
+++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcJsonWebTokenProducer.java
@@ -8,6 +8,7 @@
import org.eclipse.microprofile.jwt.Claims;
import org.eclipse.microprofile.jwt.JsonWebToken;
+import org.jboss.logging.Logger;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
@@ -24,6 +25,7 @@
@Alternative
@RequestScoped
public class OidcJsonWebTokenProducer {
+ private static final Logger LOG = Logger.getLogger(OidcJsonWebTokenProducer.class);
@Inject
SecurityIdentity identity;
@@ -77,6 +79,7 @@ private JsonWebToken getTokenCredential(Class extends TokenCredential> type) {
return new OidcJwtCallerPrincipal(jwtClaims, credential);
}
String tokenType = type == AccessTokenCredential.class ? "access" : "ID";
- throw new OIDCException("Current identity is not associated with an " + tokenType + " token");
+ LOG.tracef("Current identity is not associated with an %s token", tokenType);
+ return new NullJsonWebToken();
}
}
diff --git a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTokenCredentialProducer.java b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTokenCredentialProducer.java
index f29e5f85b622f..2ee0952e4880d 100644
--- a/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTokenCredentialProducer.java
+++ b/extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcTokenCredentialProducer.java
@@ -4,6 +4,8 @@
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
+import org.jboss.logging.Logger;
+
import io.quarkus.oidc.AccessTokenCredential;
import io.quarkus.oidc.IdTokenCredential;
import io.quarkus.oidc.OIDCException;
@@ -13,7 +15,7 @@
@RequestScoped
public class OidcTokenCredentialProducer {
-
+ private static final Logger LOG = Logger.getLogger(OidcTokenCredentialProducer.class);
@Inject
SecurityIdentity identity;
@@ -25,19 +27,34 @@ public class OidcTokenCredentialProducer {
@Produces
@RequestScoped
IdTokenCredential currentIdToken() {
- return identity.getCredential(IdTokenCredential.class);
+ IdTokenCredential cred = identity.getCredential(IdTokenCredential.class);
+ if (cred == null) {
+ LOG.trace("IdTokenCredential is null");
+ cred = new IdTokenCredential();
+ }
+ return cred;
}
@Produces
@RequestScoped
AccessTokenCredential currentAccessToken() {
- return identity.getCredential(AccessTokenCredential.class);
+ AccessTokenCredential cred = identity.getCredential(AccessTokenCredential.class);
+ if (cred == null) {
+ LOG.trace("AccessTokenCredential is null");
+ cred = new AccessTokenCredential();
+ }
+ return cred;
}
@Produces
@RequestScoped
RefreshToken currentRefreshToken() {
- return identity.getCredential(RefreshToken.class);
+ RefreshToken cred = identity.getCredential(RefreshToken.class);
+ if (cred == null) {
+ LOG.trace("RefreshToken is null");
+ cred = new RefreshToken();
+ }
+ return cred;
}
/**
diff --git a/integration-tests/oidc/pom.xml b/integration-tests/oidc/pom.xml
index 2e4079086fab8..c616f218a9207 100644
--- a/integration-tests/oidc/pom.xml
+++ b/integration-tests/oidc/pom.xml
@@ -53,8 +53,11 @@
awaitility
test
-
+
+ io.quarkus
+ quarkus-elytron-security-properties-file-deployment
+
io.quarkus
quarkus-oidc-deployment
diff --git a/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/UsersResource.java b/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/UsersResource.java
index 14bead7f636a6..2ea179a688153 100644
--- a/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/UsersResource.java
+++ b/integration-tests/oidc/src/main/java/io/quarkus/it/keycloak/UsersResource.java
@@ -1,7 +1,5 @@
package io.quarkus.it.keycloak;
-import java.security.Principal;
-
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.ws.rs.GET;
@@ -11,6 +9,8 @@
import org.eclipse.microprofile.jwt.JsonWebToken;
+import io.quarkus.security.identity.SecurityIdentity;
+
/**
* @author Pedro Igor
*/
@@ -18,14 +18,14 @@
public class UsersResource {
@Inject
- Principal identity;
+ SecurityIdentity identity;
@GET
@Path("/me")
@RolesAllowed("user")
@Produces(MediaType.APPLICATION_JSON)
public User principalName() {
- return new User(identity.getName());
+ return new User(identity.getPrincipal().getName());
}
@GET
@@ -33,7 +33,7 @@ public User principalName() {
@RolesAllowed("user")
@Produces(MediaType.APPLICATION_JSON)
public User preferredUserName() {
- return new User(((JsonWebToken) identity).getClaim("preferred_username"));
+ return new User(((JsonWebToken) identity.getPrincipal()).getClaim("preferred_username"));
}
public static class User {
diff --git a/integration-tests/oidc/src/main/resources/application.properties b/integration-tests/oidc/src/main/resources/application.properties
index 423e0a93117e1..2a425123ef58e 100644
--- a/integration-tests/oidc/src/main/resources/application.properties
+++ b/integration-tests/oidc/src/main/resources/application.properties
@@ -3,5 +3,11 @@ quarkus.oidc.auth-server-url=${keycloak.ssl.url}/realms/quarkus/
quarkus.oidc.client-id=quarkus-app
quarkus.oidc.credentials.secret=secret
quarkus.oidc.token.principal-claim=email
-quarkus.http.cors=true
quarkus.oidc.tls.verification=none
+quarkus.http.cors=true
+
+quarkus.http.auth.basic=true
+quarkus.security.users.embedded.enabled=true
+quarkus.security.users.embedded.plain-text=true
+quarkus.security.users.embedded.users.alice=password
+quarkus.security.users.embedded.roles.alice=user
diff --git a/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
index d3f0e8e6fd3f0..a6e266bddf58b 100644
--- a/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
+++ b/integration-tests/oidc/src/test/java/io/quarkus/it/keycloak/BearerTokenAuthorizationTest.java
@@ -5,7 +5,9 @@
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.equalTo;
+import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.Base64;
import java.util.concurrent.TimeUnit;
import org.hamcrest.Matchers;
@@ -58,6 +60,17 @@ public void testSecureAccessSuccessCustomPrincipal() {
}
}
+ @Test
+ public void testBasicAuth() {
+ byte[] basicAuthBytes = "alice:password".getBytes(StandardCharsets.UTF_8);
+ RestAssured.given()
+ .header("Authorization", "Basic " + Base64.getEncoder().encodeToString(basicAuthBytes))
+ .when().get("/api/users/me")
+ .then()
+ .statusCode(200)
+ .body("userName", equalTo("alice"));
+ }
+
@Test
public void testSecureAccessSuccessPreferredUsername() {
for (String username : Arrays.asList("alice", "jdoe", "admin")) {