Skip to content

Commit

Permalink
Merge pull request #36609 from sberyozkin/oidc_custom_identity_providers
Browse files Browse the repository at this point in the history
Add a custom OIDC identity provider test
  • Loading branch information
sberyozkin committed Oct 22, 2023
2 parents 2b26502 + cfc5fb2 commit dff07fa
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.quarkus.oidc.test;

import java.security.Principal;

import jakarta.annotation.Priority;
import jakarta.enterprise.context.ApplicationScoped;

import org.eclipse.microprofile.jwt.Claims;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.InvalidJwtException;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;

import io.quarkus.oidc.runtime.OidcJwtCallerPrincipal;
import io.quarkus.security.AuthenticationCompletionException;
import io.quarkus.security.AuthenticationFailedException;
import io.quarkus.security.credential.TokenCredential;
import io.quarkus.security.identity.AuthenticationRequestContext;
import io.quarkus.security.identity.IdentityProvider;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.security.identity.request.TokenAuthenticationRequest;
import io.quarkus.security.runtime.QuarkusSecurityIdentity;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
@Priority(1)
public class CustomIdentityProvider implements IdentityProvider<TokenAuthenticationRequest> {

@Override
public Class<TokenAuthenticationRequest> getRequestType() {
return TokenAuthenticationRequest.class;
}

@Override
public Uni<SecurityIdentity> authenticate(TokenAuthenticationRequest request, AuthenticationRequestContext context) {
QuarkusSecurityIdentity.Builder builder = QuarkusSecurityIdentity.builder();

TokenCredential credential = request.getToken();
try {
JwtClaims jwtClaims = new JwtConsumerBuilder()
.setSkipSignatureVerification()
.setSkipAllValidators()
.build().processToClaims(credential.getToken());
jwtClaims.setClaim(Claims.raw_token.name(), credential.getToken());

Principal principal = new OidcJwtCallerPrincipal(jwtClaims, credential);
if ("jdoe".equals(principal.getName())) {
throw new AuthenticationCompletionException();
}
builder.setPrincipal(principal);
} catch (InvalidJwtException e) {
throw new AuthenticationFailedException(e);
}

return Uni.createFrom().item(builder.build());

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package io.quarkus.oidc.test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;

import java.io.IOException;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.SilentCssErrorHandler;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;

import io.quarkus.test.QuarkusDevModeTest;
import io.quarkus.test.common.QuarkusTestResource;
import io.quarkus.test.keycloak.server.KeycloakTestResourceLifecycleManager;

@QuarkusTestResource(KeycloakTestResourceLifecycleManager.class)
public class CustomIdentityProviderTestCase {

private static Class<?>[] testClasses = {
ProtectedResource.class,
CustomIdentityProvider.class
};

@RegisterExtension
static final QuarkusDevModeTest test = new QuarkusDevModeTest()
.withApplicationRoot((jar) -> jar
.addClasses(testClasses)
.addAsResource("application-introspection-disabled.properties", "application.properties"));

@Test
public void testCustomIdentityProviderSuccess() throws IOException, InterruptedException {

try (final WebClient webClient = createWebClient()) {
HtmlPage page = webClient.getPage("http://localhost:8080/protected");

assertEquals("Sign in to quarkus", page.getTitleText());

HtmlForm loginForm = page.getForms().get(0);

loginForm.getInputByName("username").setValueAttribute("alice");
loginForm.getInputByName("password").setValueAttribute("alice");

page = loginForm.getInputByName("login").click();

assertEquals("alice", page.getBody().asNormalizedText());

webClient.getCookieManager().clearCookies();
}

}

@Test
public void testCustomIdentityProviderFailure() throws IOException, InterruptedException {

try (final WebClient webClient = createWebClient()) {
HtmlPage page = webClient.getPage("http://localhost:8080/protected");

assertEquals("Sign in to quarkus", page.getTitleText());

HtmlForm loginForm = page.getForms().get(0);

loginForm.getInputByName("username").setValueAttribute("jdoe");
loginForm.getInputByName("password").setValueAttribute("jdoe");

try {
loginForm.getInputByName("login").click();
fail("Exception is expected because `jdoe` is not allowed to access the service");
} catch (FailingHttpStatusCodeException ex) {
assertEquals(401, ex.getStatusCode());
}

webClient.getCookieManager().clearCookies();
}

}

private WebClient createWebClient() {
WebClient webClient = new WebClient();
webClient.setCssErrorHandler(new SilentCssErrorHandler());
return webClient;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
quarkus.oidc.auth-server-url=${keycloak.url}/realms/quarkus
quarkus.oidc.application-type=web-app
quarkus.oidc.client-id=quarkus-web-app
quarkus.oidc.credentials.secret=secret

#It blocks an attempt to fetch JWK set at the start up
quarkus.oidc.token.require-jwt-introspection-only=true

0 comments on commit dff07fa

Please sign in to comment.