Oracle Cloud API Gateway - Using an Authorizer Function for Client Secret Authorization on API Access image 92

Oracle Cloud API Gateway – Using an Authorizer Function for Client Secret Authorization on API Access

The objective in this article: create a simple Authorizer Function that checks the Client Secret passed in API calls and allows and denies requests based on whether the correct Client Secret is included. The Authorizer Function does a little more than simply accept or deny: in case of accept it also return a token object that becomes part of the request sent to the real backend.

image

Steps:

  1. Create a Function with Fn (in any runtime language, for example Java, Go or Node) – that returns HTTP 200 in case of success or 5xx in case of failure. The response body in case of success has a prescribed format.
  2. Deploy the Function to an OCI environment
  3. Make sure the API Gateway has access to the Function
  4. Configure a Request Policy – on either the API Deployment or a specific Route within an API Deployment – of type Authentication and associated with the Authorizer Function
  5. Make calls – with or without security header – to the API – and verify if the authorization is enforced as expected

Sources discussed in this article can be found on GitHub: https://github.com/lucasjellema/oci-cloud-native-explorations/tree/master/client-secret-authorizer

Here we go.

Create a Function with Fn to act as Authorizer Function

(in any runtime language, for example Java, Go or Node) – that returns HTTP 200 in case of success or 5xx in case of failure. The response body in case of success has a prescribed format.

Using the following Fn CLI command, I create the new function:

fn init –runtime node client-secret-authorizer

image

Add file authorizer.js and implement some naive form of authorization logic:

image

A check is performed if the last character in the token is a number and if that number if even. If it is, the function authorizeClientSecret simply creates a positive response, a JSON object in the format expected by API Gateway with data that hopefully we can somehow make available to the function that the API route will send the request to. If it is not – an even number – then a negative advise is returned.

Test this logic:

image

Now I connect the authorizer.js module to the generated func.js file:

image

This function checks if a token was passed in; if not, it will return a failed authorization response (active is false, wwwAuthenticate is set). The result is that the API Gateway will not route the request forward and instead returns a 401 (Unauthorized) to the original caller. Which is good. Of all the properties in the response object in this case, only the wwwAuthenticate property seems used and is seen as HTTP header in the 401 response. If a token was passed in, the authorizeClientSecret function is invoked to perform a check on the contents of the header or token. If this check fails, this also results in response with active is false.

When the authorization is successful, no header or context element seems influenced by what the Authorizer function does and returns to the API Gateway.

Note: on thing that confused me is the expiresAt property. This property advises the API Gateway on how long the authorization result for the provided input – token or header – is valid. And the API Gateway uses this information to cache the response from the Authorizer function. If the expiresAt indicates for a certain input that the authorization result is valid for one minute or one hour, then API Gateway will not call the Authorizer function again for that same input during the minute or hour during which the result should hold true. During testing the set up of API Gateway Policy and Authorizer function it could be useful to start with an expiresAt setting in the past – to make sure the Authorizer is called on every request.

Deploy the Function to an OCI environment

Deployment of the function is like always:

fn deploy –app lab-app

fn list functions lab-app

SNAGHTML3cbe8578

Invoke the function.

The response depends on whether there is a token and if that token ends with an even number:

SNAGHTML3cbcc8b5

Make sure the API Gateway has access to the Function

The Function should be in a compartment on which the API Gateway has been granted Function Invocation privileges. This is described in this article.

Configure a Request Policy in the API Gateway

– on either the API Deployment or a specific Route within an API Deployment – of type Authentication and associated with the Authorizer Function

In the OCI console for API Gateway, I edit the Deployment /fn – add an API Request Policy of type Authentication. Indicate which function is to be invoked in order to perform Authorization and which HTTP Header provides the token value that is to be passed to the function in order to do authorization:

image

Because I have specified that Anonymous access is enabled, I can specify per route in the API Deployment if Authorization is enforced or anonymous access is allowed for a specific route.

For route /hello I have specified that Authorization is required and no anonymous access is allowed:

image

Make calls – with or without security header – to the API – and verify if the authorization is enforced as expected

With all changes made and deployed, I am ready to invoke the hello API – with or without the required header client-secret. I get the expected authorization enforcement:

When the Authorizer returns a response with active set to true, the API Gateway correctly accepts the request and routes it to the specified backend – function hello in this case.

However, none of the response element returned by the Authorizer function are found as either headers or Fn context properties. I do not see the user principal or other aspects of the security context object returned by the Authorizer function. It seems the finding of the Authorizer is interpreted correctly – but its conclusion about the identity and other properties of the security principal are not made available to the backend function. Which is not as expected. I have requested help from the API Gateway team at Oracle and they are currently investigating whether this could be a bug.

Here a screenshot in Postman of a request to /fn/hello with a valid client-secret header. The Authorizer is invoked. None of its response is available as header or in the context object. The original headers are available in the backend function – as expected.

image

The next screenshot shows a request with an invalid value for client-secret. The Authorizer is invoked. None of its response is available as header or in the context object, except for the WWW-Authenticate header.

image

When the Authorizer function returns a result object with active set to false (or an HTTP Status Code of 5XX in case of an error in the Function execution), the API Gateway correctly returns a 401 Status to the caller, indicating that the request is unauthorized; the header WWW-Authenticate is correctly derived from the wwwAuthenticate property in the response object from the Authorizer function; none of the other properties in the response object seem to make an appearance in the response or anywhere else.

image

I hope I can update this article in the very near future with an explanation on how the security context provided by the Authorizer function can be leveraged in the backend Function.

Credits

I would like to thank the API Gateway Engineering Team at Oracle – specifically Bobby Tse – the on-call Engineer – and Robert Wunderlich – the Product Strategy Director – for their very rapid support on a Sunday that helped me tremendously with understanding the behavior and overcoming some challenges along the way.

Resources

Sources discussed in this article can be found on GitHub: https://github.com/lucasjellema/oci-cloud-native-explorations/tree/master/client-secret-authorizer

API Gateway Documentation on Authorizer Function: https://docs.cloud.oracle.com/iaas/Content/APIGateway/Tasks/apigatewayaddingauthzauthn.htm#Creating

Practical example of using Authorizer Function with Oracle IDCS : https://www.ateam-oracle.com/the-cloud-native-approach-to-extending-your-saas-applications/comment-submitted?cid=da65600f-6df7-4f66-ad35-6ba79847cae9

Fn documentation on Functions in Node: https://github.com/fnproject/docs/tree/master/fdks/fdk-node

One Response

  1. David Santiago Osorio Vasquez April 30, 2020