All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dhatim.dropwizard.jwt.cookie.authentication.JwtCookieAuthBundle Maven / Gradle / Ivy

There is a newer version: 5.1.3
Show newest version
/**
 * Copyright 2020 Dhatim
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package org.dhatim.dropwizard.jwt.cookie.authentication; import com.fasterxml.jackson.databind.module.SimpleModule; import com.google.common.hash.Hashing; import com.google.common.primitives.Ints; import io.dropwizard.Configuration; import io.dropwizard.ConfiguredBundle; import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.auth.AuthFilter; import io.dropwizard.auth.AuthValueFactoryProvider; import io.dropwizard.auth.Authorizer; import io.dropwizard.jersey.setup.JerseyEnvironment; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import io.jsonwebtoken.Claims; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.DefaultClaims; import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import javax.crypto.KeyGenerator; import javax.crypto.spec.SecretKeySpec; import javax.ws.rs.container.ContainerResponseFilter; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.time.Duration; import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Function; /** * Dopwizard bundle * * @param Your application configuration class * @param

the class of the principal that will be serialized in / deserialized from JWT cookies */ public class JwtCookieAuthBundle implements ConfiguredBundle { public static final String JWT_COOKIE_DEFAULT_NAME = "sessionToken"; private static final String JWT_COOKIE_PREFIX = "jwtCookie"; private final Class

principalType; private final Function serializer; private final Function deserializer; private Function configurationSupplier; private BiFunction keySuppplier; /** * Get a bundle instance that will use DefaultJwtCookiePrincipal * * @param Your application configuration class * @return a bundle instance that will use DefaultJwtCookiePrincipal */ public static JwtCookieAuthBundle getDefault() { return new JwtCookieAuthBundle<>( DefaultJwtCookiePrincipal.class, DefaultJwtCookiePrincipal::getClaims, DefaultJwtCookiePrincipal::new); } /** * Build a new instance of JwtCookieAuthBundle * * @param principalType the class of the principal that will be serialized in / deserialized from JWT cookies * @param serializer a function to serialize principals into JWT claims * @param deserializer a function to deserialize JWT claims into principals */ public JwtCookieAuthBundle(Class

principalType, Function serializer, Function deserializer) { this.principalType = principalType; this.serializer = serializer; this.deserializer = deserializer; this.configurationSupplier = c -> new JwtCookieAuthConfiguration(); } /** * If you want to sign the JWT with your own key, specify it here * * @param keySupplier a bi-function which will return the signing key from the configuration and environment * @return this */ public JwtCookieAuthBundle withKeyProvider(BiFunction keySupplier) { this.keySuppplier = keySupplier; return this; } /** * If you need to configure the bundle, specify it here * * @param configurationSupplier a bi-function which will return the bundle configuration from the application configuration * @return this */ public JwtCookieAuthBundle withConfigurationSupplier(Function configurationSupplier) { this.configurationSupplier = configurationSupplier; return this; } @Override public void initialize(Bootstrap bootstrap) { //in case somebody needs to serialize a DefaultJwtCookiePrincipal bootstrap.getObjectMapper().registerModule(new SimpleModule().addAbstractTypeMapping(Claims.class, DefaultClaims.class)); } @Override public void run(C configuration, Environment environment) throws Exception { JwtCookieAuthConfiguration conf = configurationSupplier.apply(configuration); //build the key from the key factory if it was provided Key key = Optional .ofNullable(keySuppplier) .map(k -> k.apply(configuration, environment)) .orElseGet(() -> generateKey(conf.getSecretSeed())); JerseyEnvironment jerseyEnvironment = environment.jersey(); jerseyEnvironment.register(new AuthDynamicFeature(getAuthRequestFilter(key, conf.getCookieName()))); jerseyEnvironment.register(new AuthValueFactoryProvider.Binder<>(principalType)); jerseyEnvironment.register(RolesAllowedDynamicFeature.class); jerseyEnvironment.register(getAuthResponseFilter(key, conf)); jerseyEnvironment.register(DontRefreshSessionFilter.class); } /** * Get a filter that will deserialize the principal from JWT cookies found in HTTP requests * * @param key the key used to validate the JWT * @param cookieName the name of the cookie holding the JWT * @return the request filter */ public AuthFilter getAuthRequestFilter(Key key, String cookieName) { return new JwtCookieAuthRequestFilter.Builder() .setCookieName(cookieName) .setAuthenticator(new JwtCookiePrincipalAuthenticator(key, deserializer)) .setPrefix(JWT_COOKIE_PREFIX) .setAuthorizer((Authorizer

) (P::isInRole)) .buildAuthFilter(); } /** * Get a filter that will deserialize the principal from JWT cookies found in HTTP requests, * using the default cookie name. * * @param key the key used to validate the JWT * @return the request filter */ public AuthFilter getAuthRequestFilter(Key key) { return getAuthRequestFilter(key, JWT_COOKIE_DEFAULT_NAME); } /** * Get a filter that will serialize principals into JWTs and add them to HTTP response cookies * * @param key the key used to sign the JWT * @param configuration cookie configuration (secure, httpOnly, expiration...) * @return the response filter */ public ContainerResponseFilter getAuthResponseFilter(Key key, JwtCookieAuthConfiguration configuration) { return new JwtCookieAuthResponseFilter<>( principalType, serializer, configuration.getCookieName(), configuration.isSecure(), configuration.isHttpOnly(), configuration.getDomain(), configuration.getSameSite(), key, Ints.checkedCast(Duration.parse(configuration.getSessionExpiryVolatile()).getSeconds()), Ints.checkedCast(Duration.parse(configuration.getSessionExpiryPersistent()).getSeconds())); } /** * Generate a HMAC SHA256 Key that can be used to sign JWTs * * @param secretSeed a seed from which the key will be generated. * Identical seeds will generate identical keys. * If null, a random key is returned. * @return a HMAC SHA256 Key */ public static Key generateKey(String secretSeed) { // make a key from the seed if it was provided return Optional.ofNullable(secretSeed) .map(seed -> Hashing.sha256().newHasher().putString(seed, StandardCharsets.UTF_8).hash().asBytes()) .map(k -> (Key) new SecretKeySpec(k, SignatureAlgorithm.HS256.getJcaName())) //else generate a random key .orElseGet(getHmacSha256KeyGenerator()::generateKey); } private static KeyGenerator getHmacSha256KeyGenerator() { try { return KeyGenerator.getInstance(SignatureAlgorithm.HS256.getJcaName()); } catch (NoSuchAlgorithmException e) { throw new SecurityException(e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy