diff options
Diffstat (limited to 'src/main/java')
| -rw-r--r-- | src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java | 184 | ||||
| -rw-r--r-- | src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java | 118 | 
2 files changed, 0 insertions, 302 deletions
| 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. - * - * <p>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<TokenAuthenticationRequest> { - -  @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<SecurityIdentity> 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<TokenAuthenticationRequest> 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<String> getRoles() { -      return Set.of(); -    } - -    @Override -    public <T extends Credential> T getCredential(Class<T> credentialType) { -      return null; -    } - -    @Override -    public Set<Credential> getCredentials() { -      return Set.of(); -    } - -    @Override -    public <T> T getAttribute(String name) { -      return null; -    } - -    @Override -    public Map<String, Object> getAttributes() { -      return Map.of(); -    } - -    @Override -    public CompletionStage<Boolean> 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. - * - * <p>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"); -  } -} | 
