OAuth with Spring Security
There are a lot of posts talking about OAuth from Client Side, for example how to connect to service providers like Twitter or Facebook, but there are less posts about OAuth but from Server Side, more specificaly how to implement an authentication mechanism using OAuth for protecting resources, and not for accessing them (Client Side Part).
In this post I will talk about how to protect your resources, using Spring Security (Spring Security OAuth). The example will be simple enough to understand the basics for implementing an OAuth service provider.
I have found this post that explains with a simple example, what OAuth is and how it works. I think it is a good starting point with OAuth http://hueniverse.com/2007/10/beginners-guide-to-oauth-part-ii-protocol-workflow/
Now it is time to start writing our service provider. First of all I will explain what our Service Provider will offer.
Imagine you are developing a website (called CV) where users will register and after that they will be able to upload their Curriculum Vitae. Now we are going to transform this website to a Service Provider where OAuth will be used for protecting resources (Curriculm Vitae of registered users). Imagine again that some companies have agreed with CV people that when they publish job vacances, users will have the possibility of uploading their curriculum directly from CV site to HR department instead of sending by email or copy & paste from document. As you can see here is where OAuth starts managing security between CV website and Company RH site.
In summary we have a Curriculum Vitae Service Provider (CV) with protected resource (document itself). Companies that offer users the possibility of acquiring directly their Curriculum Vitae from CV are the Consumers. So when a user visits company job vacancies (in our example called fooCompany) and wants to apply for a job, he only has to authorize FooCompany “Job Vacancies” website with permissions to download its Curriculum Vitae from CV site.
Because we will use Spring Security for OAuth authentication, first of all we are going to configure Spring Security into SpringMVC CV application. Nothing special here:
In web.xml file we define Security Filter:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
And in root-context.xml we define protected resources and authentication manager. In this case In memory apporoach is used:
<http auto-config='true'> <intercept-url pattern="/**" access="ROLE_USER" /> </http> <authentication-manager> <authentication-provider> <user-service> <user name="leonard" password="nimoy" authorities="ROLE_USER" /> </user-service> </authentication-provider> </authentication-manager>
Next step, create an Spring Controller that returns the Curriculum Vitae of logged user:
@RequestMapping(value="/cvs", method=RequestMethod.GET) @ResponseBody public String loadCV() { StringBuilder cv = new StringBuilder(); cv.append("Curriculum Vitae -- Name: ").append(getUserName()).append(" Experience: Java, Spring Security, ..."); return cv.toString(); } private String getUserName() { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); String username; if (principal instanceof UserDetails) { username = ((UserDetails)principal).getUsername(); } else { username = principal.toString(); } return username; }
This controller returns directly a String, instead a ModelView object. This String is sent directly as HttpServletResponse.
Now we have got a simple website that returns the Curriculum Vitae of logged user. If you try to access to /cvs resource, if you are not authenticated, Spring Security will show you a login page, and if you are already logged, your job experience will be returned. Works as any other website that are using Spring Security.
Next step is modifing this project for allowing external sites can access to protected resources using OAuth 2 authentication protocol.
In root-context.xml:
<beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.InMemoryOAuth2ProviderTokenServices"> <beans:property name="supportRefreshToken" value="true" /> </beans:bean> <oauth:provider client-details-service-ref="clientDetails" token-services-ref="tokenServices"> <oauth:verification-code user-approval-page="/oauth/confirm_access" /> </oauth:provider> <oauth:client-details-service id="clientDetails"> <oauth:client clientId="foo" authorizedGrantTypes="authorization_code" /> </oauth:client-details-service>
First bean, is an OAuth2ProviderTokenServices interface implementation with id tokenServices. The OAuth2ProviderTokenServices interface defines operations that are necessary to manage OAuth 2.0 tokens. These tokens should be stored for subsequent access token can reference it. For this example InMemory store is enough.
Next bean is <oauth:provider>. This tag is used to configure the OAuth 2.0 provider mechanism. And in this case three parameters are configured; the first one is a reference to a bean that defines the client details service, explained in next paragraph. The second one is token service for providing tokens, explained in previous paragraph, and the last one is the URL at which a request for authorization token will be serviced. This is the typically Authorize/Denny page where service provider asks to user if it permits the Consumer (in our case fooCompany) accessing to protected resources (its Curriculum Vitae).
Last bean is <oauth:client-details-service>. In this tag you define which clients you authorize to access to protected resources with previous authentication. In this case because CV company has agreed with foo company that they can connect to its Curriculum Vitae Service, a client is defined with id foo.
Now we have our application configured with OAuth. Last step is creating a controller for taking requests from /oauth/confirm_access URL.
private ClientAuthenticationCache authenticationCache = new DefaultClientAuthenticationCache(); private ClientDetailsService clientDetailsService; @RequestMapping(value="/oauth/confirm_access") public ModelAndView accessConfirmation(HttpServletRequest request, HttpServletResponse response) { ClientAuthenticationToken clientAuth = getAuthenticationCache().getAuthentication(request, response); if (clientAuth == null) { throw new IllegalStateException("No client authentication request to authorize."); } ClientDetails client = getClientDetailsService().loadClientByClientId(clientAuth.getClientId()); TreeMap<String, Object> model = new TreeMap<String, Object>(); model.put("auth_request", clientAuth); model.put("client", client); return new ModelAndView("access_confirmation", model); }
This controller returns a ModelAndView object with client information and which page should be shown for granting permission to protected resources. This JSP page is called access_confirmation.jsp and the most important part is:
<div id="content"> <% if (session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) != null && !(session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION) instanceof UnapprovedClientAuthenticationException)) { %> <div class="error"> <p>Access could not be granted. (<%= ((AuthenticationException) session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION)).getMessage() %>)</p> </div> <% } %> <c:remove scope="session" var="SPRING_SECURITY_LAST_EXCEPTION"/> <authz:authorize ifAllGranted="ROLE_USER"> <h2>Please Confirm</h2> <p>You hereby authorize <c:out value="${client.clientId}"/> to access your protected resources.</p> <form id="confirmationForm" name="confirmationForm" action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="post"> <input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>" value="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/> <label><input name="authorize" value="Authorize" type="submit"/></label> </form> <form id="denialForm" name="denialForm" action="<%=request.getContextPath() + VerificationCodeFilter.DEFAULT_PROCESSING_URL%>" method="post"> <input name="<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_REQUEST_PARAMETER%>" value="not_<%=BasicUserApprovalFilter.DEFAULT_APPROVAL_PARAMETER_VALUE%>" type="hidden"/> <label><input name="deny" value="Deny" type="submit"/></label> </form> </authz:authorize> </div>
As you can see Spring Security OAuth provides helper classes for creating confirmation form and deny form. When the result is submitted, URL /cv/oauth/user/authorize (internally managed) is called, there OAuth decides if returns protected resource (String returned by loadCV() method) to caller or not depending on what option user has chosen.
And that’s all about creating an OAuth 2 system using Spring Security OAuth. But I suppose you are wondering how to test it, so for the same price I will explain how to write the client part (Consumer) using Spring Security OAuth too.
Client application (called fooCompany) is also a SpringMVC web application with Spring Security.
Spring Security part will be ignored here.
The client application contains a home page (home.jsp) that has a link to Spring Controller that will be responsible to download Curriculum Vitae from CV site, and redirecting content to a view (show.jsp).
@RequestMapping(value="/cv") public ModelAndView getCV() { String cv = cvService.getCVContent(); Map<String, String> params = new HashMap<String, String>(); params.put("cv", cv); ModelAndView modelAndView = new ModelAndView("show", params); return modelAndView; }
As you can see is a simple Controller that calls a Curriculum Vitae service. This service will be responsible to connect to CV website, and download required Curriculum Vitae. Of course it deals with OAuth communication protocol too.
Service looks:
public String getCVContent() { byte[] content = (getCvRestTemplate().getForObject(URI.create(cvURL), byte[].class)); return new String(content); }
The suggested method for accessing those resources is by using Rest. For this porpose Spring Security OAuth provides an extension of RestTemplate for dealing with OAuth protocol. This class (OAuth2RestTemplate) manages connection to required resources and also manages tokens, OAuth authorization protocol, …
OAuth2RestTemplate is injected into CVService, and it is configured into root-context.xml:
<oauth:client token-services-ref="oauth2TokenServices" /> <beans:bean id="oauth2TokenServices" class="org.springframework.security.oauth2.consumer.token.InMemoryOAuth2ClientTokenServices" /> <oauth:resource id="cv" type="authorization_code" clientId="foo" accessTokenUri="http://localhost:8080/cv/oauth/authorize" userAuthorizationUri="http://localhost:8080/cv/oauth/user/authorize" /> <beans:bean id="cvService" class="org.springsource.oauth.CVServiceImpl"> <beans:property name="cvURL" value="http://localhost:8080/cv/cvs"></beans:property> <beans:property name="cvRestTemplate"> <beans:bean class="org.springframework.security.oauth2.consumer.OAuth2RestTemplate"> <beans:constructor-arg ref="cv"/> </beans:bean> </beans:property> <beans:property name="tokenServices" ref="oauth2TokenServices"></beans:property> </beans:bean>
See that OAuth2RestTemplate is created using an OAuth resource that contains all information about where to connect for authorizing access to protected resource, and in this case is CV website, see that we are referencing an external website, although in this example we are using localhost. Also service provider URL (http://localhost:8080/cvs/cv) is set, so RestTemplate can establish a connection to content provider, and in case that authorization process ends successful, retrieving requested information.
<oauth:resource> defines OAuth resources, in this case, the name of the client (remember that this value was configured in server side client details tag for granting access to OAuth protocol). Also userAuthorizationUri is defined. This is the URI to which the user will be redirected if the user is ever needed to authorize access to the resource (this is an internal URI managed by Spring Security OAuth). And finally accessTokenUri, the URI OAuth provider endpoint that provides the access token (internal URI too).
Also creating a consumer using Spring Security OAuth is simple enough.
Now I will explain the sequence of events that happens when a user wants to give access to foo company for retrieving its Curriculum Vitae.
First of all user connects to foo website, and click on post curriculum vitae link. Then getCV method from controller is called. This method calls cvService, that at the same time creates a connection to resource URI (CV) using OAuth2RestTemplate. And this class acts as a black box, from client side, you don’t know exactly what this class will do but it returns your Curriculum Vitae stored in CV website. As you can imagine this class manages all workflow related to OAuth, like managing tokens, executing required URL redirections to get permissions, … and if all steps are performed successful, stored Curriculum Vitae in CV site will be sent to foo company site.
And that’s all steps required to allow your site to act as Service Provider using OAuth2 authorization protocol. Thanks of Spring Security folks, it is much easier that you may think at first.
Hope you find it useful.
Download ServerSide (CV)
Download ClientSide (fooCompany)
Reference: OAuth with Spring Security from our JCG partner Alex Soto at the One Jar To Rule Them All blog.
Hi Eleftheria,
I created war file of both client and server part and deployed in a tomcat server.
Getting the following error … what could be the possible reason
ERROR: org.springframework.web.context.ContextLoader – Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.security.web.context.HttpSessionSecurityContextRepository#0’: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.security.web.context.HttpSessionSecurityContextRepository]: Constructor threw exception; nested exception is java.lang.ExceptionInInitializerError
Changing the tomcat server resolved the issue but any how I need to check the root cause for this issue …
Thanks
I tried to run your examples. I am getting the following errors.
cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element ‘oauth:provider’.
and
Multiple annotations found at this line: – cvc-complex-type.3.2.2: Attribute ‘clientId’ is not allowed to appear in element ‘oauth:client’. – cvc-complex-type.3.2.2: Attribute ‘authorizedGrantTypes’ is not allowed to appear in element ‘oauth:client’.
I tried lot to get correct xsd version, but could not find. Could you please help?
did you find solution?please let me know
This application uses OAuth 1.0a and needs the following xsd in the xml file.
http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
Use the following versions of spring jars
Spring- 3.0.x
spring-security-oauth-1.0.0.M3.jar
spring-security–3.0.x.jar
use http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd in your xml file
Use the following jars
Spring- 3.0.x
spring-security-oauth-1.0.0.M3.jar
spring-security–3.0.x.jar
Same problem here. The project will not compile. Does anyone have a solution?
Hi,
Requesting you to please upload screenshots of flow of functionalities !!
Hi, Thanks A Lot for your tutorial !! it is simply AWESOME!!!! But i just stuck up at the last point , please look on this ::::: When i clicked on “Authorize” i did not receive “CV” rather my console get populate with following warnings:: WARN : org.springframework.security.oauth2.provider.verification.DefaultClientAuthenticationCache – Unable to save client authentication because the request doesn’t have a session! WARN : org.springframework.web.servlet.PageNotFound – No mapping found for HTTP request with URI [/cv/style.css] in DispatcherServlet with name ‘appServlet’ WARN : org.springframework.web.client.RestTemplate – POST request for “http://localhost:8080/cv/oauth/authorize” resulted in 404 (Not found); invoking error handler And on browser i received… Read more »
Thanks A TON!! Its working…… :)
Can you let know how you get around this problem
No mapping found for HTTP request with URI [/cv/] in DispatcherServlet with name ‘appServlet’
Hello Friends,
I am also getting the following error:
org.springframework.web.servlet.PageNotFound – No mapping found for HTTP request with URI [/cv/cvs/] in DispatcherServlet with name ‘appServlet’
Plz help me, how I can fix this issue.
Thanks.
deploy first cv and then fooCompany
after deploy you need to access the from client(fooCompany)
How you fixed the problem. can you please explain me the fix.
Thanks
Hi, Alex Soto
The following tutorial was very much useful.
Can you please provide a REST version of the above tutorial.
thanks in advance
Is it oauth2 implementation? If not please provide oauth2 + spring code.
Hi Alex, I know this is an old post, but I hope you could give me a hand. I’m new to OAuth and I need to create a simple web application for track expenses, with some basic actions (user must be able to create an account and log in, list expenses, edit them, etc) with a REST API for each one, and the trick is that I need to be able to pass credentials to both the webpage and the API. So, after some research I’ve found some examples using Digest Authentication and HMAC Authentication but lot of posts also… Read more »
Very good example. Thanks. I intercepted http messages between the client and the provider and attach it here for those interested: Authorization Request: POST /cv/oauth/authorize HTTP/1.1 Accept: application/json, application/x-www-form-urlencoded Content-Type: application/x-www-form-urlencoded User-Agent: Java/1.7.0_67 Host: localhost:8090 Connection: keep-alive Content-Length: 116 grant_type=authorization_code&client_id=foo&code=u6TI91&redirect_uri=http%3A%2F%2Flocalhost%3A8888%2FfooCompany%2Fcv Authorization Response: HTTP/1.1 200 OK Cache-Control: no-store Content-Type: application/json; charset=ISO-8859-1 Expires: Thu, 01-Jan-1970 00:00:00 GMT Set-Cookie: JSESSIONID=i0h538jw605pjijndwas2wn1;Path=/cv Content-Length: 142 Server: Jetty(6.1.25) { “access_token”: “6c481f27-fed5-4600-b407-8532f05191be”, “expires_in”: 43199, “refresh_token”: “2283d538-a60d-4f48-addd-5f794bb1eea4” } Access to resource Request: GET /cv/cvs HTTP/1.1 Authorization: OAuth2 6c481f27-fed5-4600-b407-8532f05191be Accept: application/octet-stream, */* User-Agent: Java/1.7.0_67 Host: localhost:8090 Connection: keep-alive Access to resource Response: HTTP/1.1 200 OK Content-Type: application/octet-stream Accept-Charset: big5, big5-hkscs,… Read more »
I’m trying to test an “expired token” scenario , setting the tokenServices as:
but It doesn’t allow me to re-authenticate when the token expires.
Any idea there?
Hello, Thanks for sharing source code. We’re looking for the Spring Security OAuth2 code which will return OAuth_Token, Refresh_Token etc.
I’m inspired by link https://techannotation.wordpress.com/2014/04/29/5-minutes-with-spring-oauth-2-0/, but certainly don’t have proper code to look at it.
Thanks,
The “Authorize” from the CV application doesn’t seem to work while switching to https protocol. Anyone tried this out?
thanks
Dez
Very good example.
This application is working fine for me.
But when I am intercepting the request and response, I can’t see the access token
Hello, I see Spring OAuth2 version is using this code is too old. May I request you to update this code for new maven dependencies?
Thanks,
Neha
How to intercept the the messages? Can you please let me know?