OpenAM with CORS: Is that a salad, dessert or main course?

While the title suggests this might be something to enjoy with a spoon or a fork,  I assure you this is not the case. CORS stands for “Cross-origin resource sharing”.

To put CORS it in context let me take you back in history and remind you that the world wide web took form more than  25 years ago and it started with static pages in HTML. Several years after that Brendan Eich designed  a scripting language for the web called Mocha and this soon was renamed into what we know now as JavaScript. It was a brilliant move, it gave dynamism to the web.

While dynamic pages is good, we must keep an eye on security when using scripted languages like JavaScript. So, there are some restrictions and one of them is the “Same-origin policy”, which restricts how a document or script loaded from one origin can interact with resources from another origin. This policy is used to prevent some of the Cross-Site Request Forgery attacks (CSRF).   This is enforced by the web browser and this is what prevents AJAX (XMLHttpRequest) requests to access resources outside the parent page with POST, PUT, DELETE, custom headers and other type of verbs.

But sometimes we want to be able to access resources from another origin, well, that is what CORS do, and it is the mechanism used to allow a resource from one origin to request resources from another domain outside the domain from which the resource originated.

If you want to know more about CORS, there are many articles about it in the web.

The question is now, if I want the REST APIs from my instance of OpenAM to be accessed by JavaScript pages running outside of my deployment, how do I do this?  The answer is, we need to configure  our  OpenAM deployment to support CORS.

Once you have successfully configured CORS in your deployment, the REST APIs will be available to the origins configured. Keep in mind that you should do this carefully, and allow access only to the minimum necessary resources for your applications.

To proceed, first, read the documentation: http://openam.forgerock.org/doc/webhelp/install-guide/enable-cors-support.html

Here a complete web.xml example

The relevant portion for CORS is down here.

For this example the OpenAM is running as http://openam.example.com:8080 and the server running an application using JavaScript is running in http://myoriginserver.example.com:80

Notice some things:

  1. In some cases the OpenAM will require to receive the SSO Token Id as a header, so we need to allow the session cookie name as a header, and the name can change depending on your deployment
  2. The origins are the URLs of the sites requesting to access the resources (REST APIs) in the OpenAM, notice the format: protocol://name:port
  3. When using credentials the headers containing the credentials are X-OpneAM-Username and X-OpenAM-Password
  4. The host name of the OpenAM sharing the REST API resources is configured without the protocol, but including the port
  5. In this example we have included several methods: POST,GET,PUT,DELETE,PATCH,OPTIONS but you should only allow the ones your applications are going to use.
  6. This example might need to be extended to allow more REST APIs, but it is a good way to start.

Give it a try and let us know how it worked for you.

<!-- To configure CORS Support, please see the documentation and use the following lines as a template.  -->
<filter>
   <filter-name>CORSFilter</filter-name>
   <filter-class>org.forgerock.openam.cors.CORSFilter</filter-class>
   <init-param>
      <description>
         Accepted Methods (Required):
         A comma separated list of HTTP methods for which to accept CORS requests.
      </description>
      <param-name>methods</param-name>
      <param-value>POST,GET,PUT,DELETE,PATCH,OPTIONS</param-value>
   </init-param>
   <init-param>
      <description>
         Accepted Origins (Required):
         A comma separated list of origins from which to accept CORS requests.
      </description>
      <param-name>origins</param-name>
      <param-value>http://myoriginserver.example.com,http://myoriginserver.example.com:80,http://openam.example.com:8080</param-value>
   </init-param>
   <init-param>
      <description>
         Allow Credentials (Optional):
         Whether to include the Vary (Origin) and Access-Control-Allow-Credentials headers in the response.
         Default: false
      </description>
      <param-name>allowCredentials</param-name>
      <param-value>false</param-value>
   </init-param>
   <init-param>
      <description>
         Allowed Headers (Optional):
         A comma separated list of HTTP headers which can be included in the requests.
      </description>
      <param-name>headers</param-name>
      <param-value>X-OpenAM-Username,X-OpenAM-Password,X-Requested-With,Content-Type,Accept,iPlanetDirectoryPro</param-value>
   </init-param>
   <init-param>
      <description>
         Expected Hostname (Optional):
         The name of the host expected in the request Host header.
      </description>
      <param-name>expectedHostname</param-name>
      <param-value>openam.example.com:8080</param-value>
   </init-param>
   <init-param>
      <description>
         Exposed Headers (Optional):
         The comma separated list of headers which the user-agent can expose to its CORS client.
      </description>
      <param-name>exposeHeaders</param-name>
      <param-value>exposeHeaderOne,exposeHeaderTwo</param-value>
   </init-param>
   <init-param>
      <description>
         Maximum Cache Age (Optional):
         The maximum time that the CORS client can cache the pre-flight response, in seconds.
         Default: 600
      </description>
      <param-name>maxAge</param-name>
      <param-value>600</param-value>
   </init-param>
</filter>
...
<filter-mapping>
   <filter-name>CORSFilter</filter-name>
   <url-pattern>/json/*</url-pattern>
</filter-mapping>

 

 

UPDATE:
CORS is also configurable in some containers or in other ways, for example some recent versions of Tomcat support the configuration of a CORS Filter. Here an example of the filter added into a Tomcat 8.0.32 that is running the OpenAM. Just a word of caution, this is example applies to all the URLs, you might want to limit it to certain URL patterns:

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>*</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.methods</param-name>
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.headers</param-name>
    <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,X-OpenAM-Username,X-OpenAM-Password,iPlanetDirectoryPro</param-value>
  </init-param>
  <init-param>
    <param-name>cors.exposed.headers</param-name>
    <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials,Set-Cookie</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>cors.preflight.maxage</param-name>
    <param-value>10</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

2 Comments

Comments are closed.

  1. Scott Heger 4 years ago

    Great stuff Victor. Thanks for posting!

  2. kamalsofteng 4 years ago

    Hi Victor, I was wondering why FR has decided to implement CORS even through most containers have already done so? I tested CORS with OAM11/TC7 and it works as expected but I see the support is added to OAM after 11. My question is: Is there a fundamental difference or whatever the container provides suffices?

    Thanks.

©2019 ForgeRock - we provide an identity and access platform to secure every online relationship for the enterprise market, educational sector and even entire countries. Click to view our privacy policy and terms of use.

Log in with your credentials

Forgot your details?