package digital.loom.rhizome.authentication; import java.io.IOException; import java.util.List; import java.util.Map; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.authority.SimpleGrantedAuthority; import com.auth0.Auth0; import com.auth0.authentication.AuthenticationAPIClient; import com.auth0.authentication.result.Authentication; import com.auth0.authentication.result.Credentials; import com.auth0.authentication.result.UserProfile; import com.auth0.jwt.JWTVerifier; import com.auth0.spring.security.api.Auth0AuthorityStrategy; import com.auth0.spring.security.api.Auth0JWTToken; import com.auth0.spring.security.api.Auth0UserDetails; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.RateLimiter; import digital.loom.rhizome.configuration.auth0.Auth0Configuration; /** * This class is a sanity check to ensure that authentication is successfully working against auth0 server. The hard * coded credentials and secrets are only usable for testing. * * @author Matthew Tamayo-Rios <matthew@kryptnostic.com> */ public class AuthenticationTest { private static final Logger logger = LoggerFactory .getLogger( AuthenticationTest.class ); private static final String domain = "loom.auth0.com"; private static final String issuer = "https://loom.auth0.com/"; private static final String clientId = "PTmyExdBckHAiyOjh4w2MqSIUGWWEdf8"; private static final String clientSecret = "VmzAkSSsYQe7DGe5Fz8IHZnKsZF8Ul3UA6tMtikZQC1wLxoA-Krve0bdMN2UH1jb"; private static final String securedRoute = "NOT_USED"; private static final String authorityStrategy = "ROLES"; private static final String signingAlgorithm = "HS256"; // private static final String defaultAuth0ApiSecurityEnabled = "false"; // private static final String signingAlgorithm = "HS256"; private static final boolean base64EncodedSecret = true; private static final LoadingCache<AuthenticationTestRequestOptions, Authentication> authentications; private static final AuthenticationTestRequestOptions authOptions = new AuthenticationTestRequestOptions() .setUsernameOrEmail( "support@kryptnostic.com" ) .setPassword( "abracadabra" ); private static final String token = "No token for you"; public static final Auth0Configuration configuration = new Auth0Configuration( domain, issuer, clientId, clientSecret, securedRoute, authorityStrategy, signingAlgorithm, base64EncodedSecret, token ); private static final Auth0 auth0 = new Auth0( clientId, "loom.auth0.com" ); private static final AuthenticationAPIClient client = auth0.newAuthenticationAPIClient(); private static final RateLimiter authRateLimiter = RateLimiter.create( 0.25 ); static { authentications = CacheBuilder.newBuilder() .build( new CacheLoader<AuthenticationTestRequestOptions, Authentication>() { @Override public Authentication load( AuthenticationTestRequestOptions options ) throws Exception { authRateLimiter.acquire(); Authentication auth = client .getProfileAfter( client.login( options.getUsernameOrEmail(), options.getPassword() ) .setConnection( options.getConnection() ) .setScope( options.getScope() ) ) .execute(); logger.info( "Caching the following idToken: Bearer {}", auth.getCredentials().getIdToken() ); return auth; } } ); } public static Authentication getAuthentication( AuthenticationTestRequestOptions options ) { return authentications.getUnchecked( options ); } public static Authentication authenticate() { return authentications.getUnchecked( authOptions ); } public static Authentication refreshAndGetAuthentication( AuthenticationTestRequestOptions options ) { authentications.invalidate( options ); return authentications.getUnchecked( options ); } @Test public void testRoles() throws Exception { Authentication auth = authenticate(); JWTVerifier jwtVerifier = new JWTVerifier( new Base64( true ).decodeBase64( clientSecret ), clientId, issuer ); String idToken = auth.getCredentials().getIdToken(); Auth0JWTToken token = new Auth0JWTToken( idToken ); final Map<String, Object> decoded = jwtVerifier.verify( idToken ); Map<String, Object> d2 = auth.getProfile().getAppMetadata(); Auth0UserDetails userDetails = new Auth0UserDetails( d2, Auth0AuthorityStrategy.ROLES.getStrategy() ); Assert.assertTrue( "Return roles must contain user", userDetails.getAuthorities().contains( new SimpleGrantedAuthority( "user" ) ) ); logger.info( "Roles: {}", userDetails.getAuthorities() ); } @Test public void testLogin() throws JsonParseException, JsonMappingException, IOException { Credentials creds = authenticate().getCredentials(); UserProfile profile = client.tokenInfo( creds.getIdToken() ).execute(); @SuppressWarnings( "unchecked" ) List<String> roles = (List<String>) profile.getAppMetadata().getOrDefault( "roles", ImmutableList.of() ); Assert.assertTrue( "Return roles must contain user", roles.contains( "user" ) ); Assert.assertTrue( StringUtils.isNotBlank( creds.getIdToken() ) ); } }