Skip to content

Commit

Permalink
Inject OidcConfigurationMetadata
Browse files Browse the repository at this point in the history
  • Loading branch information
sberyozkin committed Mar 11, 2021
1 parent f212906 commit bdaa7f3
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import io.quarkus.oidc.runtime.OidcAuthenticationMechanism;
import io.quarkus.oidc.runtime.OidcBuildTimeConfig;
import io.quarkus.oidc.runtime.OidcConfig;
import io.quarkus.oidc.runtime.OidcConfigurationMetadataProducer;
import io.quarkus.oidc.runtime.OidcIdentityProvider;
import io.quarkus.oidc.runtime.OidcJsonWebTokenProducer;
import io.quarkus.oidc.runtime.OidcRecorder;
Expand Down Expand Up @@ -72,6 +73,7 @@ public void additionalBeans(BuildProducer<AdditionalBeanBuildItem> additionalBea
builder.addBeanClass(OidcAuthenticationMechanism.class)
.addBeanClass(OidcJsonWebTokenProducer.class)
.addBeanClass(OidcTokenCredentialProducer.class)
.addBeanClass(OidcConfigurationMetadataProducer.class)
.addBeanClass(OidcIdentityProvider.class)
.addBeanClass(DefaultTenantConfigResolver.class)
.addBeanClass(DefaultTokenStateManager.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.quarkus.oidc;

import java.util.Collections;
import java.util.Set;

import io.vertx.core.json.JsonObject;

public class OidcConfigurationMetadata {
Expand All @@ -18,6 +21,7 @@ public class OidcConfigurationMetadata {
private final String userInfoUri;
private final String endSessionUri;
private final String issuer;
private JsonObject json;

public OidcConfigurationMetadata(String tokenUri,
String introspectionUri,
Expand All @@ -43,6 +47,7 @@ public OidcConfigurationMetadata(JsonObject wellKnownConfig) {
this.userInfoUri = wellKnownConfig.getString(USERINFO_ENDPOINT);
this.endSessionUri = wellKnownConfig.getString(END_SESSION_ENDPOINT);
this.issuer = wellKnownConfig.getString(ISSUER);
this.json = wellKnownConfig;
}

public String getTokenUri() {
Expand Down Expand Up @@ -72,4 +77,16 @@ public String getEndSessionUri() {
public String getIssuer() {
return issuer;
}

public String get(String propertyName) {
return json != null ? null : json.getString(propertyName);
}

public boolean contains(String propertyName) {
return json.containsKey(propertyName);
}

public Set<String> getPropertyNames() {
return Collections.unmodifiableSet(json.fieldNames());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ public Uni<SecurityIdentity> apply(TokenVerificationResult result, Throwable t)
JsonObject rolesJson = getRolesJson(vertxContext, resolvedContext, tokenCred, tokenJson,
userInfo);
SecurityIdentity securityIdentity = validateAndCreateIdentity(vertxContext, tokenCred,
resolvedContext.oidcConfig,
tokenJson, rolesJson, userInfo);
resolvedContext, tokenJson, rolesJson, userInfo);
if (tokenAutoRefreshPrepared(tokenJson, vertxContext, resolvedContext.oidcConfig)) {
return Uni.createFrom().failure(new TokenAutoRefreshException(securityIdentity));
} else {
Expand All @@ -169,9 +168,7 @@ public Uni<SecurityIdentity> apply(TokenVerificationResult result, Throwable t)
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder();
builder.addCredential(tokenCred);
OidcUtils.setSecurityIdentityUserInfo(builder, userInfo);

// getRolesJson: make sure the introspection is picked up correctly
// OidcRuntimeClient.verifyCodeToken - set the introspection there - which may be ambiguous
OidcUtils.setSecurityIdentityConfigMetadata(builder, resolvedContext);
if (result.introspectionResult.containsKey(OidcConstants.INTROSPECTION_TOKEN_USERNAME)) {
final String userName = result.introspectionResult
.getString(OidcConstants.INTROSPECTION_TOKEN_USERNAME);
Expand Down Expand Up @@ -289,7 +286,7 @@ private static Uni<SecurityIdentity> validateTokenWithoutOidcServer(TokenAuthent
try {
TokenVerificationResult result = resolvedContext.provider.verifyJwtToken(request.getToken().getToken());
return Uni.createFrom()
.item(validateAndCreateIdentity(null, request.getToken(), resolvedContext.oidcConfig,
.item(validateAndCreateIdentity(null, request.getToken(), resolvedContext,
result.localVerificationResult, result.localVerificationResult, null));
} catch (Throwable t) {
return Uni.createFrom().failure(new AuthenticationFailedException(t));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ public class OidcProvider {

private static final Logger LOG = Logger.getLogger(OidcProvider.class);

private final OidcProviderClient client;
private final RefreshableVerificationKeyResolver keyResolver;
private final OidcTenantConfig oidcConfig;
final OidcProviderClient client;
final RefreshableVerificationKeyResolver keyResolver;
final OidcTenantConfig oidcConfig;

public OidcProvider(OidcProviderClient client, OidcTenantConfig oidcConfig, JsonWebKeyCache jwks) {
this.client = client;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ RefreshToken currentRefreshToken() {
@Produces
@RequestScoped
UserInfo currentUserInfo() {
UserInfo userInfo = (UserInfo) identity.getAttribute("userinfo");
UserInfo userInfo = (UserInfo) identity.getAttribute(OidcUtils.USER_INFO_ATTRIBUTE);
if (userInfo == null) {
throw new OIDCException("UserInfo can not be injected");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import io.vertx.ext.web.RoutingContext;

public final class OidcUtils {
static final String CONFIG_METADATA_ATTRIBUTE = "configuration-metadata";
static final String USER_INFO_ATTRIBUTE = "userinfo";
static final String TENANT_ID_ATTRIBUTE = "tenant-id";
/**
* This pattern uses a positive lookahead to split an expression around the forward slashes
* ignoring those which are located inside a pair of the double quotes.
Expand Down Expand Up @@ -133,8 +136,9 @@ private static List<String> convertJsonArrayToList(JsonArray claimValue) {

static QuarkusSecurityIdentity validateAndCreateIdentity(
RoutingContext vertxContext, TokenCredential credential,
OidcTenantConfig config, JsonObject tokenJson, JsonObject rolesJson, JsonObject userInfo) {
TenantConfigContext resolvedContext, JsonObject tokenJson, JsonObject rolesJson, JsonObject userInfo) {

OidcTenantConfig config = resolvedContext.oidcConfig;
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder();
builder.addCredential(credential);

Expand All @@ -150,6 +154,7 @@ static QuarkusSecurityIdentity validateAndCreateIdentity(
builder.setPrincipal(jwtPrincipal);
setSecurityIdentityRoles(builder, config, rolesJson);
setSecurityIdentityUserInfo(builder, userInfo);
setSecurityIdentityConfigMetadata(builder, resolvedContext);
setBlockinApiAttribute(builder, vertxContext);
setTenantIdAttribute(builder, config);
return builder.build();
Expand All @@ -175,12 +180,19 @@ public static void setBlockinApiAttribute(QuarkusSecurityIdentity.Builder builde
}

public static void setTenantIdAttribute(QuarkusSecurityIdentity.Builder builder, OidcTenantConfig config) {
builder.addAttribute("tenant-id", config.tenantId.orElse("Default"));
builder.addAttribute(TENANT_ID_ATTRIBUTE, config.tenantId.orElse("Default"));
}

public static void setSecurityIdentityUserInfo(QuarkusSecurityIdentity.Builder builder, JsonObject userInfo) {
if (userInfo != null) {
builder.addAttribute("userinfo", new UserInfo(userInfo.encode()));
builder.addAttribute(USER_INFO_ATTRIBUTE, new UserInfo(userInfo.encode()));
}
}

public static void setSecurityIdentityConfigMetadata(QuarkusSecurityIdentity.Builder builder,
TenantConfigContext resolvedContext) {
if (resolvedContext.provider.client != null) {
builder.addAttribute(CONFIG_METADATA_ATTRIBUTE, resolvedContext.provider.client.getMetadata());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.quarkus.oidc.IdToken;
import io.quarkus.oidc.IdTokenCredential;
import io.quarkus.oidc.OIDCException;
import io.quarkus.oidc.OidcConfigurationMetadata;
import io.quarkus.oidc.RefreshToken;
import io.quarkus.security.Authenticated;
import io.quarkus.security.identity.SecurityIdentity;
Expand All @@ -26,6 +27,9 @@ public class ProtectedResource {
@Inject
SecurityIdentity identity;

@Inject
OidcConfigurationMetadata configMetadata;

@Inject
@IdToken
JsonWebToken idToken;
Expand All @@ -51,6 +55,12 @@ public String hello() {
return securityContext.getUserPrincipal().getName();
}

@GET
@Path("configMetadataIssuer")
public String configMetadataIssuer() {
return configMetadata.getIssuer();
}

@GET
public String getName() {
if (!idTokenCredential.getToken().equals(idToken.getRawToken())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ public void testCodeFlowNoConsent() throws IOException {

assertEquals("Welcome to Test App", page.getTitleText(),
"A second request should not redirect and just re-authenticate the user");

page = webClient.getPage("http://localhost:8081/web-app/configMetadataIssuer");

assertEquals(
KeycloakRealmResourceManager.KEYCLOAK_SERVER_URL + "/realms/" + KeycloakRealmResourceManager.KEYCLOAK_REALM,
page.asText());

webClient.getCookieManager().clearCookies();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@

public class KeycloakRealmResourceManager implements QuarkusTestResourceLifecycleManager {

private static final String KEYCLOAK_SERVER_URL = System.getProperty("keycloak.url", "http://localhost:8180/auth");
private static final String KEYCLOAK_REALM = "quarkus";
public static final String KEYCLOAK_SERVER_URL = System.getProperty("keycloak.url", "http://localhost:8180/auth");
public static final String KEYCLOAK_REALM = "quarkus";
private List<RealmRepresentation> realms = new ArrayList<>();

@Override
Expand Down

0 comments on commit bdaa7f3

Please sign in to comment.