Support UMA Resource Servers

IG includes support for User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization specifications. This chapter describes UMA 2.0, and applies to IG 5.5 and later versions, used with AM 5.5 and later versions. For earlier versions, see their documentation.

About IG As an UMA Resource Server

The following figure shows an UMA environment, with IG protecting a resource, and AM acting as an authorization server. For information about UMA, see AM’s User-Managed Access (UMA) 2.0 Guide.

In the UMA architecture, IG protects the resource server.

The following figure shows the data flow when the resource owner registers a resource with AM, and sets up a share using a Protection API Token (PAT):

uma-protecting-resource-flow

The following figure shows the data flow when the client accesses the resource, using a Requesting Party Token (RPT):

uma-grant-flow

For information about CORS support, see Configuring CORS Support in AM’s Security Guide. This procedure describes how to modify the AM configuration to allow cross-site access.

Limitations Of IG As an UMA Resource Server

When using IG as an UMA resource server, note the following points:

  • IG depends on the resource owner for the PAT.

    When a PAT expires, no refresh token is available to IG. The resource owner must repeat the entire share process with a new PAT in order to authorize access to protected resources. The resource owner should delete the old resource and create a new one.

  • Data about PATs and shared resources is held in memory.

    IG has no mechanism for persisting the data across restarts. When IG stops and starts again, the resource owner must repeat the entire share process.

  • UMA client applications for sharing and accessing protected resources must deal with UMA error conditions and IG error conditions.

  • By default, the REST API to manage share objects exposed by IG is protected only by CORS.

  • When matching protected resource paths with share patterns, IG takes the longest match.

    For example, if resource owner Alice shares /photos/.* with Bob, and /photos/vacation.png with Charlie, and then Bob attempts to access /photos/vacation.png, IG applies the sharing permissions for Charlie, not Bob. As a result, Bob can be denied access.

Set Up the UMA Example

This section describes tasks to set up AM as an authorization server:

  • Enabling cross-origin resource sharing (CORS) support in AM

  • Configuring AM as an authorization server

  • Registering UMA client profiles with AM

  • Setting up a resource owner (Alice) and requesting party (Bob)

The settings in this section are suggestions for this tutorial. They are not intended as instructions for setting up AM CORS support on a server in production.

If you need to accept all origins, by allowing the use of Access-Control-Allowed-Origin=*, do not allow Content-Type headers. Allowing the use of both types of headers exposes AM to cross-site request forgery (CSRF) attacks.

Before you start, prepare AM, IG, and the sample application as described in Example Installation for This Guide.

If you use different settings for the sample application, see Edit the Example to Match Custom Settings.

  1. Set up AM:

    1. Find the name of the AM session cookie:

      $ curl http://openam.example.com:8088/openam/json/serverinfo/* | jq .cookieName

      The rest of the steps in this procedure assume that you are using the default AM session cookie, iPlanetDirectoryPro. If not, substitute the value in the procedure.

    2. Create an OAuth 2.0 Authorization Server:

      1. Select Services > Add a Service > OAuth2 Provider.

      2. Add a service with the default values.

    3. Configure an UMA Authorization Server:

      1. Select Services > Add a Service > UMA Provider.

      2. Add a service with the default values.

    4. Add an OAuth 2.0 client for UMA protection:

      1. Select Applications > OAuth 2.0 > Clients.

      2. Add a client with these values:

        • Client ID : OpenIG

        • Client secret : password

        • Scope : uma_protection

      3. (From AM 6.5) On the Advanced tab, select the following option:

        • Grant Types : Resource Owner Password Credentials

    5. Add an OAuth 2.0 client for accessing protected resources:

      1. Select Applications > OAuth 2.0 > Clients.

      2. Add a client with these values:

        • Client ID : UmaClient

        • Client secret : password

        • Scope : openid

      3. (From AM 6.5) On the Advanced tab, select the following option:

        • Grant Types : Resource Owner Password Credentials and UMA

    6. Select Identities, and add an identity for a resource owner, with the following values:

      • ID : alice

      • Password : UMAexamp1e

    7. Select Identities, and add an identity for a requesting party, with the following values:

      • ID : bob

      • Password : UMAexamp1e

    8. Enable the CORS filter on AM:

      1. In a terminal window, retrieve an access_token from AM:

        $ mytoken=$(curl --request POST \
        --header "Accept-API-Version: resource=2.1" \
        --header "X-OpenAM-Username: amadmin" \
        --header "X-OpenAM-Password: password" \
        --header "Content-Type: application/json" \
        --data "{}"  \
        http://openam.example.com:8088/openam/json/authenticate | jq -r ".tokenId")
      2. Using the token retrieved in the previous step, enable the CORS filter on AM, by using the use the /global-config/services/CorsService REST endpoint:

        $ curl  \
          --request PUT \
          --header "Content-Type: application/json" \
          --header "iplanetDirectoryPro: $mytoken" http://openam.example.com:8088/openam/json/global-config/services/CorsService/configuration/CorsService \
          --data '{
             "acceptedMethods": [
               "POST",
               "GET",
               "PUT",
               "DELETE",
               "PATCH",
               "OPTIONS"
             ],
             "acceptedOrigins": [
               "http://app.example.com:8081",
               "http://openig.example.com:8080",
               "http://openam.example.com:8088/openam"
             ],
             "allowCredentials": true,
             "acceptedHeaders": [
               "Authorization",
               "Content-Type",
               "iPlanetDirectoryPro",
               "X-OpenAM-Username",
               "X-OpenAM-Password",
               "Accept",
               "Accept-Encoding",
               "Connection",
               "Content-Length",
               "Host",
               "Origin",
               "User-Agent",
               "Accept-Language",
               "Referer",
               "Dnt",
               "Accept-Api-Version",
               "If-None-Match",
               "Cookie",
               "X-Requested-With",
               "Cache-Control",
               "X-Password",
               "X-Username",
               "X-NoSession"
             ],
             "exposedHeaders": [
               "Access-Control-Allow-Origin",
               "Access-Control-Allow-Credentials",
               "Set-Cookie",
               "WWW-Authenticate"
             ],
             "maxAge": 600,
             "enabled": true,
             "allowCredentials": true
          }'
        
        {
         "_id": "CorsService",
         "_rev": "300529028",
         "maxAge": 600,
         "exposedHeaders": ["Access-Control-Allow-Origin", "Access-Control-Allow-Credentials", "WWW-Authenticate", "Set-Cookie"],
         "acceptedOrigins": ["http://openig.example.com:8080", "http://app.example.com:8081", "http://openam.example.com:8088/openam"],
         "acceptedMethods": ["DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"],
         "acceptedHeaders": ["Cookie", "Origin", "X-Username", "Accept", "X-Requested-With", "Connection", "User-Agent", "Referer", "Host", "Dnt", "X-NoSession", "Accept-Encoding", "iPlanetDirectoryPro", "If-None-Match", "Authorization", "Cache-Control", "X-OpenAM-Username", "X-Password", "Accept-Language", "Content-Length", "X-OpenAM-Password", "Accept-Api-Version", "Content-Type"],
          "enabled": true,
          "allowCredentials": true,
          "_type": {
            "_id": "CorsService",
            "name": "CORS Service",
            "collection": true
          }
        }

        To delete the CORS configuration and create another, first run the following command:

        $ curl \
         --request DELETE \
         --header "X-Requested-With: XMLHttpRequest" \
         --header "iplanetDirectoryPro: $mytoken" \
         http://openam.example.com:8088/openam/json/global-config/services/CorsService/CorsService/configuration/CorsService
  2. Set up IG as an UMA resource server:

    1. Add the following route to IG, to serve .css and other static resources for the sample application:

      • Linux

      • Windows

      $HOME/.openig/routes/static-resources.json
      appdata\OpenIG\config\routes\static-resources.json
      {
        "name" : "sampleapp-resources",
        "baseURI" : "http://app.example.com:8081",
        "condition": "${matches(request.uri.path,'^/css')}",
        "handler": "ReverseProxyHandler"
      }
    2. Add the following route to IG:

      • Linux

      • Windows

      $HOME/.openig/config/admin.json
      appdata\OpenIG\config\admin.json
      • Standalone mode

      • Web container mode

      {
        "prefix": "openig",
        "connectors": [
          { "port" : 8080 }
        ],
        "heap": [
          {
            "name": "ClientHandler",
            "type": "ClientHandler"
          },
          {
            "name": "ApiProtectionFilter",
            "type": "CorsFilter",
            "config": {
              "policies": [
                {
                  "acceptedOrigins": [ "http://app.example.com:8081" ],
                  "acceptedMethods": [ "GET", "POST", "DELETE" ],
                  "acceptedHeaders": [ "Content-Type" ]
                }
              ]
            }
          }
        ]
      }
      {
        "prefix": "openig",
        "heap": [
          {
            "name": "ClientHandler",
            "type": "ClientHandler"
          },
          {
            "name": "ApiProtectionFilter",
            "type": "CorsFilter",
            "config": {
              "policies": [
                {
                  "acceptedOrigins": [ "http://app.example.com:8081" ],
                  "acceptedMethods": [ "GET", "POST", "DELETE" ],
                  "acceptedHeaders": [ "Content-Type" ]
                }
              ]
            }
          }
        ]
      }

      Notice the following feature of the route:

      • The default ApiProtectionFilter is overridden by the CorsFilter, which allows requests from the origin http://app.example.com:8081. For information, see AdminHttpApplication (admin.json).

    3. Add the following route to IG:

      • Linux

      • Windows

      $HOME/.openig/routes/00-uma.json
      appdata\OpenIG\config\routes\00-uma.json
      {
        "name": "00-uma",
        "condition": "${request.uri.host == 'app.example.com'}",
        "heap": [
          {
            "name": "UmaService",
            "type": "UmaService",
            "config": {
              "protectionApiHandler": "ClientHandler",
              "wellKnownEndpoint": "http://openam.example.com:8088/openam/uma/.well-known/uma2-configuration",
              "resources": [
                {
                  "comment": "Protects all resources matching the following pattern.",
                  "pattern": ".*",
                  "actions": [
                    {
                      "scopes": [
                        "#read"
                      ],
                      "condition": "${request.method == 'GET'}"
                    },
                    {
                      "scopes": [
                        "#create"
                      ],
                      "condition": "${request.method == 'POST'}"
                    }
                  ]
                }
              ]
            }
          }
        ],
        "handler": {
          "type": "Chain",
          "config": {
            "filters": [
              {
                "type": "CorsFilter",
                "config": {
                  "policies": [
                    {
                      "acceptedOrigins": [ "http://app.example.com:8081" ],
                      "acceptedMethods": [ "GET" ],
                      "acceptedHeaders": [ "Authorization" ],
                      "exposedHeaders": [ "WWW-Authenticate" ],
                      "allowCredentials": true
                    }
                  ]
                }
              },
              {
                "type": "UmaFilter",
                "config": {
                  "protectionApiHandler": "ClientHandler",
                  "umaService": "UmaService"
                }
              }
            ],
            "handler": "ReverseProxyHandler"
          }
        }
      }

      Notice the following features of the route:

      • The route matches requests from app.example.com.

      • The UmaService describes the resources that a resource owner can share, using AM as the authorization server. It provides a REST API to manage sharing of resource sets.

      • The CorsFilter defines the policy for cross-origin requests, listing the methods and headers that the request can use, the headers that are exposed to the frontend JavaScript code, and whether the request can use credentials.

      • The UmaFilter manages requesting party access to protected resources, using the UmaService. Protected resources are on the sample application, which responds to requests on port 8081.

    4. Restart IG to reload the configuration.

  3. Test the setup:

    1. If necessary, log out of AM, and then go to http://app.example.com:8081/uma/.

    2. Share resources:

      1. Select Alice shares resources.

      2. On Alice’s page, select Share with Bob. The following items are displayed:

        • The PAT that Alice receives from AM.

        • The metadata for the resource set that Alice registers through IG.

        • The result of Alice authenticating with AM in order to create a policy.

        • The successful result when Alice configures the authorization policy attached to the shared resource.

          If the step fails, run the following command to get an access token for Alice:

          $ curl -X POST \
          -H "Cache-Control: no-cache" \
          -H "Content-Type: application/x-www-form-urlencoded" \
          -d 'grant_type=password&scope=uma_protection&username=alice&password=UMAexamp1e&client_id=OpenIG&client_secret=password' \
          http://openam.example.com:8088/openam/oauth2/access_token
          
          {"access_token":"AQI...QAA*","scope":"uma_protection","token_type":"Bearer","expires_in":3599}

          If you fail to get an access token, check that AM is configured as described in Set Up the UMA Example. If you continue to have problems, make sure that your IG configuration matches that shown when you are running the test on http://app.example.com:8081/uma/.

    3. Access resources:

      1. Go back to the first page, and select Bob accesses resources.

      2. On Bob’s page, select Get Alice’s resources. The following items are displayed:

        • The WWW-Authenticate Header.

        • The OpenID Connect Token that Bob gets to obtain the RPT.

        • The RPT that Bob gets in order to request the resource again.

        • The final response containing the body of the resource.

Edit the Example to Match Custom Settings

If you use a configuration that is different to that described in this chapter, consider the following tasks to adjust the sample to your configuration:

  1. Unpack the UMA files from the sample application described in Downloading and Starting the Sample Application to temporary folder:

    $ mkdir /tmp/uma
    $ cd /tmp/uma
    $ jar -xvf /path/to/IG-sample-application-7.1.0.jar webroot-uma
    
    created: webroot-uma/
    inflated: webroot-uma/bob.html
    inflated: webroot-uma/common.js
    inflated: webroot-uma/alice.html
    inflated: webroot-uma/index.html
    inflated: webroot-uma/style.css
  2. Edit the configuration in common.js, alice.html, and bob.html to match your settings.

  3. Repack the UMA sample client files and then restart the sample application:

    $ jar -uvf /path/to/IG-sample-application-7.1.0.jar webroot-uma
    
    adding: webroot-uma/(in = 0) (out= 0)(stored 0%)
    adding: webroot-uma/bob.html(in = 26458) (out= 17273)(deflated 34%)
    adding: webroot-uma/common.js(in = 3652) (out= 1071)(deflated 70%)
    adding: webroot-uma/alice.html(in = 27775) (out= 17512)(deflated 36%)
    adding: webroot-uma/index.html(in = 22046) (out= 16060)(deflated 27%)
    adding: webroot-uma/style.css(in = 811) (out= 416)(deflated 48%)
    updated module-info: module-info.class
  4. If necessary, adjust the CORS settings for AM.

Understand the UMA API With an API Descriptor

The UMA share endpoint serves API descriptors at runtime. When you retrieve an API descriptor for the endpoint, a JSON that describes the API for the endpoint is returned.

You can use the API descriptor with a tool such as Swagger UI to generate a web page that helps you to view and test the endpoint. For information, see Understanding IG APIs With API Descriptors.