Coder Social home page Coder Social logo

dropwizard-jwt-cookie-authentication's Introduction

Build Status Maven Central Coverage Status Javadoc Mentioned in Awesome Dropwizard

Please note version 5 requires Java 11 and Dropwizard 4.

dropwizard-jwt-cookie-authentication

Statelessness is not only an architectural constaint of RESTful applications, it also comes with a lot of advantages regarding scalability and memory usage.

A common pattern is to provide the client with a signed JWT containing all necessary authorization and/or session state information. This JWT must then be passed along subsequent requests, usually in bearer Authorization HTTP headers.

However, in the particular case where clients of the RESTful application are web applications, it is much more interesting to use cookies. The browser will automatically read, store, send and expire the tokens, saving front-end developers the hassle of doing it themselves.

This dropwizard bundle makes things simple for back-end developpers too. It automatically serializes/deserializes session information into/from JWT cookies.

Enabling the bundle

Add the dropwizard-jwt-cookie-authentication dependency

Add the dropwizard-jwt-cookie-authentication library as a dependency to your pom.xml file:

<dependency>
    <groupId>org.dhatim</groupId>
    <artifactId>dropwizard-jwt-cookie-authentication</artifactId>
    <version>5.1.3</version>
</dependency>

Edit you app's Dropwizard YAML config file

The default values are shown below. If they suit you, this step is optional.

jwtCookieAuth:
  secretSeed: null
  secure: false
  httpOnly: true
  domain: null
  sameSite: null
  sessionExpiryVolatile: PT30m
  sessionExpiryPersistent: P7d

Add the 'JwtCookieAuthConfiguration' to your application configuration class:

This step is also optional if you skipped the previous one.

@Valid
@NotNull
private JwtCookieAuthConfiguration jwtCookieAuth = new JwtCookieAuthConfiguration();

public JwtCookieAuthConfiguration getJwtCookieAuth() {
  return jwtCookieAuth;
}

Add the bundle to the dropwizard application

public void initialize(Bootstrap<MyApplicationConfiguration> bootstrap) {
  bootstrap.addBundle(JwtCookieAuthBundle.getDefault());
}

If you have a custom configuration fot the bundle, specify it like so:

bootstrap.addBundle(JwtCookieAuthBundle.getDefault().withConfigurationSupplier(MyAppConfiguration::getJwtCookieAuth));

Using the bundle

By default, the JWT cookie is serialized from / deserialized in an instance of DefaultJwtCookiePrincipal.

When the user authenticate, you must put an instance of DefaultJwtCookiePrincipal in the security context (which you can inject in your resources using the @Context annotation) using JwtCookiePrincipal.addInContext

JwtCookiePrincipal principal = new DefaultJwtCookiePrincipal(name);
principal.addInContext(context);

Once a principal has been set, it can be retrieved using the @Auth annotation in method signatures. You can also use CurrentPrincipal.get() within the request thread.

Each time an API endpoint is called, a fresh cookie JWT is issued to reset the session TTL. You can use the @DontRefreshSession on methods where this behavior is unwanted.

To specify a max age in the cookie (aka "remember me"), use DefaultJwtCookiePrincipal.setPersistent(true).

It is a stateless auhtentication method, so there is no real way to invalidate a session other than waiting for the JWT to expire. However calling JwtCookiePrincipal.removeFromContext(context) will make browsers discard the cookie by setting the cookie expiration to a past date.

Principal roles can be specified via the DefaultJwtCookiePrincipal.setRoles(...) method. You can then define fine grained access control using annotations such as @RolesAllowed or @PermitAll.

Additional custom data can be stored in the Principal using DefaultJwtCookiePrincipal.getClaims().put(key, value).

Sample application resource

@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public DefaultJwtCookiePrincipal login(@Context ContainerRequestContext requestContext, String name){
    DefaultJwtCookiePrincipal principal = new DefaultJwtCookiePrincipal(name);
    principal.addInContext(requestContext);
    return principal;
}

@GET
@Path("logout")
public void logout(@Context ContainerRequestContext requestContext){
    JwtCookiePrincipal.removeFromContext(requestContext);
}

@GET
@Produces(MediaType.APPLICATION_JSON)
public DefaultJwtCookiePrincipal getPrincipal(@Auth DefaultJwtCookiePrincipal principal){
    return principal;
}

@GET
@Path("idempotent")
@Produces(MediaType.APPLICATION_JSON)
@DontRefreshSession
public DefaultJwtCookiePrincipal getSubjectWithoutRefreshingSession(@Auth DefaultJwtCookiePrincipal principal){
    return principal;
}

@GET
@Path("restricted")
@RolesAllowed("admin")
public String getRestrictedResource(){
    return "SuperSecretStuff";
}

Custom principal implementation

If you want to use your own Principal class instead of the DefaultJwtCookiePrincipal, simply implement the interface JwtCookiePrincipal and pass it to the bundle constructor along with functions to serialize it into / deserialize it from JWT claims.

e.g:

bootstrap.addBundle(new JwtCookieAuthBundle<>(MyCustomPrincipal.class, MyCustomPrincipal::toClaims, MyCustomPrincipal::new));

JWT Signing Key

By default, the signing key is randomly generated on application startup. It means that users will have to re-authenticate after each server reboot.

To avoid this, you can specify a secretSeed in the configuration. This seed will be used to generate the signing key, which will therefore be the same at each application startup.

Alternatively you can specify your own key factory:

bootstrap.addBundle(JwtCookieAuthBundle.getDefault().withKeyProvider((configuration, environment) -> {/*return your own key*/}));

Manual Setup

If you need Chained Factories or Multiple Principals and Authenticators, don't register directly the bundle. Use instead its getAuthRequestFilter and getAuthResponseFilter methods to manually setup authentication.

You will also be responsible for generating the signing key and registering RolesAllowedDynamicFeature or DontRefreshSessionFilter if they are needed.

Example:

JwtCookieAuthBundle jwtCookieAuthBundle = new JwtCookieAuthBundle<>(
    MyJwtCookiePrincipal.class,
    MyJwtCookiePrincipal::toClaims,
    MyJwtCookiePrincipal::new);

SecretKey key = JwtCookieAuthBundle.generateKey(configuration.getJwtCookieAuth().getSecretSeed());

environment.jersey().register(
        new PolymorphicAuthDynamicFeature<>(
                ImmutableMap.of(
                        MyJwtCookiePrincipal.class, jwtCookieAuthBundle.getAuthRequestFilter(key),
                        MyBasicPrincipal.class, new BasicCredentialAuthFilter.Builder<MyBasicPrincipal>()
                            .setAuthenticator(new MyBasicAuthenticator())
                            .setRealm("SUPER SECRET STUFF")
                            .buildAuthFilter()
                )
        )
);
environment.jersey().register(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(MyJwtCookiePrincipal.class, MyBasicPrincipal.class)));
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(DontRefreshSessionFilter.class);
environment.jersey().register(jwtCookieAuthBundle.getAuthResponseFilter(key, configuration.getJwtCookieAuth()));

Javadoc

It's here.

dropwizard-jwt-cookie-authentication's People

Contributors

adamhoward avatar dependabot[bot] avatar jhendess avatar ochedru avatar olivierchedru avatar renovate[bot] avatar rgbj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dropwizard-jwt-cookie-authentication's Issues

Can I set JWT Tokens without expiry

I've set 1 minute time for volatile session in yml file, and its working.

jwtCookieAuth:
secretSeed: null
httpsOnlyCookie: false
sessionExpiryVolatile: PT1m
sessionExpiryPersistent: P2d

Is there a way for me to not set expiry to token.. i have a use case for file upload API which should consist md5 and user identifier in token without expiry.. so i can receive (only md5 matching file) file in future.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build.yml
  • actions/checkout v4
  • actions/setup-java v4
.github/workflows/codeql-analysis.yml
  • actions/checkout v4
  • github/codeql-action v3
  • github/codeql-action v3
  • github/codeql-action v3
maven
pom.xml
  • io.dropwizard:dropwizard-bom 4.0.7
  • jakarta.annotation:jakarta.annotation-api 2.1.1
  • io.jsonwebtoken:jjwt-api 0.12.5
  • io.jsonwebtoken:jjwt-impl 0.12.5
  • io.jsonwebtoken:jjwt-jackson 0.12.5
  • org.jacoco:jacoco-maven-plugin 0.8.12
  • org.apache.maven.plugins:maven-surefire-plugin 3.2.5
  • org.eluder.coveralls:coveralls-maven-plugin 4.3.0
  • javax.xml.bind:jaxb-api 2.3.1
  • org.apache.maven.plugins:maven-source-plugin 3.3.1
  • org.apache.maven.plugins:maven-javadoc-plugin 3.6.3

  • Check this box to trigger a request for Renovate to run again on this repository

Help: How to generate a JWT cookie from Basic Authentication endpoint?

I am trying to use your library to generate a JWT for a mobile app.

The API endpoint is called /login which uses Basic Authentication (username / password) to then generate a token

@GET
@Path("/login")
@Produces(MediaType.APPLICATION_JSON)
public DefaultJwtCookiePrincipal setPrincipal(@Context ContainerRequestContext requestContext, @Auth final
    ShepherdUser user){
    DefaultJwtCookiePrincipal principal = new DefaultJwtCookiePrincipal(user.getName());
    principal.addInContext(requestContext);
    return principal;
}

When calling the endpoint I get a HTTP 401 with the message

Credentials are required to access this resource.

If I remove this line from the application bootstrap, the basic authentication works

bootstrap.addBundle(JwtCookieAuthBundle.getDefault());

However I get back an unencrypted DefaultJwtCookiePrincipal response

{"name":"admin","persistent":false,"roles":[],"claims":{"sub":"admin","pst":false,"rls":[]}}

This is in my basic authentication in the application run

    // app authentication
    environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<ShepherdUser>()
            .setAuthenticator(new ShepherdAuthenticator())
            .setAuthorizer(new ShepherdAuthoriser())
            .setRealm(configuration.getName())
            .buildAuthFilter()));
    environment.jersey().register(RolesAllowedDynamicFeature.class);
    environment.jersey().register(new AuthValueFactoryProvider.Binder<>(ShepherdUser.class));

Finally my Principle

public class ShepherdUser implements Principal {

private String name;
private Set<String> roles;

public ShepherdUser(String name, Set<String> roles) {
    this.name = checkNotNull(name, "User name is required");
    this.roles = checkNotNull(roles, "Roles are required");
}

public String getName() {
    return this.name;
}

public Set<String> getRoles() {
    return roles;
}
}

It seems that Dropwizard is not using the correct authentication for my /login endpoint, how can I make this work.
I couldn't find any documentation on this.

I also posted on Stackoverflow for the benefit of anyone else

https://stackoverflow.com/questions/47080904/creating-json-web-tokens-through-basic-authentication-endpoint-dropwizard/47094105#47094105

Thanks

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Location: renovate.json
Error type: The renovate configuration file contains some invalid settings
Message: packageRules[0]: Each packageRule must contain at least one match* or exclude* selector. Rule: {"automerge":true}

Volatile session is not expired after specific time

I've set 1 minute time for volatile session in yml file, but its not working.

jwtCookieAuth:
secretSeed: null
httpsOnlyCookie: false
sessionExpiryVolatile: PT1m
sessionExpiryPersistent: P2d

Please suggest me a way to achieve it or share some example.

Non-static method cannot be referenced from a static context

With a custom configuration, following the tutorial here on GitHub, if I add this line:

bootstrap.addBundle(JwtCookieAuthBundle.getDefault().withConfigurationSupplier(MyAppConfiguration::getJwtCookieAuth));

It says:

non-static method cannot be referenced from a static context

What is the correct way to apply custom configuration?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.