Skip to content

Commit

Permalink
Support for combining OIDC with other auth mechanisms
Browse files Browse the repository at this point in the history
  • Loading branch information
sberyozkin committed Sep 7, 2020
1 parent 948d8a3 commit c5e1538
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 12 deletions.
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/security-testing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions docs/src/main/asciidoc/security.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +25,7 @@
@Alternative
@RequestScoped
public class OidcJsonWebTokenProducer {
private static final Logger LOG = Logger.getLogger(OidcJsonWebTokenProducer.class);

@Inject
SecurityIdentity identity;
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -13,7 +15,7 @@

@RequestScoped
public class OidcTokenCredentialProducer {

private static final Logger LOG = Logger.getLogger(OidcTokenCredentialProducer.class);
@Inject
SecurityIdentity identity;

Expand All @@ -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;
}

/**
Expand Down
5 changes: 4 additions & 1 deletion integration-tests/oidc/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,11 @@
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>

<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-elytron-security-properties-file-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-oidc-deployment</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -11,29 +9,31 @@

import org.eclipse.microprofile.jwt.JsonWebToken;

import io.quarkus.security.identity.SecurityIdentity;

/**
* @author <a href="mailto:[email protected]">Pedro Igor</a>
*/
@Path("/api/users")
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
@Path("/preferredUserName")
@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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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")) {
Expand Down

0 comments on commit c5e1538

Please sign in to comment.