OAuth 2.0: Authorization through a Custom Policy

Runtime policies enable what is called “cross cutting concerns” to be injected onto an API without embedding code at the application level.  Cross cutting concerns affect the entire application and should be centralized in one location if possible.  They typically represent operations such as authentication, authorization, caching, exception management, logging and instrumentation.

Runtime policies are defined in Anypoint Platform as out-of-the-box components or through custom policies.  They are self-contained and consist of a policy configuration XML file and a policy definition YAML file.  The Mule runtime polls the API Manager on outbound port 443 over HTTPS by default every 5 seconds for new and updated policies and stores them in the $MULE_HOME/policies folder.

If you enable External Identities through Anypoint Platform you can use the out-of-the-box OAuth 2.0 access token enforcement using external provider runtime policy provided by MuleSoft.  However there are cases where you may want to define your OAuth validation through a custom policy.

Get Token API

​To get the access token, first create an API Gateway Application that returns the access token from the Identity Provider.  This is just a straight through proxy application.  Make sure you set the grant type to client credentials.

curl -i -X POST \
   -H "Content-Type:application/x-www-form-urlencoded" \
   -d "client_id=client_id_value" \
   -d "grant_type=client_credentials" \
   -d "client_secret=client_secret_value" \
 'https://vipname/api/oauth2/token/v1'

OAuth 2.0 Custom Policy

​To validate the token through a custom policy can use the archetype provided to you through the Anypoint Studio Custom Policy Editor (currently beta). 

Configuration XML

First we need to add the HTTP Request Configuration to make the outbound call to the Identity Provider to validate the token.  Properties denoted in YAML notation need to be defined in the YAML Definition file and will be exposed as external properties when you apply the policy. 

Currently you cannot use YAML notation in an integer field such as port, this is a current limitation and therefore forced us to hardcode it at this point.  Hopefully this will be fixed in later versions.

<http:request-config name="HTTP_Request_Configuration" protocol="HTTPS" host="{{ oauthHost }}" port="443">
    <tls:context>
        <tls:trust-store path="file:C:/mule/certs/truststore.jks" type="jks" password="{{ oauthPassword }}"/>
     </tls:context>
</http:request-config>

​The pointcut is a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of an API with a certain name and version).

<pointcut>
    <api-platform-gw:api-pointcut apiName="{{ apiName }}" apiVersion="{{ apiVersionName }}"/>
</pointcut>

​Advice is an action taken at a particular join point. Different types of advice include “around,” “before” and “after” advice.   In this case we are implementing before advice as we want to validate the token before we execute the API.

​To make the service call to validate the token with the Identity Provider (in our example, PingFederate) we are passing two headers, one for the basic authorization containing the Client ID and Client Secret, and one for the access token.  The token validation is called inside an enricher so that we preserve the payload and variables in the scopes from the inbound call.  Failure to validate the token will propagate the errors to the calling application.

<before>
    <mule:enricher target="variable:tokenValidation">
      <mule:processor-chain>
          <mule:set-payload
               value="grant_type=urn:pingidentity.com:oauth2:grant_type:validate_bearer
                   &R1HIOMmfC3vkHNHfe9JQK58tQVlH&token=#[message.inboundProperties.token]" />
         <http:request config-ref="HTTP_Request_Configuration" path="{{ oauthPath }}" method="POST">
            <http:request-builder>
                <http:header value="#[message.inboundProperties.authorization]" headerName="Authorization"/>
                <http:header value="application/x-www-form-urlencoded" headerName="Content-Type"/>
            </http:request-builder>
         </http:request>
         <mule:object-to-string-transformer />
         <mule:logger level="INFO" message="RESPONSE FROM POLICY: #[payload]" />
         <mule:logger level="INFO" message="*** TOKEN VALIDATED ***" />
       </mule:processor-chain>
    </mule:enricher>
</before>

YAML Definition

​The YAML Definition file contains the configuration attributes for the policy.  In addition to the default values generated when you create a Custom Policy Project you also need to add the boolean flag requiresConnectivity: false.

All the values denoted in YAML notation in the XML Configuration file by {{  someValue  }} are configured here. Configuration:

  • propertyName: oauthHost
    • name: OAuth 2.0 Host
    • description: OAuth 2.0 Host Name
    • type: string
    • optionalfalse
    • defaultValue: “idp.hostname.com”
  • propertyName: oauthPath
    • name: OAuth 2.0 Path
    • description: OAuth 2.0 Path
    • type: string
    • defaultValue: “/as/token.oauth2”
    • optionalfalse
  • propertyName: oauthPassword
    • name: OAuth 2.0 Password
    • description: OAuth 2.0 Password
    • type: string
    • optionalfalse

​The default values will then be populated when you select to apply the policy.

Authentication Process

When a client wants to access an application protected by OAuth through the custom policy without setting up external identities there is some part of the process that will be manual.

  1. Use the API Portal to Request API Access.  This will issue the client a Client ID and Client Secret.
  2. Add the Client ID and Client Secret to the Identity Provider so that you can generate access tokens.
  3. The client then makes a call to the Get Token API which returns the token.
  4. The client then calls the API which has the Custom OAuth 2.0 Policy applied with the two headers for authorization and the token.
0

Leave a Reply

Your email address will not be published. Required fields are marked *