Protecting and Accessing Resources with OAuth in Oracle Access Manager

Version 9

    In Oracle Access Manager (OAM) you can use OAuth Service to enable the client to access resources protected with OAuth 2.0. In this article, I'll demonstrate how to configure OAM to protect a service hosted on WebLogic Server (WLS) as well as a Web Application (also hosted on WebLogic) consuming it, using 3-legged OAuth flow.

     

    Scenario

    The solution was applied on OAM 11.1.2.2 and WLS 12.1.3.

     

    OAuth Service is part of Oracle Mobile and Social Access Service (OMSAS) and allows protection or accessing of resources in OAM using OAuth 2.0. You can implement some scenarios with OAuth Service. For more information, see OAM Documentation (Using the OAuth Service API - 11g Release 2 (11.1.2.2.0)). For this article, I'm using a 3-legged OAuth flow with Authorization Code Grant Type.

     

    In this scenario, a user accesses a Web Application on WLS that requires  some user information be retrieved from a customer service hosted on another instance of WLS. Customer service needs to retrieve the user profile from OAM.

     

    Here is the complete scenario:

    scenario_oauth_oam.png

     

    1. User invokes a Web Application on WLS through a browser.

     

    2. Web Application starts OAuth flow, requesting an authorization code from OAM.

     

    3. If  the user is not authenticated, OAM redirects to the login page.

     

    4. After OAM verifies user credentials, it presents a consent page, authorizing the user to access the required services (Customer Service and user profile).

     

    5. With consent given, OAM returns the authorization code to the Web Application.

     

    6. Using the authorization code, the Web Application requests an access token to OAM.

     

    7. OAM accesses the resource (Customer Service) with the access token.

     

    8. WLS calls OAM to validate the access token.

     

    9. WLS requests user profile from OAM using the access token.

     

    10. WLS returns customer data to the Web Application.

     

    11. WLS returns the result to the Web Application.

     

     

    Implementation

     

     

    Customer Service

    The class CustomerData represents a customer repository. For test purposes, I implemented the repository in memory.

    package customer;
    
    import java.util.HashMap;
    import java.util.Map;
    
    public class CustomerData {
    
      private Map<String,String> customers;
    
      public CustomerData() {
        customers = new HashMap<String,String>();
    
        customers.put("ronaldo", "<customer><id>1</id><name>Ronaldo</name><address>My Street, 1</address><phone>000-0000</phone></customer>");
        customers.put("test", "<customer><id>2</id><name>Test</name><address>Other Street, 2</address><phone>000-0001</phone></customer>");
      }
    
      public String get(String id) {
        return (String) customers.get(id);
      }
    
    }
    

     

    The class Customer is the service to be accessed by the Web Application.

    package customer;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.charset.Charset;
    
    import javax.ws.rs.FormParam;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    import org.codehaus.jettison.json.JSONException;
    import org.codehaus.jettison.json.JSONObject;
    
    import com.sun.xml.messaging.saaj.util.Base64;
    
    @Path("/customer")
    public class Customer {
    
      private static String CLIENT_ID = "customerServer";
      private static String CLIENT_SECRET = "sJo7tb01zJ2LsBmJ";
    
      private static String ACCESS_TOKEN_URL = "https://idm.oracleads.com:14101/ms_oauth/oauth2/endpoints/oauthservice/tokens";
      private CustomerData customerData = new CustomerData();
    
      @POST
      @Produces(MediaType.APPLICATION_JSON)
      public String getInfo(@FormParam("code") String code) {
        String uid = null;
    
        // Validate access token
        boolean valid = validateToken(code);
    
        if (valid) {
        // Get User Profile to obtain user id
          String response2 = getUserProfile(code);
    
          JSONObject obj;
    
          try {
            obj = new JSONObject(response2);
            uid = obj.getString("uid");
    
          } catch (JSONException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
          }
    
        }
    
        // Return customer data
        return customerData.get(uid);
    
      }
    
      /**
      * Validate access token
      *
      * @param code
      * @return
      */
      private boolean validateToken(String code) {
        String response = null;
        boolean valid = false;
    
        //Validate OAuth Token
        String params  = "grant_type=oracle-idm%3A%2Foauth%2Fgrant-type%2Fresource-access-token%2Fjwt&"
            + "oracle_token_action=validate&"
            + "scope=Customer.info&"
            + "assertion=" + code;
    
        byte[] postData = params.getBytes( Charset.forName("UTF-8"));
        HttpURLConnection connection = null;
    
        try{
          //GET ACESS TOKEN
          URL url = new URL(ACCESS_TOKEN_URL);
    
          String client_access = CLIENT_ID + ":" + CLIENT_SECRET;
          String basicCredentials = "Basic " + new String(Base64.encode(client_access.getBytes()));
    
          connection = (HttpURLConnection)url.openConnection();
          connection.setRequestMethod("POST");
          connection.setRequestProperty("Authorization", basicCredentials);
          connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
          connection.setRequestProperty("Accept-Charset", "UTF-8");
          connection.setRequestProperty("Connection", "Keep-Alive");
          connection.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
          connection.setDoOutput(true);
    
          OutputStream wr = new DataOutputStream(connection.getOutputStream());
          wr.write(postData);
          wr.flush();
          wr.close();
    
          // get data
          BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
          String line;
          StringBuffer resp = new StringBuffer();
    
          while ((line = rd.readLine()) != null) {
            resp.append(line);
          }
    
          response = resp.toString();
    
          // validating response
          JSONObject obj;
    
          obj = new JSONObject(response);
          String successful = obj.getString("successful");
          valid = successful.equals("true");
    
          rd.close();
    
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException(e);
    
        }finally{
          if (connection != null) {
            connection.disconnect();
          }
        }
    
        return valid;
      }
    
      /**
      * Get User Profile
      *
      * @param code
      * @return
      */
      private String getUserProfile(String code) {
        String userProfile = null;
        String baseUrl = "https://idm.oracleads.com:14101/ms_oauth/resources/userprofile/me";
        HttpURLConnection connection = null;
    
        try{
          //GET ACESS TOKEN
          URL url = new URL(baseUrl);
    
          connection = (HttpURLConnection)url.openConnection();
          connection.setRequestMethod("GET");
          connection.setRequestProperty("Authorization", code);
    
          BufferedReader in = new BufferedReader(
              new InputStreamReader(connection.getInputStream()));
          String inputLine;
          StringBuffer resp = new StringBuffer();
    
          while ((inputLine = in.readLine()) != null) {
            resp.append(inputLine);
          }
    
          in.close();
          userProfile = resp.toString();
    
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException(e);
    
        } finally {
          if (connection != null) {
            connection.disconnect();
          }
        }
    
        return userProfile;
      }
    
    }
    

     

    CLIENT_ID and CLIENT_SECRET represent an OAuth client. I’ll show how to register it on OAM, below.

     

    ACCESS_TOKEN_URL is the OAM default token endpoint.

     

    The method getInfo(…) receives an access token, validates it against OAM, retrieves the user profile from OAM, and returns the corresponding customer data. To protect this access, I’m using the scope Customer.info, which will be registered later in OAM.

     

    The private method validateToken(…) validates the access token using the scope Customer.info.

     

    User Profile is a default existing service on OAM. The private method getUserProfile(…) retrieves the user profile using the access token received.

     

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
      <display-name>CustomerWeb</display-name>
      <servlet>
        <servlet-name>ServletAdaptor</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
          <param-name>com.sun.jersey.config.property.packages</param-name>
          <param-value>customer</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>ServletAdaptor</servlet-name>
        <url-pattern>/rest/*</url-pattern>
      </servlet-mapping>
    </web-app>
    

     

     

    Web Application

     

    The user will access the application using class CustomerServlet.

    package web;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import rest.CustomerRequester;
    
    @WebServlet("/CustomerServlet")
    public class CustomerServlet extends HttpServlet {
    
      private static final long serialVersionUID = 1L;
    
      /**
      * GetCustomerInfo using OAuth 3-Legged Authorization
      *
      * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
      */
      protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException  {
        try {
          // Authorization code doesn't exist
          if (request.getParameter("code") == null) {
            new CustomerRequester().authorize(response);
    
          // Already authorized
          } else {
            new CustomerRequester().getCustomerInfo(request, response);
          }
    
        } catch (Exception e) {
          e.printStackTrace();
          throw new ServletException(e);
        }
    
      }
    
    }
    

     

    This class verifies whether it has received an authorization code. If not, it will start the process of requesting one; otherwise, it’ll request the access token to call Customer Service.

     

    Class CustomerRequester has the logic to request authorization and access codes.

    package rest;
    
    import java.io.BufferedReader;
    import java.io.DataOutputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.charset.Charset;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.codehaus.jettison.json.JSONObject;
    
    import com.sun.xml.messaging.saaj.util.Base64;
    
    public class CustomerRequester {
    
      private static String CLIENT_ID = "customerClient";
      private static String CLIENT_SECRET = "zRpJbl73iE8X";
    
      private static String ACCESS_TOKEN_URL = "https://idm.oracleads.com:14101/ms_oauth/oauth2/endpoints/oauthservice/tokens";
    
      private static String AUTHORIZATION_URL = "https://idm.oracleads.com:14101/ms_oauth/oauth2/"
          + "endpoints/oauthservice/authorize"
          + "?response_type=code&client_id=" + CLIENT_ID
          + "&redirect_uri=https://test-services:7502/Rest_Web/CustomerInfo"
          + "&scope=Customer.info%20UserProfile.me&state=abc";
    
        /**
        * Request Authorization Code
        *
        * @param response
        * @throws ServletException
        * @throws java.io.IOException
        */
        public void authorize(HttpServletResponse response)
            throws ServletException, java.io.IOException {
          response.sendRedirect(AUTHORIZATION_URL);
        }
    
        /**
        * Get Customer Info with Authorization Code
        *
        * @param request
        * @param response
        * @throws ServletException
        * @throws java.io.IOException
        */
        public void getCustomerInfo(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, java.io.IOException {
    
          //Get Access Token
          String accessToken = getAccessToken(request);
    
          // Get Customer Profile
          String result = getCustomerProfile(request, accessToken);
    
          // Print Response
          response.setContentType("application/json");
          PrintWriter out = response.getWriter();
          out.print(result);
          out.flush();
          out.close();
        }
    
        /**
        * Get Access Token using Authorization Code
        *
        * @param request
        * @return Access Token
        */
        private String getAccessToken(HttpServletRequest request) {
          String accessToken = null;
    
          String params  = "redirect_uri=https://test-services:7502/Rest_Web/CustomerInfo&"
            + "grant_type=authorization_code&"
            + "code=" + request.getParameter("code");
          byte[] postData = params.getBytes( Charset.forName("UTF-8"));
    
        HttpURLConnection connection = null;
    
        try{
          //Post data to get access token
          URL url = new URL(ACCESS_TOKEN_URL);
          String client_access = CLIENT_ID + ":" + CLIENT_SECRET;
    
          String basicCredentials = "Basic " + new String(Base64.encode(client_access.getBytes()));
    
          connection = (HttpURLConnection)url.openConnection();
          connection.setRequestMethod("POST");
          connection.setRequestProperty("Authorization", basicCredentials);
          connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
          connection.setRequestProperty("Accept-Charset", "UTF-8");
          connection.setRequestProperty("Connection", "Keep-Alive");
          connection.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
          connection.setDoOutput(true);
    
          OutputStream wr = new DataOutputStream(connection.getOutputStream());
          wr.write(postData);
          wr.flush();
          wr.close();
    
          // Get Token Response
          BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charset.forName("UTF-8")));
          String line;
          StringBuffer resp = new StringBuffer();
    
          while ((line = rd.readLine()) != null) {
            resp.append(line);
          }
    
          rd.close();
    
          // Get Access Token from JSON Response
          JSONObject obj;
    
          obj = new JSONObject(resp.toString());
          accessToken = obj.getString("access_token");
    
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException(e);
    
        } finally {
          if(connection != null) {
            connection.disconnect();
          }
        }
    
        return accessToken;
      }
    
      /**
      * Get Customer Info using the Access token
      *
      * @param request
      * @param accessToken
      * @return
      * @throws ServletException
      * @throws java.io.IOException
      */
      private String getCustomerProfile(HttpServletRequest request, String accessToken)
          throws ServletException, java.io.IOException {
    
        String userProfile = null;
        String baseUrl = "https://host-services:7502/CustomerWeb/rest/customer";
        HttpURLConnection connection = null;
    
        try{
          String params  = "code=" + accessToken;
          byte[] postData = params.getBytes( Charset.forName("UTF-8"));
    
          //Call Service with Access token
          URL url = new URL(baseUrl);
    
          connection = (HttpURLConnection)url.openConnection();
          connection.setRequestMethod("POST");
          connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded;charset=UTF-8");
          connection.setRequestProperty("Accept-Charset", "UTF-8");
          connection.setRequestProperty("Connection", "Keep-Alive");
          connection.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
          connection.setDoOutput(true);
    
          OutputStream wr = new DataOutputStream(connection.getOutputStream());
          wr.write(postData);
          wr.flush();
          wr.close();
    
          BufferedReader in = new BufferedReader(
              new InputStreamReader(connection.getInputStream()));
          String inputLine;
          StringBuffer resp = new StringBuffer();
    
          while ((inputLine = in.readLine()) != null) {
            resp.append(inputLine);
          }
    
          in.close();
    
          userProfile = resp.toString();
    
        } catch (Exception e) {
          e.printStackTrace();
          throw new RuntimeException(e);
    
        } finally {
          if (connection != null) {
            connection.disconnect();
          }
        }
    
        return userProfile;
      }
    
    }
    

     

    AUTHORIZATION_URL is the URL to request the authorization code, where:

    • redirect_url is the URL to redirect the user after login and consent page
    • scope informs the scopes required for the authorization code (i.e., the required privileges). In this case, the scopes are Customer.info and UserProfile.me

     

    The method authorize(…) simply redirects the user to OAM, which will be responsible for requiring authentication and consents.

     

    The method getAccessToken(…) receives the authorization code, requests the access token and calls Customer Service.

     

    web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
      <display-name>Rest_Web</display-name>
      <servlet>
        <servlet-name>CustomerServlet</servlet-name>
        <servlet-class>web.CustomerServlet</servlet-class>
      </servlet>
      <servlet-mapping>
        <servlet-name>CustomerServlet</servlet-name>
        <url-pattern>/CustomerInfo</url-pattern>
      </servlet-mapping>
    </web-app>
    

     

     

    Configuration

     

    WebLogic Server

    Deploy the package containing Customer Service on WLS.

     

    Deploy the package containing the web application on another WLS.

     

    Oracle Access Manager

    Access the OAM console (Ex: http://<host>:<port>/oamconsole>) and be sure "Mobile and Social" is enabled:

    oam_available_services.png

     

    Access OAuth Service and enter the default domain:

    oam_console_2.png

     

    oam_oauth_domain.png

     

    oam_default_domain.png

     

    Resource Server

    Click on the Resource Server tab and create a new one for Customer Service:

    oam_create_custom_resource_server.png

     

    • Name: Customer
    • Authorization & Consent Service Plug-in: CoherenceAuthzUserConsentPlugin
    • Scope Namespace Prefix: Customer.
    • Scopes: Customer.info
    • Description: read customer info (this text it'll be showed on the Consent Page)

    oam_oauth_create_resource_server.png

     

    oam_oauth_resource_server.png

     

    OAuth Client

    In this step, create clients for Web Application and Customer Service.

     

    Click on the OAuth clients tab, and create a new one for Web Application:

    oam_create_oauth_client.png

     

    oam_oauth_create_customerclient.png

     

    Create a new OAuth Client for Customer Service:

    • Client ID: customerServer
    • Client Secret: sJo7tb01zJ2LsBmJ

    oam_oauth_create_customerserver.png

     

    WebLogic Server

    In OAM's WLS console, create a new user for testing. In my case, I created the user "ronaldo."

     

    Testing

     

    Open a browser and test the URL of Web Application (e.g., https://test-services:7502/Rest_Web/CustomerInfo).

     

    When user is not authenticated, OAM redirects to the login page:

    oam_login.png

     

    After authentication, the user is presented with consent page:

    oam_oauth_consent.png

     

    OAM generates an authorization code (e.g., eyJhbGciOiJS…V8sOhH9W0).

     

    Web Application calls OAM with an authorization code and OAM generates an access token:

    {"expires_in":3600,"token_type":"Bearer","access_token":"eyJhbG…NsJYddE"}

     

    Web Applications calls Customer Service with the access token. Customer Service validates it against OAM:

    {"successful":true}

     

    Customer Service requests the user profile from OAM using the access token:

    {"uid":"ronaldo","lastname":"ronaldo","commonname":"ronaldo","uri":"\/ms_oauth\/resources\/userprofile\/me\/ronaldo"}

     

    Customer Service returns the customer data:

    <customer><id>1</id><name>Ronaldo</name><address>My Street, 1</address><phone>000-0000</phone></customer>

     

    You can see the token generated and manage it using Token Life Cycle Management:

    oam_oauth_token.png

     

    Conclusion

    This article described a procedure to configure OAuth Service on OAM to protect and access resources with OAuth 2.0. I hope it will help in your future implementations.

     

    About the Author

    Ronaldo Fernandes is a principal consultant for Oracle Consulting in Brazil. He specializes in Oracle Fusion Middleware, SOA, and security, and has worked with Java technologies since 1996. He has more than 17 years of experience in defining architectures, problem solving, technical leadership and software development. LinkedIn