Contents


Configure an ISAM reverse proxy as a PEP to an OpenID connect provider

Enable to accept OpenID Connect access tokens to authenticate

Comments

Note: The code under the section "Perform a test flow" (within section "Step 2. Configure a reverse proxy as a point of contact for OpenID Connect") has been updated as of May 9, 2017.

IBM Security Access Manager (ISAM) added OpenID Connect (OIDC) as a federation protocol in Version 9.0. OIDC includes the ability to configure an OpenID Provider (OP), which can issue user identities (id_tokens), as well as access tokens used for authentication in the same way that OAuth Version 2.0 does. Reverse proxies are capable of being a policy enforcement point (PEP) using OAuth Access tokens, which are achieved by using the WS-Trust interface defined here. This interface is traditionally used with an ISAM for Mobile API Protection Definition, however it is not compatible with the access tokens issued as part of an OpenID Connect flow. This article explains how to configure a reverse proxy as a policy enforcement point that is compatible with the OpenID Connect–issued access tokens.

The following steps are required:

  1. Configure an OP federation.
  2. Configure a reverse proxy as the point of contact.
  3. Configure an STS chain to make the authorization decision.
  4. Configure a reverse proxy as a policy enforcement point (PEP).

In this article, I show how to configure a reverse proxy as a policy enforcement point that is compatible with access tokens issued by an OpenID connect provider.

An OpenID Connect provider on ISAM is a federation, with each set of relying party credentials being a partner. In the procedure I describe below, one OP federation is configured and two partners are created; one to represent the relying party entity and another used by the reverse proxy/Secure Token Service when acting as a PEP.

Note: "Reverse proxy" in this article always refers to an ISAM reverse proxy.

Step 1. Configure the OP federation

Federations can be configured and managed out of the federations user interface (UI), or via REST API calls. The federations UI is available under Secure: Federation > Federations. If you are using the REST service, see the web services documentation (go to Secure: Federation > Federations > Create a new federation) for an explanation of how to invoke the Federation REST API and the properties to provide.

Configure federation via the user interface

  1. Select the Federations menu option. Screen capture showing federations menu option
    Screen capture showing federations menu option
  2. Add a new federation. Screen capture showing adding federation
    Screen capture showing adding federation
  3. Enter the issuer value, as the value you want to appear in the 'iss' claim of the id_token. Select HS256 signing. Screen capture showing configuring provider
    Screen capture showing configuring provider
  4. Select, at a minimum, Authorization Code from the available grants. Screen capture showing configuring grants
    Screen capture showing configuring grants
  5. Select the mapping rules. Screen capture showing selection of mapping rules
    Screen capture showing selection of mapping rules
  6. Review the Summary to ensure that all values are correct. Screen capture showing summary
    Screen capture showing summary

Configure a federation via REST

Use the following code to create the federation:

POST_DATA='
{
  "name": "acmeProvider",
  "protocol": "OIDC",
  "role": "op",
  "configuration": {
    "providerId": "acmeProvider",
    "issuerIdentifier": "https://acmeProvider.com",
    "signatureAlgorithm": "HS256",
    "authorizationCodeLength": 30,
    "refreshTokenLength": 50,
    "accessTokenLength": 40,
    "authorizationCodeLifetime": 30,
    "accessTokenLifetime": 7200,
    "authorizationGrantLifetime": 604800,
    "idTokenLifetime": 7200,
    "grantTypesSupported": [
      "authorization_code",
      "implicit",
      "refresh_token"
    ],
    "identityMapping": {
      "activeDelegateId": "default-map",
      "properties": {
        "identityMappingRuleReference": "5"
      }
    }
  }
}'

curl -k -vvv --user $USER:$PASSWORD  -H 'Content-Type: application/json' \
-H 'Accept: application/json' https://$HOST/iam/access/v8/federations/ -d "$POST_DATA"

This will return a location header containing the UUID for this federation. To list all federations, issue the command below.

curl -k -vvv --user $USER:$PASSWORD  -H 'Accept: application/json' \
https://$HOST/iam/access/v8/federations/$FED_UUID/partners

To show just one federation, include the UUID of the federation as a path parameter:

curl -k -vvv --user $USER:$PASSWORD  -H 'Accept: application/json' \
https://$HOST/iam/access/v8/federations/$FED_UUID/partners/$PARTNER_UUID

Note: Pending changes must be deployed before this federation can be used. Deploying them will trigger a runtime restart.

Configure the OP Partners

Each configured partner at the OP represents a set of client credentials used to obtain a grant. These client credentials are made up of:

  • Client Id
  • Client Secret(optional)
  • Redirect URI
  • Allowed scopes (at a minimum this must contain openid)

There are more options that can be configured for an OP Partner. See the IBM Knowledge Center for more information.

The partners are configured in the next substeps.

Configure the test partner

  1. Select the federation to add the partner and click the Partners button. Screen capture showing adding partner
    Screen capture showing adding partner
  2. Click Add and enter the friendly name of the partner. Enable the partner. Screen capture showing population of fields
    Screen capture showing population of fields
  3. Set the client credentials manually, or check Generate to have them created. Screen capture showing client credentials
    Screen capture showing client credentials
  4. Enter the display name. Screen capture showing introspection
    Screen capture showing introspection
  5. Enter the redirect URIs. Screen capture showing redirects
    Screen capture showing redirects
  6. Enter valid scopes that can be requested, and indicate whether consent for a given scope is preauthorized. Screen capture showing scopes
    Screen capture showing scopes
  7. Review the Summary to ensure all values are correct. Screen capture showing sumary
    Screen capture showing sumary
  8. Create the partner with the following code:
    POST_DATA='
      {
        "role": "op",
        "templateName": "",
        "configuration": {
          "responseTypes": [
            "code"
          ],
          "clientId":"rpClient",
          "clientSecret":"rpClientSecret",
          "clientName": "Reverse proxy Client",
          "tokenEndpointAuthMethod": "clientSecretBasic",
          "scope": [
            "openid",
          ],
          "allowRefreshGrant": true,
          "redirectUris": [
            "https://unused.com/"
          ],
          "allowIntrospect": true
        },
        "name": "reverseProxyClient",
        "enabled": true
      }
    '
    
    curl -k -vvv --user $USER:$PASSWORD  -H 'Content-Type: application/json' \
    -H 'Accept: application/json' https://$HOST/iam/access/v8/federations/$UUID/partners -d "$POST_DATA"

Note: Pending changes must be deployed before this partner can be used. This will trigger a runtime restart. To automatically generate a clientID or clientSecret, exclude it from the request. To specify a blank clientSecret, include the property clientSecret with the value "" (empty string). A client secret of "" cannot be used by the reverse proxy partner, nor can it be "" if the partner is doing an authorization code flow as in the example below.

Create the reverse proxy partner

To create the reverse proxy partner credentials, use the following code snippet. If using the user interface, be sure to check the Allow Introspect option.

POST_DATA='
  {
    "role": "op",
    "templateName": "",
    "configuration": {
      "responseTypes": [
        "code"
      ],
      "clientId":"rpClient",
      "clientSecret":"rpClientSecret",
      "clientName": "Reverse proxy Client",
      "tokenEndpointAuthMethod": "clientSecretBasic",
      "scope": [
        "openid",
      ],
      "allowRefreshGrant": true,
      "redirectUris": [
        "https://unused.com/"
      ],
      "allowIntrospect": true
    },
    "name": "reverseProxyClient",
    "enabled": true
  }
'

curl -k -vvv --user $USER:$PASSWORD  -H 'Content-Type: application/json' \
-H 'Accept: application/json' https://$HOST/iam/access/v8/federations/$UUID/partners -d "$POST_DATA"

It is important to note that the allowIntrospect setting is set to true. This setting allows a client to make requests to the /introspect endpoint.

Step 2. Configure a reverse proxy as a point of contact for OpenID Connect

In order to make use of the configured OpenID Connect Provider Federation, a reverse proxy needs to be configured as an appropriate point of contact. This article assumes a freshly configured reverse proxy. For more information on configuring a reverse proxy, see Configuring an instance in the ISAM version 9 user documentation.

After the instance has been configured, use the federation-automated configuration web service to configure the reverse proxy appropriately. This can be done with the following code snippet. To find out the UUID of the federation, see "Configure the OP Federation" above.

POST_DATA='
{
  "runtime": {
    "hostname": "localhost",
    "port": "443",
    "username": "easuser",
    "password": "passw0rd"
  },
  "federation_id": "'$FED_UUID'",
  "reuse_certs": true,
  "reuse_acls": true
}
'
curl -k -vvv --user $USER:$PASSWORD -H "Accept: application/json" -H "Content-Type:application/json" \
 https://$HOST/wga/reverseproxy/$REVERSE_PROXY_NAME/fed_config -d "$POST_DATA"

After this call is made, deploy all pending changes and restart the reverse proxy. For more information on invoking the fed_config web service consult the appliance's web services documentation, under the topic Secure: Web Settings > Manage > Reverse Proxy > Federation Configuration.

Note: You must deploy the federation before trying to invoke the federation configuration web service.

Create a test user

Create a basic TAM user to be used in testing at this point, using the following commands. Once this user has been created, attributes may be added to the user, such as a phone number. For more information, see Create a user.

pdadmin -a sec_master -p $PASSWORD user create testuser cn=testuser,dc=iswga testuser testuser testPwd
pdadmin -a sec_master -p $PASSWORD user modify testuser account-valid yes

Perform a test flow

At this point, there should be a configured OpenID Connect provider federation, partner, and reverse proxy. A grant can now be obtained, although this article has not covered configuring a client of any kind. The following explains how to perform an authorization code flow using Curl. Alternatively, the first half of the flow can be performed in a browser, with the authorization code exchange at the /token endpoint being done via Curl. To initiate an OpenID Connect flow, a request is made to the /authorize endpoint by some method initiated by the client. The URL below can be pasted into a browser to retrieve an authorization code, or Curl can be used.

Note: Authorization codes have a default lifetime of 60 seconds.

You can use the following script to perform an authorization code flow:

#!/bin/bash
#hostname of the reverse proxy
REVERSE_PROXY_HOSTNAME=isam-demo
PROVIDER_ID=acmeProvider
USERNAME=testuser
#populate with testusers password
PASSWORD=
 
#populate with client credentials
CLIENT_ID=
CLIENT_SECRET=
#state is presented at /authorize and /token
STATE=someState$RANDOM$RANDOM$$
#change to a registered redirect URI.
REDIR_URI=https%3A%2F%2FmyService.com%2Fredirect
 
#initial /authorize request
curl -b cookieJar.txt -c cookieJar.txt -v -k
"https://$REVERSE_PROXY_HOSTNAME/isam/oidc/endpoint/amapp-runtime-$PROVIDER_ID/authorize?scope=openid%20profile%20email&response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIR_URI&state=$STATE"
curl -b cookieJar.txt -c cookieJar.txt -v -k "https://$REVERSE_PROXY_HOSTNAME/isam/sps/amapp-runtime-$PROVIDER_ID/oidc/auth"
#this returns a login form
curl -b cookieJar.txt -c cookieJar.txt -v -k "https://$REVERSE_PROXY_HOSTNAME/isam/sps/auth"
 
#POST to the login form
curl -b cookieJar.txt -c cookieJar.txt -v -k -d
"username=$USERNAME&password=$PASSWORD&login-form-type=pwd" "https://$REVERSE_PROXY_HOSTNAME/pkmslogin.form"
curl -b cookieJar.txt -c cookieJar.txt -v -k "https://$REVERSE_PROXY_HOSTNAME/isam/sps/auth"
curl -b cookieJar.txt -c cookieJar.txt -v -k "https://$REVERSE_PROXY_HOSTNAME/isam/sps/amapp-runtime-$PROVIDER_ID/oidc/auth"
#Extract the consent nonce from the consent page
CONSENT=$(curl -b cookieJar.txt -c cookieJar.txt -v -k
"https://$REVERSE_PROXY_HOSTNAME/isam/oidc/endpoint/amapp-runtime-$PROVIDER_ID/authorize?scope=openid%20profile%20email&response_type=code&client_id=$CLIENT_ID&redirect_uri=$REDIR_URI&state=$STATE")
CONSENT_NONCE=$(echo $CONSENT |  grep -o -P '((?<="consentNonce":")[a-zA-Z0-9]*(?=")')
#Hard coded consent decision of "yes" to openid, profile, email
CODE_RSP=$(curl -b cookieJar.txt -c cookieJar.txt -v -k
"https://$REVERSE_PROXY_HOSTNAME/isam/oidc/endpoint/amapp-runtime-$PROVIDER_ID/authorize"
-d
"consentNonce=$CONSENT_NONCE&client_id=$CLIENT_ID&response_type=code&scope=openid+profile+email&state=$STATE&redirect_uri=$REDIR_URI&prompt=consent"
2>&1)
# Extract the code

CODE=$(echo $CODE_RSP | grep -o -P '(?<=code=)[^&]*')
echo code: $CODE
 
#Exchange code for an id token at /token, token does not require a cookieJar
curl -k -u $CLIENT_ID:$CLIENT_SECRET
https://$REVERSE_PROXY_HOSTNAME/isam/oidc/endpoint/amapp-runtime-$PROVIDER_ID/token -d
"code=$CODE&grant_type=authorization_code&state=$STATE&redirect_uri=$REDIR_URI"
 
#Make it stateless
rm cookieJar.txt

The net output of this script should be a successful response from the /token endpoint, as a bearer token, including a claim id_token. This claim is a JSON Web Token (JWT). An example response is:

{
  "access_token": "RMOqVQl7WHMNDeQQBwoXlpP41cqQdZqEq5TGSZrM",
  "token_type": "Bearer",
  "expires_in": 7199,
  "scope": "openid profile email",
  "refresh_token": "M62yhoNS7uGYboXGy7hJsHCyx6DhdMaCJFYEEXvtwNf18zY1AP",
  "id_token": "eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FjbWVQcm92aWRlci5jb20iLCJ
               hdF9oYXNoIjoiVWtwMlo3SnFnSDRna2lPTGJZZnJpQSIsInN1YiI6InRlc3R1c2VyIiw
               iYXVkIjoiOXNwOWhKck1YcExES1BWWTU3TTQiLCJyZWFsbU5hbWUiOiJtZ2EiLCJleHA
               iOjE0NDQ5NzY4NTMsImlhdCI6MTQ0NDk2OTY1M30.ViLy3ixFSkNBfMUFscFiQA6dVWJ
               p4i0QjL7r0_c6e1s"
}

The id_token is made up of three parts, the first two are base64URL-encoded JSON. The third is a signature for verification of the JWT.

$ JWT="eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2FjbWVQcm92aWRlci5jb20iLCJhdF9oYXN
       oIjoiVWtwMlo3SnFnSDRna2lPTGJZZnJpQSIsInN1YiI6InRlc3R1c2VyIiwiYXVkIjoiOXNwOWh
       Kck1YcExES1BWWTU3TTQiLCJyZWFsbU5hbWUiOiJtZ2EiLCJleHAiOjE0NDQ5NzY4NTMsImlhdCI
       6MTQ0NDk2OTY1M30.ViLy3ixFSkNBfMUFscFiQA6dVWJp4i0QjL7r0_c6e1s"
$ JWT_HEAD=$(echo $JWT | cut -f1 -d".")
$ echo $JWT_HEAD | base64 -d
{"alg":"HS256"}
$ JWT_BODY=$(echo $JWT | cut -f2 -d".")
$ echo $JWT_BODY | base64 -d
{
  "iss": "https://acmeProvider.com",
  "at_hash": "Ukp2Z7JqgH4gkiOLbYfriA",
  "sub": "testuser",
  "aud": "9sp9hJrMXpLDKPVY57M4",
  "realmName": "mga",
  "exp": 1444976853,
  "iat": 1444969653
}

Introspect a token

The token introspection endpoint allows clients to find out the validity of an access_token. This endpoint requires authentication with client credentials. An example request would be:

#!/bin/bash
REVERSE_PROXY_HOSTNAME=isam-demo
ACCESS_TOKEN=zWmP2T7qITV2ibMKaicNl8gxoQ9oWlWNX6VAExPE
PROVIDER_ID=acmeProvider
CLIENT_ID=9sp9hJrMXpLDKPVY57M4
CLIENT_SECRET=BYjqNcIHWGrP0wseZMNg

curl -k -u $CLIENT_ID:$CLIENT_SECRET \
https://$REVERSE_PROXY_HOSTNAME/isam/oidc/endpoint/amapp-runtime-$PROVIDER_ID/introspect \
-d "token=$ACCESS_TOKEN"

This would yield the following output:

{
  "sub": "testuser",
  "grant_type": "authorization_code",
  "realmName": "mga",
  "scope": "openid profile email",
  "active": true,
  "exp": 1445228044,
  "token_type": "Bearer",
  "iat": 1445220844,
  "client_id": "9sp9hJrMXpLDKPVY57M4"
}

This provides enough information to populate an STSUU correctly with the information expected.

Note: The values in lines 2, 3, 5, 7, and 10 will be extracted in the mapping rule and returned to the reverse proxy, which will then be included as attributes.

Step 3. Configure the STS Chain

The STS chain to be configured takes an STSUU and, based on its contents, adds attributes that WebSEAL can use as an authorization decision. This is a well-defined interface. Configuring the STS chain is done in the following steps:

  1. Create the chain template.
  2. Upload the mapping rule to be used by the map module.
  3. Create an instance of the chain template.

Create the chain template

Creating an STS Template is done via the STS user interface. For this chain, you need three modules:

  • STSUU module in mode validate
  • Default map module in mode map
  • STSUU module in mode issue

Follow the steps below to configure the template:

  1. Click Security Token Service under Manage.Screen capture showing sts menu option
    Screen capture showing sts menu option
  2. Click Templates.Screen capture showing template selection
    Screen capture showing template selection
  3. Click Add to add a new template.Screen capture showing adding template
    Screen capture showing adding template
  4. Populate the template. Screen capture showing populating template
    Screen capture showing populating template

Upload the mapping rule

Before an instance of the chain can be created, a mapping rule to be used by the default map module needs to be uploaded. There is a web interface that allows the uploading and editing of JavaScript mapping rules. The mapping rule that will be uploaded does the following:

  1. Extracts the access_token from the STSUU.
  2. If no access token is returned, makes no changes to the STSUU.
  3. If there was an access token, makes a request to the /introspect endpoint of the OP.
  4. Checks that the response from /introspect was 200.
  5. If the response from /introspect shows the token is invalid, returns an unauthorized decision.
  6. If the response from /introspect contains the property active = true, the token is valid; extracts the values from the introspect response and inserts them into the STSUU.

This mapping rule populates most of the attributes defined in "OAuth STS Interface for Authorization Enforcement Points."

  1. ISAM Version 9 added a web interface for managing mapping rules. Click Mapping Rules under Global Settings. Screen capture showing selection of mapping rules
    Screen capture showing selection of mapping rules
  2. Click Add to add a new rule. Screen capture showing addition of new rule
    Screen capture showing addition of new rule
  3. Save the rule.Screen capture showing saving of rule
    Screen capture showing saving of rule

The mapping rule is:

//Use the trace strings below to debug:
//com.tivoli.am.fim.trustserver.sts.utilities.* so IDMappingExtUtils.traceString will show.
//com.tivoli.am.fim.trustserver.sts.modules.* rule execution pre/post
//com.ibm.security.access.httpclient.* http client debugging.
importPackage(Packages.com.tivoli.am.fim.trustserver.sts);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.uuser);
importPackage(Packages.com.tivoli.am.fim.trustserver.sts.utilities);
importPackage(Packages.com.ibm.security.access.httpclient);

// Types:
var attribute = "urn:ibm:names:ITFIM:oauth:response:attribute";
var decision = "urn:ibm:names:ITFIM:oauth:response:decision";
var param = "urn:ibm:names:ITFIM:oauth:param";

//Names(minimum):
var USERNAME = "username";
var AUTHZ = "authorized";
var EXPIRES = "expires";
var AT = "access_token";

//Names (extra):

var CLIENT_TYPE = "client_type"; // "confidential" or "public"
var SCOPE = "scope";
var TOKEN_CLIENT_ID = "oauth_token_client_id"

//Constants:
var TRUE = "TRUE";
var FALSE = "FALSE";

// Introspect details:
var ENDPOINT = "https://127.0.0.1/oidc/endpoint/amapp-runtime-acmeProvider/introspect";

// Used to authenticate to /introspect
var CLIENT_ID = "websealClient";
var CLIENT_SECRET = "websealClientSecret";

var access_token = stsuu.getContextAttributes().getAttributeValueByName(AT);

IDMappingExtUtils.traceString("access_token: " + access_token);
if (access_token != null || access_token.trim() != "" ) {
	var body = "token=" + access_token;
	IDMappingExtUtils.traceString("body: " + body);

	var headers = new Headers();
	headers.addHeader("Accept", "*/*");
	/*
	 * httpPost(String url,
	 *          Map headers,
	 *          String body,
	 *          String httpsTrustStore,
	 *          String basicAuthUsername,
	 *          String basicAuthPassword,
	 *          String clientKeyStore,
	 *          String clientKeyAlias);
	 */
	var hr = HttpClient.httpPost(
			ENDPOINT,
			headers,
			body,
			"rt_profile_keys",
			CLIENT_ID,
			CLIENT_SECRET,
			null, null)

	IDMappingExtUtils.traceString("code: " + hr.getCode());
	IDMappingExtUtils.traceString("body: " + hr.getBody());

	if (hr.getCode() == "200") {
		var rsp = JSON.parse(hr.getBody());
		IDMappingExtUtils.traceString("active:" + rsp.active);
		IDMappingExtUtils.traceString("is active:" + (rsp.active == true));
		IDMappingExtUtils.traceString("clientId:" + rsp.client_id);
		IDMappingExtUtils.traceString("sub:" + rsp.sub);
		//active will be false if its bad otherwise
		if(rsp.active == true) {
			// active == authorized
			stsuu.addAttribute(new Attribute(AUTHZ, decision, TRUE));

			//Some attributes
			stsuu.addAttribute(new Attribute(USERNAME, attribute, rsp.sub));
			stsuu.addAttribute(new Attribute(TOKEN_CLIENT_ID, attribute, rsp.client_id));
			stsuu.addAttribute(new Attribute(SCOPE, attribute, rsp.scope));
			stsuu.addAttribute(new Attribute(EXPIRES, decision, secondsToDate(rsp.exp)));

			// Based on the grant type used to get this
			// access_token we decide on the client_type:
			if(rsp.grant_type == "authorization_code"){
				stsuu.addAttribute(new Attribute(CLIENT_TYPE, attribute, "confidential"));
			} else {
				stsuu.addAttribute(new Attribute(CLIENT_TYPE, attribute, "public"));
			}
		} else {
			IDMappingExtUtils.traceString("invalid access token");
			stsuu.addAttribute(new Attribute(AUTHZ, decision, FALSE));
		}
	} else {
		IDMappingExtUtils.traceString("non 200 code");
		stsuu.addAttribute(new Attribute(AUTHZ, decision, FALSE));
	}

} else {
	IDMappingExtUtils.traceString("access_token was null");
	stsuu.addAttribute(new Attribute(AUTHZ, decision, FALSE));
}

/*
 * Helper:
 * Turn an OIDC exp property which is in seconds and turn it into a string
 * suitable for the EXPIRES attribute of a STSUU
 */
function secondsToDate(seconds) {
	var d = new Date(0);
	d.setSeconds(seconds);
	IDMappingExtUtils.traceString("Expires:" + d);
	return IDMappingExtUtils.getTimeStringUTC(d.getFullYear(),
						  d.getMonth(),
						  d.getDay(),
						  d.getHours(),
						  d.getMinutes(),
						  d.getSeconds());
}

Create the chain

Now that there is a template, you can configure a mapping rule to use the instance of the chain. Configure the chain with values that the reverse proxy will use when it invokes the chain. The Issuer address must be urn:ibm:ITFIM:oauth20:token:bearer, but you can configure the Applies To presented by the reverse proxy. The default value that it will present is https://localhost/sps/oauthfed/oauth10. The value that the reverse proxy will use can be set in the [oauth] stanza, under the property default-fed-id. This example uses an Applies To value of https://localhost/sps/oidc.

  1. Add a chain.Screen capture showing adding chain
    Screen capture showing adding chain
  2. Configure a new chain. Screen capture showing chain config
    Screen capture showing chain config
  3. Set the Request Type, Issuer, and Applies To values. Screen capture showing setting of values
    Screen capture showing setting of values
  4. Configure the modules. In this case, you can only configure the default map module with one option, the rule to use. Screen capture showing                 configuration of modules
    Screen capture showing configuration of modules

Test the chain

You can use the code snippet below to test the configured STS chain. The request must be a SOAP message containing a request security token. The message that the reverse proxy will send will contain several pieces of information about the endpoint requested and the reverse proxy. However, only the "access_token" context attribute is used by the provided rule. The example below provides only an access_token. The attributes provided by the reverse proxy can be used to enhance the rule.

#!/bin/bash
#For more on invoking the STS via HTTP.

REVERSE_PROXY_HOSTNAME=isam-demo
ISSUER=urn:ibm:ITFIM:oauth20:token:bearer
APPLIES_TO=https://localhost/sps/oidc
ACCESS_TOKEN=39eAcg8X9PUN6CWGoF9Wnti1KDME9IsjeR8y49fh
USER=easuser
PASSWORD=passw0rd

BODY='
<SOAP-ENV:Envelope
          xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:ns1="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
          xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
          xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
          xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
>
  <SOAP-ENV:Body>
    <ns1:RequestSecurityTokenCollection>
      <ns1:RequestSecurityToken>
        <wsp:AppliesTo>
          <wsa:EndpointReference>
            <wsa:Address>https://localhost/sps/oidc</wsa:Address>
          </wsa:EndpointReference>
        </wsp:AppliesTo>
        <wst:Issuer>
          <wsa:Address>urn:ibm:ITFIM:oauth20:token:bearer</wsa:Address>
        </wst:Issuer>
        <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Validate</wst:RequestType>
        <wst:Base>
          <stsuuser:STSUniversalUser xmlns:stsuuser="urn:ibm:names:ITFIM:1.0:stsuuser">
            <stsuuser:Principal/>
            <stsuuser:AttributeList/>
            <stsuuser:RequestSecurityToken>
              <stsuuser:Attribute name="AppliesTo" type="http://schemas.xmlsoap.org/ws/2004/09/policy">
                <stsuuser:Value>https://localhost/sps/oidc</stsuuser:Value>
              </stsuuser:Attribute>
              <stsuuser:Attribute name="Issuer" type="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                <stsuuser:Value>urn:ibm:ITFIM:oauth20:token:bearer</stsuuser:Value>
              </stsuuser:Attribute>
              <stsuuser:Attribute name="Base" type="urn:ibm:names:ITFIM:1.0:stsuuser">
                <stsuuser:Value>
                  <stsuuser:STSUniversalUser>
                    <stsuuser:ContextAttributes>
                      <stsuuser:Attribute name="access_token" type="urn:ibm:names:ITFIM:oauth:param">
                        <stsuuser:Value>QLW6clF6O6NEXu8KjMZ2r2u9Si4qYtEC67EUMYon</stsuuser:Value>
                      </stsuuser:Attribute>
                    </stsuuser:ContextAttributes>
                  </stsuuser:STSUniversalUser>
                </stsuuser:Value>
              </stsuuser:Attribute>
            </stsuuser:RequestSecurityToken>
            <stsuuser:ContextAttributes>
              <stsuuser:Attribute name="access_token" type="urn:ibm:names:ITFIM:oauth:param">
                <stsuuser:Value>QLW6clF6O6NEXu8KjMZ2r2u9Si4qYtEC67EUMYon</stsuuser:Value>
              </stsuuser:Attribute>
            </stsuuser:ContextAttributes>
            <stsuuser:AdditionalAttributeStatement/>
          </stsuuser:STSUniversalUser>
        </wst:Base>
      </ns1:RequestSecurityToken>
    </ns1:RequestSecurityTokenCollection>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
'
curl -k -vvv --user $USER:$PASSWORD \
https://$REVERSE_PROXY_HOSTNAME/isam/TrustServerWS/SecurityTokenServiceWST13 -d "$BODY"

Note: The endpoint will need an unauth acl attached to it. Alternatively, bind the runtime to an external interface and update the URL to go directly to the runtime. Note: The STS is protected by BA authentication. See Managing User Registries for more information on configuring the user accounts.

Step 4. Configure the reverse proxy as an enforcement point

You now need to configure the reverse proxy to be an OAuth PEP. You will use the same instance from earlier. Make the following changes to the configuration file, which enables OAuth authorization. Take note of the default-fed-id value, as it must correspond to the Applies To value of the STS chain.

[oauth]
fed-id-param = FederationId
default-fed-id = https://localhost/sps/oidc
user-identity-attribute = username
oauth-auth = both
cluster-name = oauth-cluster

[server]
http-method-disabled-remote = TRACE,CONNECT

[tfim-cluster:oauth-cluster]
basic-auth-user = easuser
basic-auth-passwd = passw0rd
handle-idle-timeout = 240
handle-pool-size = 10
ssl-keyfile = pdsrv.kdb
ssl-keyfile-stash = pdsrv.sth
server = 9,https://127.0.0.1/TrustServerWS/SecurityTokenServiceWST13
ssl-keyfile-label = server
[azn-decision-info]
HTTP_CONTENT_TYPE_HDR = header:content-type
HTTP_HOST_HDR = header:host
HTTP_REQUEST_METHOD = method
HTTP_TRANSFER_ENCODING_HDR = header:transfer-encoding
HTTP_AZN_HDR = header:authorization
HTTP_REQUEST_URI = uri
HTTP_REQUEST_SCHEME = scheme

This is scripted in the snippet below.

#! /bin/bash
USER=admin
PASSWORD=
HOST=
INST=default
APPLIES_TO=https://localhost/sps/oidc
RUNTIME=127.0.0.1

function setEntry {
    STZ=$1b
    ENT=$2
    VAL=$3

    curl -k -H "Accept: application/json" -H "Content-Type: application/json" \
    --user $USER:$PASSWORD https://$HOST/wga/reverseproxy/$INST/configuration/stanza/$STZ/entry_name/$ENT \
    -X PUT -d '{"value":"'$VAL'"}'

}

setEntry oauth fed-id-param  FederationId
setEntry oauth default-fed-id  $APPLIES_TO
setEntry oauth user-identity-attribute username
setEntry oauth oauth-auth both
setEntry oauth cluster-name oauth-cluster

setEntry server http-method-disabled-remote TRACE,CONNECT

setEntry "tfim-cluster:oauth-cluster" ssl-keyfile-stash pdsrv.sth
setEntry "tfim-cluster:oauth-cluster" basic-auth-user easuser
setEntry "tfim-cluster:oauth-cluster" handle-idle-timeout 240
setEntry "tfim-cluster:oauth-cluster" handle-pool-size 10
setEntry "tfim-cluster:oauth-cluster" basic-auth-passwd passw0rd
setEntry "tfim-cluster:oauth-cluster" ssl-keyfile pdsrv.kdb
setEntry "tfim-cluster:oauth-cluster" server 9,https://$RUNTIME/TrustServerWS/SecurityTokenServiceWST13
setEntry "tfim-cluster:oauth-cluster" ssl-keyfile-label server

setEntry azn-decision-info HTTP_CONTENT_TYPE_HDR header:content-type
setEntry azn-decision-info HTTP_HOST_HDR header:host
setEntry azn-decision-info HTTP_REQUEST_METHOD method
setEntry azn-decision-info HTTP_TRANSFER_ENCODING_HDR header:transfer-encoding
setEntry azn-decision-info HTTP_AZN_HDR header:authorization
setEntry azn-decision-info HTTP_REQUEST_URI uri
setEntry azn-decision-info HTTP_REQUEST_SCHEME scheme


curl -k -v -L -H 'Accept: application/json' -X PUT --user $USER:$PASSWORD
https://$HOST/isam/pending_changes
curl -k -v -L -H 'Accept: application/json' -H "Content-Type: application/json" -X PUT \
--user $USER:$PASSWORD https://$HOST/wga/reverseproxy/$INST -d
 '{"operation":"restart"}'

Configure the POP

A POP needs to be configured to trigger the OAuth EAS. See "High-level overview of the OAuth EAS for more information about the OAuth EAS. You can use the following pdadmin commands to configure the POP:

pop create oidc-pop
pop modify oidc-pop set attribute eas-trigger oauth_pop_trigger

Attach this to the root resource as a test:

pop attach /WebSEAL/isam-op-default/ oidc-pop
server replicate

Test the PEP

Access tokens can now be used as a method of authenticating a request via a reverse proxy by using the Authorization: Bearer header. The following steps can be followed as a test:

  1. Acquire an access token.
  2. Access the resource without the header and be prompted to log in.
  3. Access the resource with the authorization header and be allowed through.

This is demonstrated below:

$ curl -v -k -L  https://isam-demo/
> GET / HTTP/1.1
> User-Agent: curl/7.37.0
> Host: isam-demo
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 12505
< content-type: text/html
< date: Tue, 20 Oct 2015 01:06:46 GMT
<
...
      &lt;form class="login-container" method="POST"
      action="/pkmslogin.form?token=Unknown"&gt;
... Login form.

$ curl -v -k -L -H "Authorization: Bearer QLW6clF6O6NEXu8KjMZ2r2u9Si4qYtEC67EUMYon" \
https://isam-demo/
> GET / HTTP/1.1
> User-Agent: curl/7.37.0
> Host: isam-demo
> Accept: */*
> Authorization: Bearer QLW6clF6O6NEXu8KjMZ2r2u9Si4qYtEC67EUMYon
>
< HTTP/1.1 200 OK
< content-length: 510
< content-type: text/html
...
   &lt;center&gt;&lt;img src="/pics/iv30.gif" alt=""&gt;&lt;/center&gt;
...Reverse Proxy Landing page

The POP can now be attached to various resources to allow access to them using access_tokens.

Conclusion

This concludes the configuration to enable an ISAM reverse proxy to accept OpenID Connect access tokens as a method of authentication. You should now be able to:

  • Configure an OpenID Connect federation by using both the UI or the web service.
  • Invoke the federation configuration web service.
  • Perform an OpenID Connect authorization code flow.
  • Create an STS template and chain.
  • Create a POP and attach it to a resource.
  • Requesting a page from a reverse proxy by using an access token as a method of authentication

Downloadable resources


Related topics


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Security
ArticleID=1023665
ArticleTitle=Configure an ISAM reverse proxy as a PEP to an OpenID connect provider
publish-date=05092017