Securing GWT Apps using Spring Oauth & Spring Social

General purpose :

Provides a spring security configuration for any Oauth2 providers in our projects. For that I found many good starting points on the web and I merged them into a simple GWTP project forked from an existing repository (https://github.com/imrabti/gwtp-spring-security) which helped me to achieve this goal with minor changes.

This a solution for GWT developers who want to provide a session management using usual (Google, Facebook, …) or custom Oauth2 provider in their applications.

Dependency management :

<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>${oauth.version}</version>
</dependency>
...
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${spring-security.version}</version>
</dependency>

<!-- Spring Social -->
<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-security</artifactId>
    <version>${spring-social.version}</version>
</dependency>

<!-- Persistance dependencies -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.4.186</version>
</dependency>

2 – Custom provider definition :

I added some classes here as other embedded Social provider to define my own provider

– Provider definition

public interface Corporate {
    CorporatePofile getUserProfile();

    void updateStatus(String message);
}

– Oauth2 provider service

public class CorporateServiceProvider extends AbstractOAuth2ServiceProvider<Corporate> {
    public CorporateServiceProvider(OAuth2Operations oauth2Operations) {
        super(oauth2Operations);
    }

    @Override
    public Corporate getApi(String s) {
        return new CorporateServiceTemplate(s);
    }
}

– Provider service template

public class CorporateServiceTemplate extends AbstractOAuth2ApiBinding implements Corporate {
    private final static String corporateProfileURL = "http://localhost:8080/oauthprotoserver/profile/";
    private String accessToken;

    CorporateServiceTemplate(String accessToken) {
        super(accessToken);
        this.accessToken = accessToken;
    }

    @Override
    public CorporatePofile getUserProfile() {
        try {
            ResponseEntity<String> content = getRestTemplate().exchange(URI.create(corporateProfileURL + accessToken),
                    HttpMethod.GET, null, String.class);

            // TODO Retrieve a Corporate JSON user build a serialized object
            return new CorporatePofile(content.getBody(),"test@test.com","client1","employee","foo","bar");

        }  catch (HttpClientErrorException e2) {
            throw new OAuth2Exception(e2.getMessage());
        }
    }

    @Override
    public void updateStatus(String message) {
      return;
    }
}

And to perform the implicit SignUp and build a valid session with SocialUserDetails

– A SimpleConnectionSignUp which requires a JDBC user connection┬árepository

public final class SimpleConnectionSignUp implements ConnectionSignUp {
    private final AtomicLong userIdSequence = new AtomicLong();

    public String execute(Connection<?> connection) {
        return Long.toString(userIdSequence.incrementAndGet());
    }
}

– A SimpleSignInAdapter which will help to store UserDetails into Security Context

public class SimpleSignInAdapter implements SignInAdapter {
    private static final Logger log = LoggerFactory.getLogger(SimpleSignInAdapter.class);
    private final RequestCache requestCache;
    private final NuvolaCasDetailsService userService;

    @Autowired
    public SimpleSignInAdapter(RequestCache requestCache,
                               NuvolaCasDetailsService userService) {
        this.requestCache = requestCache;
        this.userService = userService;
    }

    @Override
    public String signIn(String localUserId, Connection<?> connection, NativeWebRequest request) {
        SocialUserDetails user = userService.loadUserByUserId(connection.getDisplayName());
        SignInUtils.signin(user);
        // Return Application home page
        return "/";
    }

This will help us into the final step add a simple login page to post a SignIn request catched by the ProviderSignInController (Framework controller)

...
<div align="center">
    <form id="casForm" name="casForm" action="/api/signin/corporate" method="POST" >
        <p><input name="login" value="Connexion via Portail" type="submit"></p>
    </form>
</div>

The “corporate” at the end of the uri (/api/signin/corporate) is our providerId which is a Path parameter helping the controller to build a valid OAuth2ConnectionFactory.

3 – Spring security configuration :

...
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
    return super.userDetailsServiceBean();
}

@Bean
OAuth2Template restTemplate() {
    return new OAuth2Template(corporateProvider().getClientId(),
            corporateProvider().getClientSecret(),
            corporateProvider().getUserAuthorizationUri(),
            corporateProvider().getAccessTokenUri());
}

@Bean
@Scope(value="singleton", proxyMode= ScopedProxyMode.INTERFACES)
public SocialAuthenticationServiceLocator socialAuthenticationServiceLocator() {
    SocialAuthenticationServiceRegistry registry = new SocialAuthenticationServiceRegistry();
    registry.addConnectionFactory(new CorporateConnectionFactory(restTemplate()));

    return registry;
}

@Bean
@Scope(value="singleton", proxyMode=ScopedProxyMode.INTERFACES)
public UsersConnectionRepository usersConnectionRepository() {
    JdbcUsersConnectionRepository connectionRepository =
            new JdbcUsersConnectionRepository(dataSource, socialAuthenticationServiceLocator(), Encryptors.noOpText());
    connectionRepository.setConnectionSignUp(new SimpleConnectionSignUp());
    return connectionRepository;
}

@Bean
public ProviderSignInController providerSignInController() {
    ProviderSignInController controller = new ProviderSignInController(socialAuthenticationServiceLocator(),
            usersConnectionRepository(), new SimpleSignInAdapter(new HttpSessionRequestCache(),
            userDetailsService));
    return controller;
}

@Bean
AuthorizationCodeResourceDetails corporateProvider() {
    AuthorizationCodeResourceDetails codeResourceDetails = new AuthorizationCodeResourceDetails();
    codeResourceDetails.setClientId(appClientId);
    codeResourceDetails.setClientSecret("appClientSecret");
    codeResourceDetails.setGrantType("authorization_code");
    codeResourceDetails.setTokenName("bearer");
    codeResourceDetails.setAuthenticationScheme(AuthenticationScheme.header);
    codeResourceDetails.setClientAuthenticationScheme(AuthenticationScheme.header);
    codeResourceDetails.setAccessTokenUri("accessTokenUri");
    codeResourceDetails.setUserAuthorizationUri("userAuthorizationUri");
    codeResourceDetails.setUseCurrentUri(true);
    codeResourceDetails.setPreEstablishedRedirectUri("http://localhost:8090/");
    return codeResourceDetails;
}

@Bean
AuthorizationCodeResourceDetails googleProvider() {
    return null;
}

@Bean
AuthorizationCodeResourceDetails facebookProvider() {
    return null;
}

@Override
protected AuthenticationManager authenticationManager() throws Exception {
    return super.authenticationManager();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable()
            .formLogin().loginPage("/access/login")
            .permitAll()
            .successHandler(authSuccessHandler)
            .failureHandler(authFailureHandler)
            .and()
            .logout().logoutUrl("/access/logout")
            .invalidateHttpSession(true)
            .logoutSuccessHandler(logoutSuccessHandler)
            .and()
            .apply(new SpringSocialConfigurer()
                    .postLoginUrl("/home")
                    .alwaysUsePostLoginUrl(true));

    http.authorizeRequests()
            .antMatchers(LOGIN_PATH).permitAll()
            .anyRequest().authenticated();
}
...

The complete source code is available here : https://github.com/gael-jurin/gwtp-spring-security I hope this will help many developers to customize Oauth2 authorization and authentication process via providers. This implementation supports the Oauth2 authorization code flow. Thanks to Spring community for this huge work

GWT 2.5 easy Bean validation

The goal of this paper is to setup quickly and smoothly a clean validation process on your DTO. It shows an easy setup using GIN and GWT-Dispatch. An Intro is also available here Gwt 2.5 Validation Intro

The needed libraires to start are: Javax validation (Api), Hibernate validators (Sources and Implementation) and Slf4j (logger).
For the next step we are going to build a custom annotation which will be use into our gwt-dispatch actions:
Here the code for custom validator and his annotation:

@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = RequiredValidator.class)
public @interface Required {
    String message() default "Value is required";
    Class[] groups() default {};
    Class[] payload() default {};
}

 

public class RequiredValidator implements ConstraintValidator {
    @Override
    public void initialize(Required constraintAnnotation) { }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (!Strings.isNullOrEmpty(value));
        // use Guava strings to check null or empty value
    }
}

Now we can use our required annotation into our action (There is also a possibility to use more than one annotation on our field):

public class SendMailAction extends ActionImpl {
    @Required
    private String email;
  ...
    private SendMailAction(String email) {
       this.email = email;
    }
  ...
  // add accessors
}

To complete module settings we need a validator factory for the module like it is shown into the Gwt Devguide:

public final class ClientValidatorFactory extends AbstractGwtValidatorFactory {

    // Syntax for multiple class:  @GwtValidation({action1.class, action2.class})
    @GwtValidation(SendMailAction.class)
    public interface GwtValidator extends Validator {
    }
    @Override
    public AbstractGwtValidator createValidator() {
        return GWT.create(GwtValidator.class);
    }
}

Now we are going to write a generic constraint validation manager which will be provided using Gin Injector. Here it is :

public class ClientValidation extends Validation {
    private Validator validator;
    private Set<ConstraintViolation> constraintViolations;

    public ClientValidation() {
        this.validator = buildDefaultValidatorFactory().getValidator();
    }
    public Set<ConstraintViolation> getConstraintViolations(Object object) {
        this.constraintViolations = validator.validate(object);
        return constraintViolations;
    }
    public String getPrimaryMessage() {
        return constraintViolations.iterator().next().getMessage();
    }
}

Finally we can configure one single instance into our module:

...
bind(ClientValidation.class).in(Singleton.class);

and use it everywhere on client side with @Inject:

if (!clientValidation.getConstraintViolations(new sendMailAction(email)).isEmpty()) {
            alert(clientValidation.getPrimaryMessage());
} else {
    // do something
}

Thanks to the GWT team for those enhancements for client side validation.