API products from resource centered proxies - not possible?

Hi!

I have a question about packaging of API products related to how proxies are implemented. It’s about a potential conflict between a functional grouping (API Product) and a resource based grouping (API proxy).

It seems a good practice + in line with REST, to group operations in a proxy based on the concept of resources, e.g. implement all operations related to /products in one proxy, or all operations related to /users in another proxy.

API Products requirements

When defining an API Product the first step is to include all the proxies which contains the operations that should be part of the API Product. Let’s for example assume that the API Product should contain 2 operations from /users (which holds a total of 10 operations), and 4 operations from /products (which holds a total of 15 operations).

Point being made: an API Product needs to contain/expose a subset of operations implemented in different proxies.

A constrain when defining products is that the base-path is defined as the path component that uniquely identifies the API proxy - hence the base-path must always be unique.

Setting up API proxy

When building a proxy, it's required to find out a unique base-path for my proxy. Let's say it is: domain-X/users.

Now I want to implement support for getting a list of all users, i.e. GET /users

Next is to decide how the call should be routed internally in the proxy. Let´s assume I set the match to be "/users" within the proxy. To get a list of users I would call:

GET http://api.com/ domain-X/users/users

That´s a really ugly URL! A design requirement coming from REST, is that I want the path to look like this http://api.com/ domain-X/users hence I don´t want to introduce extra/additional levels in my API (e.g. GET /users/users).

Since "/users" already is part of my base-path, I need to route requests that doesn´t contain any further qualification (a part from the base-path) to the flow responsible for getting a list of users. So, in practise I want to get a list of users when I call URL + base-path.

API Product – practical example

So, I’ll add the "/users" and "/products" proxies in the API Product design section.

Now, let’s assume my API Product should contain operations GET /users and GET /products/{product-id}:

GET http://api.com/domain-X/users

GET http://api.com/domain-X/products/{product-id}

But I don´t want to expose any other operations implemented in neither the domain-X/users nor domain-X/products proxies.

The “resource path” restriction when designing an API Product only supports restriction at paths "within" the proxy (referred to as proxy path suffix) . Hence not possible to use a combination of base-path + path-suffix to qualify/determine the scope (what should be part of) the API Product.

The practical implications of this limitation is that if the resource path is set to "/", the API consumer could access both GET /users and GET /products - the later which I don´t want to expose access to.

Is there is way to fully decouple API Product design from API proxy implementation?

Solved Solved
1 2 464
1 ACCEPTED SOLUTION

You can do what you want. You will need to set a specific variable called flow.resource.name. This is mentioned in the OAuthV2 policy documentation. But it also works with the VerifyApiKey policy.

Here's how it works.

Set up the API Product as you normally would.

  • specify the list of API proxies
  • Specify the list of resource paths. In your case maybe [ /users , /products/* ]
  • In your API proxy, before calling VerifyAPIKey or OAuthV2/VerifyAccessToken, set the variable named flow.resource.name
  • At runtime the value of that variable will be compared against the resources listed for the API Product. Only if there is a match will the key or token be authorized.
  • wildcards still work as you would expect.

OK, so how do you set the variable to the value you want?

Assuming proxies in your environment like this:

proxy name basepath
users /domain/users
products /domain/products

And API Products like this:

product nameproxiesresources
Product1users, products/users , /products/*
Product2users, products/users, /users/* , /products , /products/*

Let's look at the values of context variables that are auto-populated:

inbound request handling proxy request.path proxy.pathsuffix
/domain/users users /domain/users -empty string-
/domain/users/1234 users /domain/users/1234 /1234
/domain/products products /domain/products -empty string-
/domain/products/1234 products /domain/products/1234 /1234

Neither request.path nor proxy.pathsuffix will hold the "right value" for you to specify the resources like /users , /users/* , /products , and /products/* .

BUT, you could get the "right value" by just trimming off the /domain from the request.path. Doing that would look like this in JavaScript:

function trimFirstUrlPathElement(s) {
  var re1 = new RegExp('^/[^/]+(/.*)$');
  return s.replace(re1, '$1');
}

context.setVariable('flow.resource.name', 
                    trimFirstUrlPathElement(context.getVariable('request.path')));

That will set flow.resource.name to "/users", "/users/1234", "/products", and "/products/1234" for each of the four cases in the preceding table. That's what you want.

Attach that as a JS policy right before your key / token verification. Like this in the flow:

  <PreFlow name="PreFlow">
    <Request>
        <Step>
          <Name>JS-SetFlowResource</Name>
        </Step>
        <Step>
          <Name>VerifyAPIKey-1</Name>
        </Step>
    </Request>
     ...

As you experiment, keep in mind that information on API Keys is cached in the MP. So if you create an API Product and App, then use the key for the app in a request, the VerifyAPIKey or VerifyAccessToken will bring into cache the product + resource info for that key. If you then change the API Product to include more or different resources, and then re-use the same API key for a new call, the cached info will be used at runtime. This will confuse you, as the changes you've made will seem to have no effect.

To avoid this, when you make changes to the API Product, specifically changing the list of resources, or proxies, also create a new App (get a new credential), and use THAT credential for testing calls.

or you could wait til the cache expires for the particular key. How long is the information cached? I dunno. I think 3 minutes. (the TTL is not documented)

View solution in original post

2 REPLIES 2

You can do what you want. You will need to set a specific variable called flow.resource.name. This is mentioned in the OAuthV2 policy documentation. But it also works with the VerifyApiKey policy.

Here's how it works.

Set up the API Product as you normally would.

  • specify the list of API proxies
  • Specify the list of resource paths. In your case maybe [ /users , /products/* ]
  • In your API proxy, before calling VerifyAPIKey or OAuthV2/VerifyAccessToken, set the variable named flow.resource.name
  • At runtime the value of that variable will be compared against the resources listed for the API Product. Only if there is a match will the key or token be authorized.
  • wildcards still work as you would expect.

OK, so how do you set the variable to the value you want?

Assuming proxies in your environment like this:

proxy name basepath
users /domain/users
products /domain/products

And API Products like this:

product nameproxiesresources
Product1users, products/users , /products/*
Product2users, products/users, /users/* , /products , /products/*

Let's look at the values of context variables that are auto-populated:

inbound request handling proxy request.path proxy.pathsuffix
/domain/users users /domain/users -empty string-
/domain/users/1234 users /domain/users/1234 /1234
/domain/products products /domain/products -empty string-
/domain/products/1234 products /domain/products/1234 /1234

Neither request.path nor proxy.pathsuffix will hold the "right value" for you to specify the resources like /users , /users/* , /products , and /products/* .

BUT, you could get the "right value" by just trimming off the /domain from the request.path. Doing that would look like this in JavaScript:

function trimFirstUrlPathElement(s) {
  var re1 = new RegExp('^/[^/]+(/.*)$');
  return s.replace(re1, '$1');
}

context.setVariable('flow.resource.name', 
                    trimFirstUrlPathElement(context.getVariable('request.path')));

That will set flow.resource.name to "/users", "/users/1234", "/products", and "/products/1234" for each of the four cases in the preceding table. That's what you want.

Attach that as a JS policy right before your key / token verification. Like this in the flow:

  <PreFlow name="PreFlow">
    <Request>
        <Step>
          <Name>JS-SetFlowResource</Name>
        </Step>
        <Step>
          <Name>VerifyAPIKey-1</Name>
        </Step>
    </Request>
     ...

As you experiment, keep in mind that information on API Keys is cached in the MP. So if you create an API Product and App, then use the key for the app in a request, the VerifyAPIKey or VerifyAccessToken will bring into cache the product + resource info for that key. If you then change the API Product to include more or different resources, and then re-use the same API key for a new call, the cached info will be used at runtime. This will confuse you, as the changes you've made will seem to have no effect.

To avoid this, when you make changes to the API Product, specifically changing the list of resources, or proxies, also create a new App (get a new credential), and use THAT credential for testing calls.

or you could wait til the cache expires for the particular key. How long is the information cached? I dunno. I think 3 minutes. (the TTL is not documented)

Great reply, it works, thanks Dino!