package digital.loom.rhizome.authentication;
import javax.inject.Inject;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import com.auth0.Auth0;
import com.auth0.authentication.AuthenticationAPIClient;
import com.auth0.jwt.Algorithm;
import com.auth0.spring.security.api.Auth0AuthenticationEntryPoint;
import com.auth0.spring.security.api.Auth0AuthenticationFilter;
import com.auth0.spring.security.api.Auth0AuthenticationProvider;
import com.auth0.spring.security.api.Auth0AuthorityStrategy;
import com.auth0.spring.security.api.Auth0CORSFilter;
import com.auth0.spring.security.api.authority.AuthorityStrategy;
import com.kryptnostic.rhizome.core.JettyAnnotationConfigurationHack;
import com.kryptnostic.rhizome.core.RhizomeSecurity;
import digital.loom.rhizome.configuration.auth0.Auth0Configuration;
/**
* Including this pod will enable using Auth0 as an authentication source. It based off the one provided by auth0 in the
* spring-mvc pod, but differs in that it enables {@code @Secured} annotations, disables debug, and allows configuration
* using properties file.
*
* @author Matthew Tamayo-Rios <matthew@kryptnostic.com>
*
*/
@EnableGlobalMethodSecurity(
securedEnabled = true,
prePostEnabled = true )
@EnableWebSecurity(
debug = false )
public class Auth0SecurityPod extends WebSecurityConfigurerAdapter {
@Inject
private Auth0Configuration configuration;
@Inject
private Auth0 auth0;
static {
JettyAnnotationConfigurationHack.registerInitializer( RhizomeSecurity.class.getCanonicalName() );
}
@Bean(
name = "auth0AuthenticationManager" )
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean(
name = "authorityStrategy" )
public AuthorityStrategy authorityStrategy() {
if ( !Auth0AuthorityStrategy.contains( configuration.getAuthorityStrategy() ) ) {
throw new IllegalStateException( "Configuration error, illegal authority strategy" );
}
return Auth0AuthorityStrategy.valueOf( configuration.getAuthorityStrategy() ).getStrategy();
}
@Bean(
name = "auth0AuthenticationProvider" )
public Auth0AuthenticationProvider auth0AuthenticationProvider() {
final ConfigurableAuth0AuthenticationProvider authenticationProvider = getAuthenticationProvider();
authenticationProvider.setDomain( configuration.getDomain() );
authenticationProvider.setIssuer( configuration.getIssuer() );
authenticationProvider.setClientId( configuration.getClientId() );
authenticationProvider.setClientSecret( configuration.getClientSecret() );
authenticationProvider.setSecuredRoute( configuration.getSecuredRoute() );
authenticationProvider.setAuthorityStrategy( authorityStrategy() );
authenticationProvider.setBase64EncodedSecret( configuration.isBase64EncodedSecret() );
authenticationProvider.setSigningAlgorithm( Algorithm.valueOf( configuration.getSigningAlgorithm() ) );
return authenticationProvider;
}
@Bean(
name = "auth0EntryPoint" )
public AuthenticationEntryPoint auth0AuthenticationEntryPoint() {
return new Auth0AuthenticationEntryPoint();
}
@Bean(
name = "auth0Filter" )
public Auth0AuthenticationFilter auth0AuthenticationFilter( final AuthenticationEntryPoint entryPoint ) {
final Auth0AuthenticationFilter filter = new CookieReadingAuth0AuthenticationFilter();
filter.setEntryPoint( entryPoint );
return filter;
}
@Override
protected void configure( final AuthenticationManagerBuilder auth ) throws Exception {
auth.authenticationProvider( auth0AuthenticationProvider() );
}
@Override
public void configure( WebSecurity web ) throws Exception {
web.ignoring().antMatchers( HttpMethod.OPTIONS, "/**" );
}
@Override
protected void configure( final HttpSecurity http ) throws Exception {
// Disable CSRF for JWT usage
http.csrf().disable();
// Add Auth0 Authentication Filter
http
.addFilterAfter(
auth0AuthenticationFilter( auth0AuthenticationEntryPoint() ),
SecurityContextPersistenceFilter.class
)
.addFilterBefore(
getAuth0CORSFilter(),
CookieReadingAuth0AuthenticationFilter.class
);
// Apply the Authentication and Authorization Strategies your application endpoints require
authorizeRequests( http );
// STATELESS - we want re-authentication of JWT token on every request
http.sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS );
}
/**
* Lightweight default configuration that offers basic authorization checks for authenticated users on secured
* endpoint, and sets up a Principal user object with granted authorities
* <p>
* For simple apps, this is sufficient, however for applications wishing to specify fine-grained endpoint access
* restrictions, use Role / Group level endpoint authorization etc, then this configuration should be disabled and a
* copy, augmented with your own requirements provided. See Sample app for example
*
* Override this function in subclass to apply custom authentication / authorization strategies to your application
* endpoints
*/
protected void authorizeRequests( HttpSecurity http ) throws Exception {
http.authorizeRequests()
.antMatchers( "/**" ).authenticated();
}
/**
* Override this to modify the type of ConfigurableAuthenticationProvider for performing authentication.
* @return The Auth0AuthenticationProvider to use for performing authentications.
*/
protected ConfigurableAuth0AuthenticationProvider getAuthenticationProvider() {
return new ConfigurableAuth0AuthenticationProvider(
getAuthenticationApiClient() );
}
protected AuthenticationAPIClient getAuthenticationApiClient() {
return auth0.newAuthenticationAPIClient();
}
protected Auth0CORSFilter getAuth0CORSFilter() {
return new Auth0CORSFilter();
}
}