From 944c0b663340a6cea517d0ee1542fbb6ad4c94e6 Mon Sep 17 00:00:00 2001 From: Matthias Andreas Benkard Date: Sun, 2 Feb 2020 13:17:48 +0100 Subject: Remove the JWT cookie filters again. They were pointless: quarkus-oidc's CodeAuthenticationMechanism already takes care of setting a session cookie, which it uses in preference over an IdP redirect. The reason the cookie did not stick before is still unclear, but it was fixed by tweaking the Keycloak settings for the MulkCMS client. Change-Id: Ie547ee0af23b6532515a990c2699ba9ffa686a5a --- .../authentication/JwtCookieLoginFilter.java | 184 --------------------- .../authentication/JwtCookieSetterFilter.java | 118 ------------- 2 files changed, 302 deletions(-) delete mode 100644 src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java delete mode 100644 src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java (limited to 'src/main/java') diff --git a/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java b/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java deleted file mode 100644 index 53903f7..0000000 --- a/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java +++ /dev/null @@ -1,184 +0,0 @@ -package eu.mulk.mulkcms2.authentication; - -import io.quarkus.security.credential.Credential; -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.smallrye.jwt.runtime.auth.JWTAuthMechanism; -import io.smallrye.jwt.auth.principal.DefaultJWTCallerPrincipal; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.Permission; -import java.security.Principal; -import java.security.PublicKey; -import java.security.cert.CertificateException; -import java.time.Duration; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import javax.annotation.PostConstruct; -import javax.enterprise.context.ApplicationScoped; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.jose4j.jwa.AlgorithmConstraints; -import org.jose4j.jws.AlgorithmIdentifiers; -import org.jose4j.jwt.MalformedClaimException; -import org.jose4j.jwt.consumer.InvalidJwtException; -import org.jose4j.jwt.consumer.JwtConsumerBuilder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Interprets a possibly present JWT cookie and uses it to authenticate the user. - * - *

JWT cookies are used to authenticate further requests based on initial authentication. This - * way, there is no need to route the user through an OpenID Connect IdP on each request, for - * example. - * - * @see JWTAuthMechanism - * @see JwtCookieSetterFilter - */ -@ApplicationScoped -public class JwtCookieLoginFilter implements IdentityProvider { - - @ConfigProperty(name = "mulkcms.jwt.signing-key") - String signingKeyAlias; - - @ConfigProperty(name = "mulkcms.jwt.keystore.file") - String signingKeyFile; - - @ConfigProperty(name = "mulkcms.jwt.keystore.passphrase") - String signingKeyPassphrase; - - @ConfigProperty(name = "mulkcms.jwt.issuer") - String issuer; - - @ConfigProperty(name = "mulkcms.jwt.validity") - Duration validity; - - private static final Logger log = LoggerFactory.getLogger(JwtCookieLoginFilter.class); - - private PublicKey signingKey; - - @PostConstruct - public void postCostruct() - throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException { - log.info("Hello!"); - try (var is = new FileInputStream(signingKeyFile)) { - var keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(is, signingKeyPassphrase.toCharArray()); - signingKey = keystore.getCertificate(signingKeyAlias).getPublicKey(); - Objects.requireNonNull(signingKey); - } - } - - @Override - public CompletionStage authenticate( - TokenAuthenticationRequest request, AuthenticationRequestContext context) { - - log.info("Starting JWT verification."); - - return context.runBlocking( - () -> { - try { - log.info("JWT verification started."); - - /* - AbstractBearerTokenExtractor extractor = - new BearerTokenExtractor(requestContext, authContextInfo); - String bearerToken = extractor.getBearerToken(); - */ - - // FIXME: But how does this know how the token is extracted? What passes it here? - // Look up JWTAuthMechanism. - var bearerToken = request.getToken().getToken(); - - var jwtConsumer = - new JwtConsumerBuilder() - .setJwsAlgorithmConstraints( - new AlgorithmConstraints( - AlgorithmConstraints.ConstraintType.WHITELIST, - AlgorithmIdentifiers.RSA_USING_SHA256, - AlgorithmIdentifiers.RSA_USING_SHA384, - AlgorithmIdentifiers.RSA_USING_SHA512, - AlgorithmIdentifiers.ECDSA_USING_P256_CURVE_AND_SHA256, - AlgorithmIdentifiers.ECDSA_USING_P384_CURVE_AND_SHA384, - AlgorithmIdentifiers.ECDSA_USING_P521_CURVE_AND_SHA512)) - .setVerificationKey(signingKey) - .setRequireExpirationTime() - .setAllowedClockSkewInSeconds(60) - .build(); - - var claims = jwtConsumer.process(bearerToken).getJwtClaims(); - claims.getSubject(); - - var jwtPrincipal = new DefaultJWTCallerPrincipal(claims); - log.info("JWT verified: {}", jwtPrincipal); - - return new CookieIdentity(jwtPrincipal); - } catch (InvalidJwtException | MalformedClaimException e) { - log.info("JWT verification failed", e); - return null; - } - }); - } - - @Override - public Class getRequestType() { - return TokenAuthenticationRequest.class; - } - - private static class CookieIdentity implements SecurityIdentity { - - private Principal jwtPrincipal; - - private CookieIdentity(Principal jwtPrincipal) { - this.jwtPrincipal = jwtPrincipal; - } - - @Override - public Principal getPrincipal() { - return jwtPrincipal; - } - - @Override - public boolean isAnonymous() { - return false; - } - - @Override - public Set getRoles() { - return Set.of(); - } - - @Override - public T getCredential(Class credentialType) { - return null; - } - - @Override - public Set getCredentials() { - return Set.of(); - } - - @Override - public T getAttribute(String name) { - return null; - } - - @Override - public Map getAttributes() { - return Map.of(); - } - - @Override - public CompletionStage checkPermission(Permission permission) { - return CompletableFuture.completedFuture(false); - } - } -} diff --git a/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java b/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java deleted file mode 100644 index baa51d4..0000000 --- a/src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java +++ /dev/null @@ -1,118 +0,0 @@ -package eu.mulk.mulkcms2.authentication; - -import io.quarkus.security.identity.SecurityIdentity; -import io.smallrye.jwt.build.Jwt; -import java.io.FileInputStream; -import java.io.IOException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.time.Duration; -import javax.annotation.PostConstruct; -import javax.annotation.Priority; -import javax.inject.Inject; -import javax.ws.rs.container.ContainerRequestContext; -import javax.ws.rs.container.ContainerResponseContext; -import javax.ws.rs.container.ContainerResponseFilter; -import javax.ws.rs.core.NewCookie; -import javax.ws.rs.ext.Provider; -import org.eclipse.microprofile.config.inject.ConfigProperty; -import org.eclipse.microprofile.jwt.Claims; -import org.eclipse.microprofile.jwt.JsonWebToken; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Adds a JWT cookie to every authenticated request. - * - *

JWT cookies are used to authenticate further requests based on initial authentication. This - * way, there is no need to route the user through an OpenID Connect IdP on each request, for - * example. - * - * @see JwtCookieLoginFilter - */ -@Provider -@Priority(1100) -public class JwtCookieSetterFilter implements ContainerResponseFilter { - - @ConfigProperty(name = "mulkcms.jwt.signing-key") - String signingKeyAlias; - - @ConfigProperty(name = "mulkcms.jwt.keystore.file") - String signingKeyFile; - - @ConfigProperty(name = "mulkcms.jwt.keystore.passphrase") - String signingKeyPassphrase; - - @ConfigProperty(name = "mulkcms.jwt.issuer") - String issuer; - - @ConfigProperty(name = "mulkcms.jwt.validity") - Duration validity; - - @Inject SecurityIdentity identity; - - private static final Logger log = LoggerFactory.getLogger(JwtCookieSetterFilter.class); - - private PrivateKey signingKey; - - @PostConstruct - public void postCostruct() - throws IOException, KeyStoreException, CertificateException, NoSuchAlgorithmException, - UnrecoverableKeyException { - try (var is = new FileInputStream(signingKeyFile)) { - var keystore = KeyStore.getInstance(KeyStore.getDefaultType()); - keystore.load(is, signingKeyPassphrase.toCharArray()); - signingKey = - (PrivateKey) keystore.getKey(signingKeyAlias, signingKeyPassphrase.toCharArray()); - } - } - - @Override - public void filter( - ContainerRequestContext requestContext, ContainerResponseContext responseContext) - throws IOException { - - if (identity.isAnonymous()) { - return; - } - - var currentTimeSeconds = System.currentTimeMillis() / 1000; - - if (identity instanceof JsonWebToken - && ((JsonWebToken) identity).getExpirationTime() < currentTimeSeconds) { - return; - } - - var claims = Jwt.claims(); - - claims.issuedAt(currentTimeSeconds); - claims.claim(Claims.auth_time.name(), currentTimeSeconds); - claims.expiresAt(currentTimeSeconds + validity.toSeconds()); - claims.issuer(issuer); - claims.preferredUserName(identity.getPrincipal().getName()); - claims.subject(identity.getPrincipal().getName()); - - var token = claims.jws().signatureKeyId(signingKeyAlias).sign(signingKey); - responseContext - .getHeaders() - .add( - "Set-Cookie", - new NewCookie( - "Bearer", - token, - null, - null, - 1, - null, - (int) validity.toSeconds(), - null, - false, - true) - .toString() - + ";SameSite=Strict"); - } -} -- cgit v1.2.3