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

Securing Spring Faces (Webflow 2 + Spring Security 3) application.

The post is for people who are using Spring Faces and need basic security aspects.

Update your spring faces dependency in the pom.xml if your project is a maven one :
There is a bug in the 2.3.0 Release between the flow executor and phase listener. This is important because our login page is a facelet which is not in a flow.

<dependency>
	<groupId>org.springframework.webflow</groupId>
	<artifactId>spring-faces</artifactId>
	<version>2.3.1.BUILD-SNAPSHOT</version>
</dependency>

Add context security file to the configuration with this content :

<security:http auto-config="true">
		<security:intercept-url pattern="/views/**" access="ROLE_USER"/>
		
		<security:form-login 
			login-processing-url="/j_spring_security_check"
			login-page="/" authentication-failure-url="/"
			default-target-url="/flowprocess/shopping" />
			
		<security:logout/>
	</security:http>
	
	<security:user-service id="userService">
    		<security:user name="guest" password="guest" authorities="ROLE_USER"/>
  	</security:user-service>
	
	<security:authentication-manager>
		<security:authentication-provider user-service-ref="userService"/>
	</security:authentication-manager>

Configuring security into webflow in 2 steps:

1 – Adding secure flow context listener into the configuration file as shown below :

<beans:bean id="securityFlowExecutionListener" class="org.springframework.webflow.security.SecurityFlowExecutionListener" />

<beans:bean id="facesContextListener" class="org.springframework.faces.webflow.FlowFacesContextLifecycleListener"/>

<flow-executor id="flowExecutor">
	<flow-execution-listeners>
		<listener ref="facesContextListener"/>
		<listener ref="securityFlowExecutionListener"/>
	</flow-execution-listeners>
</flow-executor>

2 – Configure the flow to check access and authorizations before runing by adding the secure tag into the flow.xml file:

<flow start-state="identifyCustomer" xmlns="http://www.springframework.org/schema/webflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/webflow
	http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

<secured attributes="ROLE_USER"/>
…

</flow>

Very Important Notice : If we’re using subflows in our architecture, Spring does not propagate the secured configuration to child. Secure tag must be explicitely mentionned in all our subflows. I hope this will be a fix in further releases.

Create a Spring security form-login as index page into the project.

<h:form prependId="false" >
			
	<table class="form">
		<tr>
		<td><h:outputLabel value="Identifiant: " for="j_username" /></td>
		<td><h:inputText id="j_username" required="true" value="#{authenticationBean.username}" styleClass="textInput" /></td>
		
		</tr>
		<tr>
		<td><h:outputLabel value="Mot de passe " for="j_password" /></td>
		<td><h:inputSecret id="j_password" required="true" value="#{authenticationBean.password}" styleClass="textInput" /></td>
		</tr>
	</table>
				 
	<div class="buttonContainer">
		<h:commandLink styleClass="styledButton" action="#{authenticationBean.onLogin}" value="Connect"  />
	</div>
					
</h:form>

Now the flow is secure and to execute it, user should give corrects credentials. In the next article i’ll integrate OpenID authentication.

In my project i have shopping-flow wich use customer-flow as subflow. To check the point in notice above, try to call directly the shopping/customer here unsecured access to the subflow and secured access, redirection to the login

The complete project source code is available here github