Recently I’ve been looking into ForgeRock’s OpenAM; ForgeRock maintain and distribute OpenAM; OpenAM is of Sun MicroSystems OpenSSO open-source heritage.
In this blog I will demonstrate how to integrate the Layer 7 XML Networking Gateway with ForgeRocks OpenAM. In this scenario we will be using the OpenAM RESTful Services to provide externalized service authentication and authorization. More information on the OpenAM RESTful Services API can be found at ForgeRock (http://openidm.forgerock.org/confluence/display/openam/Use+OpenAM+RESTful+Services).
Integrating OpenAM’s previous incarnations OpenSSO and SJSAM both have well documented integrations:
Below is the Layer 7 Policy for the RESTful Investing Service.
The policy secures the Investing Service requests/responses using SSL (line 2); this protects the credentials and cookie used to authenticate to the Gateway. Service clients are required to either provide a session cookie containing a valid OpenAM token (line 10) or provide a username/password via Basic Authentication (line 11). The session cookie’s token is validated against OpenAM using the /identity/isValidToken RESTful service; whilst username/password credentials are validated against the OpenAM’s /identity/authenticate RESTful service.
In line 3 we have included the Audit Messages in Policy Assertion. We have set the Audit Events level to Warning and saved the Investing Requests and Reponses. This provides a simple method to test and debug our policy. Note: auditing settings should be limited to development and problem resolution.
Lines 13-33 handle authentication for the Investing Policy. The At least one assertion must evaluate to true Assertion shown below requires cookie based user verification (line 14-23) or username/password authentication (lines 24-29). Failing session verification/user authentication an Unauthorised HTTP Error Code and message are returned (lines 30-33).
The All assertions must evaluate to true Assertion in line 14 attempts to extract an OpenAM session token from a session cookie, once extracted the session token is validated against OpenAM to assert access.
Note: The OpenAM C66Encode setting is required to store our OpenAM session tokens to cookies and use tokens with the OpenAM RESTful Services. The c66Encode setting replaces the OpenAM token ‘#’, ‘@’ and ‘=’ characters with the URL encoding friendly ‘.’ and ‘*’ characters.
The request.http.header.Cookie context variable contains the cookies presented to the Gateways Investing Service Policy. The Compare Expression Assertion (line 16) checks the request.http.header.Cookie context variable for a session cookie. If the session cookie is present within the context variable we then use the Evaluate Regular Expression Assertion (line 17) to extract the OpenAM token from the session cookie.
The Regular Expression Assertion stores the extracted token into the tokens context variable array. We then use the Set Context Variable Assertion (line 18) to set the token context variable from the tokens array. If either the session cookie does not exist or isn’t populated the All Assertion branch at line 14 returns false; this then requires username/password authentication or returns unauthorized access.
The All Assertions’ Route via HTTP(S) Assertion (line 21) uses the/identity/isTokenValid RESTful Service to validate the extracted session token against OpenAM. The Routing Assertion uses a predefined emptyrequest context variable as the routing HTTP Request; this ensures we are not routing sensitive backend service requests to OpenAM. The Routing Assertions HTTP Response is stored to the openamValidateResponse context variable.
The OpenAM /identity/isTokenValid Service returns a simple boolean=true or boolean=false response. Using a Regular Expression Assertion (line 22) we evaluate the OpenAM HTTP Response contained within the openamValidateResponse context variable.
If the openamValidateResponse context variable contains boolean=true then the OpenAM session token was successfully validated and policy execution continues to authorization. If the Routing Assertion fails to retrieve a response or the Regular Expression Assertion fails to determine a valid session token then the All Assertion branch at line 14 fails. In event of All Assertion branch failure the Policy falls back to OpenAM Authentication and then to UnAuthorized.
The first time a client invokes Investing Service; the All Assertion at line 14 will fail. The Cookie based authentication provided by the aforementioned All Assertion only takes place after successful credential authentication. The All Assertion ordering is important, the cookie All Assertion appears before the username/password All Assertion to ensure previously authenticated clients are validated via cookie rather than re-authentication.
The All Assertion in line 24 uses the username and password credentials with the /identity/authentication RESTful Service to authenticate clients whilst establishing a session token.
Upon successful authentication the /identity/authentication RESTful Service returns the OpenAM session token within the HTTP Response. Using the Regular Expression Assertion (line 27) we extract the returned session token from the HTTP Response; this session token is then stored within the token context variable using the Set Context Variable Assertion.
If the All Assertion authentication Routing fails to retrieve a response or the Regular Expression Assertion fails to retrieve session token then the All Assertion branch at line 24 fails. In event of All Assertion branch failure both authentication mechanisms have been exhausted; an authorized HTTP Error Code and Message are returned to the client using the Customize Error Response Assertion (see All Assertion line 30).
In the event the All Assertion at line 24 was successful policy execution continues to authorization. If authorization is successful the session token is later returned to the client using a Set-Cookie statement on the HTTP Response.
Lines 35-44 handle authorization for the Investing Service Policy.
The At Least Assertion at line 35 requires resource access to be granted via OpenAM; otherwise, a forbidden HTTP Error Code and Message are returned to the client. The All Assertion at line 36 uses the OpenAM /identity/authorize RESTful Service together with the previously acquired session token, URL and HTTP Method of the requested resource to determine access. Like the /identity/isValidToken Service the /identity/authorize Service returns a simple boolean=true or boolean=false result.
Note: When applying OpenAM authorization to SOAP Services consider using the SOAP Action as the authorization request action. In the case of RESTful services the HTTP Method reflects the type of RESTful service operation.
Using a Regular Expression Assertion (similar to the isValidToken Service Regex Assertion) we validate the HTTP Response returned by the Authorize Service is grants access (HTTP Response equals boolean=true).
Providing the All Assertion at line 36 holds we consider access to the backend is granted, and execution continues to backend routing. If either the Routing Assertion (line 38) or Regular Expression Assertion (line 39) fails, then a HTTP Error Code and Message are returned via Customize Error Response Assertion at line 43.
The Route via HTTP(S) Assertion at line 46 sends the clients request to the backend and returns the result. In addition to returning the backend result we customize the HTTP Headers returned to the client, setting OpenAM token within the Session Cookie. We also set the domain of the Cookie to the Gateway hostname using the request.url.host context variable. The Cookie scope could also be restricted a specific Service Policy using the Cookie Path attribute.