PingGateway 2024.11

UMA support

PingGateway includes support for User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization specifications.

About PingGateway as an UMA resource server

The following figure shows an UMA environment, with PingGateway protecting a resource, and AM acting as an Authorization Server. Learn more in the AM documentation on User-Managed Access (UMA) 2.0.

In the UMA architecture, PingGateway 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

You can find information about CORS support in the AM documentation on configuring CORS support. This procedure describes how to modify the AM configuration to allow cross-site access.

Limitations of PingGateway as an UMA resource server

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

  • When you deploy the administrative endpoint on another port using "adminConnector" settings in admin.json, expose the port for use in the UMA deployment.

  • PingGateway depends on the resource owner for the PAT.

    When a PAT expires, no refresh token is available to PingGateway. 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.

    PingGateway has no mechanism for persisting the data across restarts. When PingGateway 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 PingGateway error conditions.

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

  • When matching protected resource paths with share patterns, PingGateway 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, PingGateway 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 aren’t 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=*, don’t allow Content-Type headers. Allowing the use of both types of headers exposes AM to cross-site request forgery (CSRF) attacks.

This procedure uses the Resource Owner Password Credentials grant type. As suggested in The OAuth 2.0 Authorization Framework, use other grant types whenever possible.

Before you begin, prepare AM, PingGateway, and the sample application. Learn more in the example installation for this guide.

You can find more information about using different settings for the sample application in Edit the example to match custom settings.

  1. Set up AM:

    1. Find the name of the AM session cookie at the /json/serverinfo/* endpoint. This procedure assumes that you are using the default AM session cookie, iPlanetDirectoryPro.

    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. (Optional) On the Core tab, switch to using a client secret associated with a secret label by setting a Secret Label Identifier and mapping the label to a secret.

        To learn more, read Create a client profile and Map and rotate secrets in the AM documentation.

      4. 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. 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

      • Email Address : alice@example.com

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

      • ID : bob

      • Password : UMAexamp1e

      • Email Address : bob@example.com

    8. Enable the CORS filter on AM:

      1. In a terminal window, retrieve an SSO 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://am.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 /global-config/services/CorsService REST endpoint:

        $ curl  \
          --request PUT \
          --header "Content-Type: application/json" \
          --header "iPlanetDirectoryPro: $mytoken" http://am.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://ig.example.com:8080",
               "http://am.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
          }'

        A CORS configuration is diplayed.

        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://am.example.com:8088/openam/json/global-config/services/CorsService/CorsService/configuration/CorsService
  2. Set up PingGateway as an UMA resource server:

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

      • Linux

      • Windows

      $HOME/.openig/config/routes/00-static-resources.json
      %appdata%\OpenIG\config\routes\00-static-resources.json
      {
        "name" : "00-static-resources",
        "baseURI" : "http://app.example.com:8081",
        "condition": "${find(request.uri.path,'^/css') or matchesWithRegex(request.uri.path, '^/.*\\\\.ico$') or matchesWithRegex(request.uri.path, '^/.*\\\\.gif$')}",
        "handler": "ReverseProxyHandler"
      }
    2. Add the following settings to admin.json configuration and restart PingGateway:

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

      Notice the following feature:

      • The default "apiProtectionFilter" uses a CorsFilter to allow requests from the origin http://app.example.com:8081.

    3. Add the following route to PingGateway:

      • Linux

      • Windows

      $HOME/.openig/config/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://am.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.

  3. Test the setup:

    1. In your browser’s privacy or incognito mode, 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 PingGateway.

        • 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://am.example.com:8088/openam/oauth2/access_token

          If you fail to get an access token, check that AM is configured as described in this procedure. If you continue to have problems, make sure that your PingGateway 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 Use the sample application to temporary folder:

    $ mkdir /tmp/uma
    $ cd /tmp/uma
    $ jar -xvf /path/to/PingGateway-sample-application-2024.11.0-jar-with-dependencies.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/PingGateway-sample-application-2024.11.0-jar-with-dependencies.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, refer to API descriptors.