Skip to content

Authentication integration

Vyacheslav Rusakov edited this page Mar 9, 2017 · 3 revisions

DEPRECATED Documentation was moved into new docuemntation site

Page describes how to configure dropwizard authentication with guice support.

for Dropwizard 0.9

@Provider
class OAuthDynamicFeature extends AuthDynamicFeature {

    @Inject
    OAuthDynamicFeature(OAuthAuthenticator authenticator, OAuthAuthorizer authorizer, Environment environment) {
        super(new OAuthCredentialAuthFilter.Builder<User>()
                .setAuthenticator(authenticator)
                .setAuthorizer(authorizer)
                .setPrefix("Bearer")
                .buildAuthFilter())

        environment.jersey().register(RolesAllowedDynamicFeature.class)
        environment.jersey().register(new AuthValueFactoryProvider.Binder(User.class))
    }

    // may be external class (internal for simplicity)
    @Singleton
    public static class OAuthAuthenticator implements Authenticator<String, User> {

        @Override
        Optional<User> authenticate(String credentials) throws AuthenticationException {
            return Optional.fromNullable(credentials == "valid" ? new User(name: "valid") : null)
        }
    }

    // may be external class (internal for simplicity)
    @Singleton
    public static class OAuthAuthorizer implements Authorizer<User> {
        @Override
        public boolean authorize(User user, String role) {
            return user.getName().equals("good-guy") && role.equals("ADMIN");
        }
    }   
}

If auto configuration is enabled, then class will be resolved and installed automatically. For manual mode:

bootstrap.addBundle(GuiceBundle.<TestConfiguration> builder()
                .installers(JerseyProviderInstaller.class, ResourceInstaller.class)
                .extensions(OAuthDynamicFeature.class, MySecuredResource.class)
                .build()
        );

for Dropwizard 0.8

Basic cases

The only thing which really need to be guice-aware is your custom authenticator:

@Singleton
public class SimpleAuthenticator implements Authenticator<BasicCredentials, User> {
    
    @Inject
    private MyAuthService authService;

    @Override
    public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
        return Optional.fromNullable(authService.findUser(credentials.getUsername(), credentials.getPassword());
    }
}

The other part is configured in application run method (as in manual):

@Override
public void run(ExampleConfiguration configuration,
                Environment environment) {
    environment.jersey().register(AuthFactory.binder(new BasicAuthFactory<String>(
                          InjectorLookup.getInjector(this).get().getInstance(SimpleAuthenticator.class),
                          "SUPER SECRET STUFF",
                          User.class)));
}                         

Here we simply obtain authenticator instance from guice context.

Note: dropwizard *AuthFactory classes (BasicAuthFactory, OAuthFactory) are final, so can't be extended and adopted for guice injection directly. That's why "standard" configuration is the best way.

Custom auth

When you write completely custom AuthFactory, it could be described as guice beans.

For example, if we need even custom annotation:

@Retention(RetentionPolicy.RUNTIME)
@Target([ElementType.PARAMETER, ElementType.FIELD])
@Documented
public @interface CustomAuth {
    // suppose we need custom annotation to support custom annotation field
    String value() default "custom" 
}

Our custom auth factory:

@Provider
public class AuthFactory implements Factory<User>{
    
    @Inject
    private Provider<HttpServletRequest> request;
 
    @Override
    User provide() {
        // do something with request and provide or not user
        return new User();
    }

    @Override
    void dispose(User instance) {
    }
}

External authenticator may be used if required (as in dropwizard factories). Depends on your requirements.

Next we need to associate custom auth annotation with auth factory:

@Provider
@LazyBinding // @HK2Managed may be used as alternative
class AuthFactoryProvider extends AbstractValueFactoryProvider {

    private Factory<User> authFactory;

    @Inject
    public AuthFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
                               final Factory<User> factory, // also Provider<AuthFactory> could be used
                               final ServiceLocator injector) {
        super(extractorProvider, injector, Parameter.Source.UNKNOWN);
        this.authFactory = factory;
    }

    @Override
    protected Factory<?> createValueFactory(Parameter parameter) {
        final CustomAuth auth = parameter.getAnnotation(CustomAuth.class);
        return auth != null ? authFactory : null
    }
}

Note @LazyBinding annotation here. It is required because class depends on hk beans, which are not available in time of guice injector creation. Annotated beans are not created in time of context creation and will be created only when HK start to init auth services.

As an alternative, @HK2Managed could be used, which will delegate bean creation into HK2. HK started after guice and could inject guice dependencies.

And the last, we need to define custom injection resolver so jersey could associate annotated parameter with our auth services:

@Provider
@LazyBinding
public class AuthInjectionResolver extends ParamInjectionResolver<Auth> {
    
    public AuthInjectionResolver() {
        super(AuthFactoryProvider.class)
    }
}

All classes are annotated with jersey @Provider annotation and will be installed by JerseyProviderInstaller. If you use classpath scan, then these classes must lie in the scanned packages.

In case of manual configuration, JerseyProviderInstaller installer must be installed and classes registered.

After all, custom annotation may be used in resources:

@GET
@Path("/")
public Response authDemo(@CustomAuth("customKey") User user) {
    return Response.ok().build();
}