Skip to Main Content

Security Software

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

Protecting and Accessing Resources with OAuth in Oracle Access Manager

Ronaldo.Fernandes-OracleMar 5 2015 — edited Mar 19 2015

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](https://forums.oracle.com/ords/apexds/user/param) response
\* [@throws](https://forums.oracle.com/ords/apexds/user/throws) ServletException
\* [@throws](https://forums.oracle.com/ords/apexds/user/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](https://forums.oracle.com/ords/apexds/user/param) request
\* [@param](https://forums.oracle.com/ords/apexds/user/param) response
\* [@throws](https://forums.oracle.com/ords/apexds/user/throws) ServletException
\* [@throws](https://forums.oracle.com/ords/apexds/user/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](https://forums.oracle.com/ords/apexds/user/param) request
\* [@return](https://forums.oracle.com/ords/apexds/user/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

  • Client ID: customerClient
  • Client Secret: zRpJbl73iE8X
  • HTTP Redirect URIs: https://test-services:7502/Rest_Web/CustomerInfo (the same used in CustomerRequester.class)
  • Allowed Scopes: Customer.info and UserProfile.me
  • Grant Types: Authorization

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

Comments
Post Details
Added on Mar 5 2015
17 comments
23,508 views