Spring Oauth2 with Authorization Code

In this article I’ll show how to develop a solution that uses OAuth2 as authentication protocol with Authorization code during the flow process.

Spring Framework has been used as backbone of the solution and the user’s token generated have been persisted in a MySQL Database.


The OAuth2 authentication flow uses an authorization code expects a flow like the following:

Oauth2.0 Authorization Code

Briefly, the Client asks the Server an authentication code in order to access to the protected resource. Only in this way the Client can receive a generated token.

The server checks whether the Client has already authenticated or not. It asks that to the Authentication Server.

Once authenticated in the Authentication Server, the Server asks the Client to accept the data that the application is going to access (e.g. nickname, email, birthday, …).

If the Client accepts that disclaimer, the Server generates an access token which is used, for the time it’s not expired, to make the request for the protected resources.

I hope the scenario is clear so far.

Now, as it’s shown in the diagram, I’ve used a persistence layer installed in the Server. That layer has the follow schema.

/*
* Table oauth_access_token
*/
CREATE TABLE `oauth_access_token` (
`token_id` varchar(256) DEFAULT NULL,
`token` blob,
`authentication_id` varchar(256) DEFAULT NULL,
`user_name` varchar(256) DEFAULT NULL,
`client_id` varchar(256) DEFAULT NULL,
`authentication` blob,
`refresh_token` varchar(256) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

/*
* Table oauth_client_details
*/
CREATE TABLE `oauth_client_details` (
`client_id` varchar(256) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

/*
* Table oauth_code
*/
CREATE TABLE `oauth_code` (
`code` varchar(256) DEFAULT NULL,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

/*
* Table oauth_refresh_token
*/
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(256) DEFAULT NULL,
`token` blob,
`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

These schemas have been generated according to Spring Security OAuth 2 version 1.0.5. I used this version although this is not the last released.

The rest of the libraries included are in this maven file:

	<properties>
		<springsec.version>3.1.3.RELEASE</springsec.version>
		<spring.version>3.1.2.RELEASE</spring.version>
		<jersey-version>1.11</jersey-version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security.oauth</groupId>
			<artifactId>spring-security-oauth2</artifactId>
			<version>1.0.5.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>

		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-server</artifactId>
			<version>${jersey-version}</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey.contribs</groupId>
			<artifactId>jersey-apache-client</artifactId>
			<version>${jersey-version}</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey.contribs</groupId>
			<artifactId>jersey-spring</artifactId>
			<version>${jersey-version}</version>
			<exclusions>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-beans</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-context</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-web</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.springframework</groupId>
					<artifactId>spring-aop</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-json</artifactId>
			<version>${jersey-version}</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-client</artifactId>
			<version>${jersey-version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${springsec.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${springsec.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${springsec.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-aspects</artifactId>
			<version>${springsec.version}</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.31</version>
		</dependency>
	</dependencies>

I’m speaking about Spring, so let’s see the core of the configuration code.


<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
	xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- Entry Url -->
	<sec:http pattern="/oauth/token" create-session="stateless"
		entry-point-ref="oauthAuthenticationEntryPoint"
		authentication-manager-ref="authenticationManager">
		<sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
		<sec:anonymous enabled="false" />
 		<sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
		<sec:access-denied-handler ref="oauthAccessDeniedHandler" />
	</sec:http>

	<sec:http pattern="/oauth/me" create-session="never"
        entry-point-ref="oauthAuthenticationEntryPoint">
        <sec:anonymous enabled="false" />
        <sec:intercept-url pattern="/oauth/me" method="GET" access="IS_AUTHENTICATED_FULLY" />
        <sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
        <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
    </sec:http>

	<sec:http pattern="/oauth/authorize" create-session="never"
		entry-point-ref="oauthAuthenticationEntryPoint">
		<sec:anonymous enabled="false" />
		<sec:intercept-url pattern="/oauth/authorize"
			method="GET" access="IS_AUTHENTICATED_FULLY" />
		<sec:custom-filter ref="userCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
	</sec:http>
	<!-- End Entry Url -->

	<!-- Server -->
	<oauth:authorization-server
		client-details-service-ref="clientDetails" token-services-ref="tokenServices"
		authorization-request-manager-ref="SECAuthorizationRequestManager"
		user-approval-handler-ref="SECTokenServicesUserApprovalHandler"
		user-approval-page="forward:/oauth/confirm_access_custom">
		<oauth:authorization-code
			authorization-code-services-ref="JdbcAuthorizationCodeServices" />
		<oauth:implicit />
		<oauth:refresh-token />
		<oauth:client-credentials />
	</oauth:authorization-server>

	<!-- Resource -->
	<oauth:resource-server id="resourceServerFilter"
		resource-id="springsec" token-services-ref="tokenServices" />

	<bean id="oauthAuthenticationEntryPoint"
		class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
	</bean>

	<bean id="oauthAccessDeniedHandler"
		class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
	</bean>

	<bean id="clientCredentialsTokenEndpointFilter" class="it.oauth.oauthSEC.security.SECClientCredentialToken">
		<property name="authenticationManager" ref="authenticationManager" />
		<property name="clientDetails" ref="clientDetails" />
	</bean>

	<bean id="userCredentialsTokenEndpointFilter" class="it.oauth.oauthSEC.security.SECUserCredential">
		<property name="authenticationManager" ref="authenticationManager" />
	</bean>

	<sec:authentication-manager alias="authenticationManager">
		<sec:authentication-provider
			user-service-ref="clientDetailsUserService" />
	</sec:authentication-manager>

	<bean id="clientDetailsUserService"
		class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
		<constructor-arg ref="clientDetails" />
	</bean>

	<bean id="clientDetails" class="it.oauth.oauthSEC.security.SECClientDetailsService">
		<property name="id" value="test" />
		<property name="secretKey" value="mycompanykey" />
	</bean>

	<bean id="SECTokenServicesUserApprovalHandler"
		class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
		<property name="tokenServices" ref="tokenServices" />

	</bean>

	<bean id="SECAuthorizationRequestManager" class="org.springframework.security.oauth2.provider.DefaultAuthorizationRequestManager">
		<constructor-arg ref="clientDetails" />
	</bean>

	<bean id="tokenServices"
		class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
		<property name="tokenStore" ref="tokenStore" />
		<property name="supportRefreshToken" value="true" />
		<property name="clientDetailsService" ref="clientDetails" />
	</bean>

	<bean id="JdbcAuthorizationCodeServices"
		class="org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices">
		<constructor-arg ref="jdbcTemplate" />
	</bean>	

	<bean id="tokenStore"
		class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
		<constructor-arg ref="jdbcTemplate" />
	</bean>	

	<bean id="jdbcTemplate"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://localhost:3306/oauthdb" />
		<property name="username" value="root" />
		<property name="password" value="" />
	</bean>
	<!-- End Persistence Layer -->

	<!-- ModelViewController-->
	<mvc:annotation-driven />

	<mvc:default-servlet-handler />

	<context:annotation-config />

	<context:component-scan base-package="it.oauth.oauthSEC.controller" />

	<bean
		class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
		<property name="mediaTypes">
			<map>
				<entry key="htm" value="text/htm" />
				<entry key="html" value="text/html" />
				<entry key="json" value="application/json" />
			</map>
		</property>
		<property name="viewResolvers">
			<list>
				<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
				<bean
					class="org.springframework.web.servlet.view.InternalResourceViewResolver">
					<property name="prefix" value="/WEB-INF/jsp/" />
					<property name="suffix" value=".jsp" />
				</bean>
			</list>
		</property>

		<property name="defaultViews">
			<list>
				<bean
					class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
			</list>
		</property>
	</bean>

</beans>

Essentially, The file is composed by three parts. The first part includes the security level for the url’s called by the client.
The middle part is about the integration between Spring security and OAuth2 while the last is the Spring Mvc Controller.

Now, I’m not going to speak about the last part because I’d like to draw the attention on the first and the second part.

The aim of my article is to show how use Spring OAuth2 without using the Spring Authentication or, at least, how to include a third part which not adopts the Spring authentication form.

How can I do it? Well, put the attention on the classes:

it.demo.oauthSEC.security.SECUserCredential
it.demo.oauthSEC.security.SECClientCredentialToken

The first is used during the authorization phase (/oauth/authorize) while the second is called when a new token is going to be released (/oauth/token).

In this code is missing the business rule that will be applied for checking the user authentication in third part system. This depends by the third part system.

The user is authenticated as “myUser”. I check the cookie value to understand whether the user is valid or not. This rules should be demanded at the Authentication Server.

public class SECUserCredential extends ClientCredentialsTokenEndpointFilter {

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException,
			IOException, ServletException {

		/*
		 * Business Rules for making the userCredentials
		 * ...
		 * ...
		 */
		if (SECProxy.isCookieValid(request)) {
			UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
					"myUser", "mycompanykey");

			// Authenticate the user
			Authentication auth = this.getAuthenticationManager().authenticate(
					authRequest);

			return auth;
		} else {

			// No user Authenticated
			Authentication auth = this.getAuthenticationManager().authenticate(
					new UsernamePasswordAuthenticationToken("invalidUser",
							"invalidCompanyName"));
			auth.setAuthenticated(false);

			return auth;
		}
	}

	@Override
	protected boolean requiresAuthentication(HttpServletRequest request,
			HttpServletResponse response) {

		return true;
	}
}

Now check the clientId.

public class SECClientCredentialToken extends
		ClientCredentialsTokenEndpointFilter {

	private SECClientDetailsService clientDetails;

	@Override
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException,
			IOException, ServletException {

		if (clientDetails.getId().equals(request.getParameter("client_id"))) {

			UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
					clientDetails.getId(), "mycompanykey");

			// Authenticate the user
			Authentication auth = this.getAuthenticationManager().authenticate(authRequest);

			return auth;
		} else {
			// No user Authenticated
			Authentication auth = this.getAuthenticationManager().authenticate(
					new UsernamePasswordAuthenticationToken("invalidUser",
							"invalidCompanyName"));
			auth.setAuthenticated(false);

			return auth;
		}

	}

	@Override
	protected boolean requiresAuthentication(HttpServletRequest request,
			HttpServletResponse response) {

		return true;
	}

	public SECClientDetailsService getClientDetails() {
		return clientDetails;
	}

	public void setClientDetails(SECClientDetailsService clientDetails) {
		this.clientDetails = clientDetails;
	}
}

In both codes, if the authentication failed, the method would return an unauthorized user by setting the line:
auth.setAuthenticated(false);

The others parts of the code are not so important for the article’s scope. The SECClientDetails is the bean implementation of the interface ClientDetails

public class SECClientDetails implements ClientDetails{

	protected String clientId;
	protected Set<String> resourceIds;
	protected boolean secretRequired;
	protected String clientSecret;

	protected boolean scoped;

	public Set<String> scope;

	public Set<String> authorizedGrantTypes;

	public Set<String> registeredRedirectUri;

	public Collection<GrantedAuthority> authorities;

	public Integer accessTokenValiditySeconds;

	public Integer refreshTokenValiditySeconds;

	public Map<String, Object> additionalInformation;

	public SECClientDetails(String clientId, Set<String> scope,
          Set<String> authorizedGrantTypes, Collection<GrantedAuthority> authorities)
	{
		this.clientId=clientId;
		this.scope=scope;
		this.authorizedGrantTypes=authorizedGrantTypes;
		this.authorities=authorities;
	}

	public String getClientId() {
		return clientId;
	}

	public Set<String> getResourceIds() {
		return resourceIds;
	}

	public boolean isSecretRequired() {
		return secretRequired;
	}

	public String getClientSecret() {
		return clientSecret;
	}

	public boolean isScoped() {
		return scoped;
	}

	public Set<String> getScope() {
		return scope;
	}

	public Set<String> getAuthorizedGrantTypes() {
		return authorizedGrantTypes;
	}

	public Set<String> getRegisteredRedirectUri() {
		return registeredRedirectUri;
	}

	public Collection<GrantedAuthority> getAuthorities() {
		return authorities;
	}

	public Integer getAccessTokenValiditySeconds() {
		return accessTokenValiditySeconds;
	}

	public Integer getRefreshTokenValiditySeconds() {
		return refreshTokenValiditySeconds;
	}

	public Map<String, Object> getAdditionalInformation() {
		return additionalInformation;
	}
}

Followed by SECClientDetailsService which implements the ClientDetailsService interface.

public class SECClientDetailsService implements ClientDetailsService {

	private String id;
	private String secretKey;

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getSecretKey() {
		return secretKey;
	}

	public void setSecretKey(String secretKey) {
		this.secretKey = secretKey;
	}

	/**
	 * Carico i dettaglio dell'utente
	 */
	public ClientDetails loadClientByClientId(String clientId)
			throws OAuth2Exception {
		List<String> authorizedGrantTypes = new ArrayList<String>();
 		authorizedGrantTypes.add("password");
		authorizedGrantTypes.add("refresh_token");
		authorizedGrantTypes.add("client_credentials");
		authorizedGrantTypes.add("authorization_code");
		authorizedGrantTypes.add("implicit");

		BaseClientDetails clientDetails = new BaseClientDetails();
		clientDetails.setClientId(id);
		clientDetails.setClientSecret(secretKey);
		clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);

		return clientDetails;
	}
}

Now, the idea, as described in the upper flow chart, is that the Client makes a request to the Server

/OAuthServer/oauth/authorize?response_type=code&redirect_uri=/OAuthClient/login&client_id=test

In this calling will be needed to check the user credentials in the “attemptAuthentication” method.
Once user is authenticated, the Framework will show the confirmation Page.

OAuth_confirm

Next, the User confirms the access authorization and an Authorization Code will be released as parameter of the redirect_uri path (e.g. /OAuthClient/login?code=HTSJKS).

The Client is now able to get the Access Token from the Server by calling the Url:

/OAuthServer/oauth/token?grant_type=authorization_code&code=[Authorization_code]&redirect_uri=/OAuthClient/login&client_id=test
e.g.
/OAuthServer/oauth/token?grant_type=authorization_code&code=HTSJKS&redirect_uri=/OAuthClient/login&client_id=test

The result is a JSON like this:

{
   "access_token":"ba452456-3536-4f56-ac7f-67a2bb7c4ddc",
   "token_type":"bearer",
   "refresh_token":"53386110-dec2-4567-93ef-79359ae34f01",
   "expires_in":43199
}

According with the OAuth2 protocol specification, the response contains the Access Token and the type of token.
Each value are helpful for calling the protected resource. In order to do so, the calling has to be similar to this request.

GET http://localhost.:8080/OAuthServer/oauth/me HTTP/1.1
User-Agent: Fiddler
Authorization: Bearer ba452456-3536-4f56-ac7f-67a2bb7c4ddc
Host: localhost.:8080

The calling has been made using “authorization type” Bearer and the value is the access token.

Have a look at the database:
select * FROM oauthdb.oauth_access_token

The result is the follow:

oauth2_table

The columns token and authentication contain the serialized token and authentication object.
The token_id and refresh_token are the crypted access token values.

That’s all. I wouldn’t say that this is the best solution to get the integration between Spring OAuth2 and an external Authentication Server but I could say that it works with a brief effort.

Please, don’t ask me to release the complete solution because it’s only a prototype and I haven’t built a complete solution.

However, I hope this article could help anyone who has to solve that issue and suggestions are welcome!

Advertisements

4 thoughts on “Spring Oauth2 with Authorization Code

    • Hi Mirza.
      As I wrote in the footer, I don’t have the entire working solution; this is only a snippet of the authentication layer. You can get all the code that I used in this post.

  1. Hi,
    Thank you for the explanation. I new to spring, spring security and Oauth2. This tutorial helped alot.
    I implemented your tutorial but i couldn’t see login page, it directly goes to authorization page.
    I was wondering how to implement login and then user login and then authorization page

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s