summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieLoginFilter.java184
-rw-r--r--src/main/java/eu/mulk/mulkcms2/authentication/JwtCookieSetterFilter.java118
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");
- }
-}