Saturday, June 2, 2012

Secure Jersey with OAuth2, Open Authentication Framework

Overview

Our platform must be secure. After some initial investigation, we decided to go with OAuth2, the next generation of OAuth protocol. The OAuth protocol enables websites or applications (Consumers) to access Protected Resources from a web service (Service Provider) via an API, without requiring Users to disclose their Service Provider credentials to the Consumers. More generally, OAuth creates a freely-implementable and generic methodology for API authentication.


Securing Jersey with OAuth2

We looked at implementing OAuth2 support as Tomcat security realm, servlet filter, or Tomcat valve. In the end, we decided to go with Tomcat valve for the following reasons,
  • Since we are implementing web services and not web applications, there is no standard way of caching Tomcat session between requests. This makes session based security realm irrelevant.
  • Servlet filer can do almost exactly the same thing as a Tomcat Valve, except for a servlet filter is deployed at web application level. This means we have to deploy this servlet for every web application we deploy. Not as convenient as a Tomcat valve
  • On the other hand, Tomcat valve is Tomcat specific. If we want a portable solution, we will have to stick with Servlet filter. Luckily we are sticking with Tomcat for now and there is very little effort if we need to switch to a Servlet filter based implementation

OAuth Tomcat Valve Class

Here is the example Valve class,
 package jersey.oauth;  
 import java.io.IOException;  
 import javax.servlet.ServletException;  
 import javax.servlet.http.HttpServletResponse;  
 import org.apache.catalina.connector.Request;  
 import org.apache.catalina.connector.Response;  
 import org.apache.catalina.valves.ValveBase;  
 import com.sun.security.auth.UserPrincipal;  
 public class OAuthValve extends ValveBase  
 {  
     protected String identityServerURL;  
     public String getIdentityServerURL() {  
         return identityServerURL;  
     }  
     public void setIdentityServerURL(String identityServerURL) {  
         this.identityServerURL = identityServerURL;  
     }  
     @Override  
     public void invoke(Request request, Response response) throws IOException,  
             ServletException {  
         if (request.getMethod().equals("OPTIONS"))  
             getNext().invoke(request, response);  
         else  
         {  
 //            response.sendError(HttpServletResponse.SC_FORBIDDEN);  
             String authentication = request.getHeader("authentication");  
             if (authentication == null)  
             {  
                 authentication = request.getParameter("access_token");  
             }  
             else  
             {  
                     String[] tokens = authentication.split(" ");  
                 if (tokens.length >= 2 && tokens[0].equalsIgnoreCase("Bearer"))  
                 {  
                     authentication = tokens[1];  
                 }  
                 else  
                 {  
                     authentication = null;  
                 }  
             }  
             if (authentication == null)  
                 response.sendError(HttpServletResponse.SC_UNAUTHORIZED);  
             else  
             {  
                 // TODO call identity server, passing on the access token
                 // Set return principal 
                 request.setUserPrincipal(new UserPrincipal("name"));  
                 getNext().invoke(request, response);  
             }  
         }  
     }  
 }  


Here is the sample Host block of Tomcat server.xml file,
 <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false">  
     <!-- SingleSignOn valve, share authentication between web applications  
        Documentation at: /docs/config/valve.html -->  
     <!--  
     <Valve className="org.apache.catalina.authenticator.SingleSignOn" />  
     -->  
     <!-- Access log processes all example.  
        Documentation at: /docs/config/valve.html -->  
     <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="common" prefix="localhost_access_log." resolveHosts="false" suffix=".txt"/>  
         <Valve className="jersey.oauth.OAuthValve" identityServerURL="localhost"/>  
    <Context docBase="JerseyCors" path="/jersey" reloadable="true" source="org.eclipse.jst.jee.server:JerseyCors"/></Host>  
Deploy, start Tomcat and test.

1 comment:

  1. Hi Lei Gu, Thanks for the post. what is JerseyCors here? Can you please explain how to run this piece of code in tomcat?

    ReplyDelete