/*
* Copyright (c) 2015 Yann Le Moigne.
*
* 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 fr.javatic.ratpack.jwtauth;
import com.auth0.jwt.JWTSigner;
import com.auth0.jwt.JWTVerifier;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.multibindings.Multibinder;
import ratpack.guice.ConfigurableModule;
import ratpack.handling.Context;
import ratpack.handling.HandlerDecorator;
import javax.inject.Singleton;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class JWTAuthModule extends ConfigurableModule<JWTAuthModule.Config> {
@Override
protected void configure() {
Multibinder.newSetBinder(binder(), HandlerDecorator.class)
.addBinding()
.to(JWTClaimsHandlerDecorator.class);
}
public static class Config {
private Map<Object, RealmConfig<?>> mapRealmToRealmConfig = new HashMap<>();
private String secret = UUID.randomUUID().toString();
private String header = "X-Authorization";
public <T> Config authentication(Object realm,
Class<T> credentialType,
AuthenticationFunction<T> function,
InputType inputType) {
this.mapRealmToRealmConfig.put(realm, new InstanceRealmConfig<>(credentialType, function, inputType));
return this;
}
public <T> Config authentication(Object realm,
Class<T> credentialType,
Class<? extends AuthenticationFunction<T>> functionType,
InputType inputType) {
this.mapRealmToRealmConfig.put(realm, new TypeRealmConfig<>(credentialType, functionType, inputType));
return this;
}
public Config secret(String secret) {
this.secret = secret;
return this;
}
public Config header(String header) {
this.header = header;
return this;
}
public String getHeader() {
return header;
}
}
public interface RealmConfig<T> {
T getInput(Context context) throws Exception;
JWTClaims authenticate(Injector inject, T credential) throws AuthenticationFailed;
}
public static class InstanceRealmConfig<T> implements RealmConfig<T> {
private final AuthenticationFunction<T> function;
private final Class<T> credentialType;
private final InputType inputType;
public InstanceRealmConfig(Class<T> credentialType,
AuthenticationFunction<T> function,
InputType inputType) {
this.function = function;
this.credentialType = credentialType;
this.inputType = inputType;
}
public T getInput(Context context) throws Exception {
return this.inputType.getInput(context, this.credentialType);
}
public JWTClaims authenticate(Injector inject, T credential) throws AuthenticationFailed {
return this.function.authenticate(credential);
}
}
public static class TypeRealmConfig<T> implements RealmConfig<T> {
private final Class<? extends AuthenticationFunction<T>> functionType;
private final Class<T> credentialType;
private final InputType inputType;
public TypeRealmConfig(Class<T> credentialType,
Class<? extends AuthenticationFunction<T>> functionType,
InputType inputType) {
this.functionType = functionType;
this.credentialType = credentialType;
this.inputType = inputType;
}
public T getInput(Context context) throws Exception {
return this.inputType.getInput(context, this.credentialType);
}
public JWTClaims authenticate(Injector injector, T credential) throws AuthenticationFailed {
return injector.getInstance(functionType).authenticate(credential);
}
}
@Provides
@Singleton
protected JWTVerifier jwtVerifier(Config config) {
return new JWTVerifier(config.secret);
}
@Provides
@Singleton
protected JWTSigner jwtSigner(Config config) {
return new JWTSigner(config.secret);
}
@Provides
@com.google.inject.Singleton
private LoginHandlerProvider loginHandlerProvider(Config config, JWTSigner jwtSigner, Injector injector) {
LoginHandlerProvider loginHandlerProvider = new LoginHandlerProvider();
config.mapRealmToRealmConfig.forEach((realm, realmConfig) -> {
loginHandlerProvider.addAuthenticator(realm, new LoginHandler<>(jwtSigner, realmConfig, injector));
});
return loginHandlerProvider;
}
}