Hello,
Last week I've investigated how does OAuth2 protocol works and developed a Proof of Concept (PoC) in Java. In this post I would like to show you how effortlessly develop simple client-server application using OAuth 2.0 standard for authorization of protected resources placed on a server.
Before we start developing our first secured web application with OAuth2 let's understand how it works.
What is it and how does it work?
Basically, OAuth is an open standard for authorization (not authentication) which provides to clients a secure access to server resources on behalf of a resource owner. It specifies a process for resource owners to authorize third-party access to their server resources without sharing their credentials. OAuth 2.0 replaced OAuth 1.0 which was more complicated. OAuth 2.0 doesn't require digital signatures but whole communication goes through SSL protocol. [1] You can find more information about it in references in the last section.
OAuth 2.0 standard defines four following roles:
- Resource owner is the user who authorizes an application to access its account;
- Client is the application (web, native, mobile etc.) that wants to access the user's resources;
- Resource server the web server from which you are trying to get the resources;
- Authorization server this is the server that owns the user identities and credentials. It verifies the identity of the user.
Below, you can find the authorization code flow of the simple token-based authorization system that I've found in this very interesting article [2].
Figure 1 - Graphical representation of the authorization code flow for OAuth2 framework [2]
- The application requests authorization to access service resources that belongs to the user.
- User logs into a new application in order to authorize the previous one. User is presented with the option to grant or deny access for the application.
- Server redirects back to the application sending an authorization code.
- The application then takes that code and sends another request, this time for access token.
- At last the authorization server verifies the authorization code and returns token.
After authorization, our application can request resources from the resource server, presenting its access token for authentication. If the access token is valid, the server gives access to the requested resources to the application. Additionally, at any time the user can visit the service and revoke the token which will break the integration between the client and the service for a user.
PoC implementation:
Finally, it's time to show how quickly we can implement a web application using both authorization server and resource server based on OAuth 2.0. In this example, I will use Apache Oltu [3] which is OAuth 2.0 protocol implementation for Java. I will use JAX-RS specification, however it's also possible to make it even simpler with Java servlets. I'd like to highlight that the application has been based on the example that comes from [5] article. So let's start.
We will implement 4 following endpoints as REST resources:
- End User Authorization - Authorization of the app without any authentication (to make it simpler) In response, the authorization code is redirect back to application.
- Redirect - Retrieve the response from authorization server and return JSON with headers and parameters (to obtain authorization code).
- Token - Intended to access token, check user credentials, check grant type and assigns new token.
- Resource - Validate token and return resources for a given user.
At first, we need to add proper dependency to use Oltu library. For example in Maven, it would be as follows:
<dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.client</artifactId> <version>${version.oltu}</version> <scope>test</scope> </dependency>
Below you can find simplified implementation of the End User Authorization endpoint.
@GET public Response authorize(@Context HttpServletRequest request) throws URISyntaxException, OAuthSystemException { try { OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator()); //build response according to response_type String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE); OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND); //Authz code if (responseType.equals(ResponseType.CODE.toString())) { final String authorizationCode = oauthIssuerImpl.authorizationCode(); database.addAuthCode(authorizationCode); builder.setCode(authorizationCode); } //Build response and redirect to given in request URI String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI); final OAuthResponse response = builder.location(redirectURI).buildQueryMessage(); URI url = new URI(response.getLocationUri()); //Send reponse to given URI return Response.status(response.getResponseStatus()).location(url).build(); }//... handle errors and build response
This part relates to the steps (1) & (2) of the authorization code flow from Figure 1, however we do not authenticate the user to make the whole process simpler. As we can see, OAuthIssuerImpl implementation is used to generate authorization code. We end redirecting back to the URL with authorization code (HTTP Redirect Binding) in the response .
In the below redirect endpoint we basically create an instance of a JSONObject class, retrieving all the query parameters from the GET request and adding them to our JSON object. This part includes (3) step from authorization code flow.
@GET public String redirect() { JSONObject object = new JSONObject(); JSONObject headers = new JSONObject(); JSONObject queryparams = new JSONObject(); String json = "Error!";(...) for (Map.Entry<String, List<String>> entry : httpHeaders.getRequestHeaders().entrySet()) { headers.put(entry.getKey(), entry.getValue().get(0)); } object.put("headers", headers); for (Map.Entry<String, List<String>> entry : uriInfo.getQueryParameters().entrySet()) { queryparams.put(entry.getKey(), entry.getValue().get(0)); } object.put("queryParameters", queryparams); json = object.toString(4);(...) return json; }
After that, the user can access the token. In order to do it we create an endpoint that generates a token for him. This is (4) and (5) step from the Figure 1. Below you can find simplified version of the authorization server.
@POST @Consumes("application/x-www-form-urlencoded") @Produces("application/json") public Response authorize(@Context HttpServletRequest request) throws OAuthSystemException {OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request); OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator()); // fake check of client id and client secret (...) // check for grant types // code - tells the service that we have an authorization code and would like to have tokenif (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) { if (!checkAuthCode(oauthRequest.getParam(OAuth.OAUTH_CODE))) { return buildBadAuthCodeResponse(); } }// create and add new token to database for a given user final String accessToken = oauthIssuerImpl.accessToken(); database.addToken(accessToken); // send response with token OAuthResponse response = OAuthASResponse .tokenResponse(HttpServletResponse.SC_OK) .setAccessToken(accessToken) .setExpiresIn("3600") .buildJSONMessage(); return Response.status(response.getResponseStatus()).entity(response.getBody()).build(); }
Above code neither verify client id nor client authorization code. However in real process of resource authorization in order to create new token for a user we have to identify client and check his authorization code. To access protected resources we can implement the following code:
@GET @Produces("text/html") public Response get(@Context HttpServletRequest request) throws OAuthSystemException { try { // Make the OAuth Request out of this request OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.HEADER); // Get the access token String accessToken = oauthRequest.getAccessToken(); // Validate the access token if (!database.isValidToken(accessToken)) { // Return the OAuth error message } final String resources = ACCESS_TO_RESOURCES_COMMUNICATE + "\nYour accessToken: " + accessToken; // Return the resource when token is in db return Response.status(Response.Status.OK).entity(resources).build();// ... handle errors
This code is also part of authorization flow. The user will get access to protected resources only if he sends the valid access token. In this case, resource is just a plain text with user's access token.
In order to test my web application I've used Arquillian framework [6]. I have written simple integration tests for the classes that are executed inside servlet container in GlassFish4 application server.
All the code from the project can be checked out from the following repository on github: https://github.com/tr0k/OAuth2LibPoc
SAML2 vs OAuth 2.0
Undoubtedly, the big advantage of using OAuth 2.0 is that it is very simple. The communication from the authorization server back to the client and resources server is done over HTTP Redirects with the token provided as query parameter. SAML2 mostly uses HTTP POST Binding to send back token to the service provider (because of digital signatures) and this is a problem for mobile applications because they don't have access to the HTTP POST body. This means that it's impossible to read directly the SAML token without workarounds.
SAML2 has one significant feature that OAuth 2.0 lacks of: the SAML2 token contains the user identity information. In OAuth 2.0 the resource server needs to make another round trip to validate the token with the Authorization Server. On the other hand, with OAuth 2.0, we can easily invalidate an access token and disable it from further access to the resource server.
Significant flaw of using OAuth 2.0 standard is that specification doesn't prescribe how the communication between resource server and authorization server works in many situations, for instance with token validation.
To sum up, both approaches have nice features and work fine for SSO, however, OAuth 2.0 provides a simpler and more standardized solution for native applications. Comparing to SAML2, OAuth 2.0 doesn't assume that the client is a web-browser. It's straightforward solution that works out of the box for mobile applications without any workarounds. Nevertheless, OAuth 2.0 is still very young and thus it has still lack in specification. [7]
Summary:
In this post I've presented you how does OAuth 2.0 protocol works and explained you how to implement basic client-server web application while showing step-by-step all the process of resource authorization. The final result is a simple internet application, consisting of four endpoints representing client, redirect, authorization server and resource server. You can test the full code that is accessible from github repository. At the end I've compared two possible solutions, SAML2 and OAuth 2.0.
Currently, OAuth 2.0 standard is used at CERN, but only on selected applications. OAuth 2.0 can be complementary solution for SAML2 protocol. Both solutions allow to achieve SSO for CERN web applications.
Next step in my research would be to verify how the application presented in this entry works on WebLogic server and how properly setup environment to protect and access resources with OAuth 2.0 in CERN Single-Sign On (SSO).
References:
- http://tutorials.jenkov.com/oauth2/index.html
- https://www.digitalocean.com/community/tutorials/an-introduction-to-oau…
- https://oltu.apache.org/
- http://oauth.net/2/
- http://blogs.steeplesoft.com/posts/2013/a-simple-oauth2-client-and-serv…
- http://arquillian.org/
- https://www.mutuallyhuman.com/blog/2013/05/09/choosing-an-sso-strategy-…
- https://developer.github.com/v3/oauth/