Question Limiting Developer App Access Using Scopes

Not applicable

I am not sure that I am going to ask this the right way, but here goes. If we were to have the following Product, Proxy, and Developer Apps:

API Product: Foo

API Proxy: Proxy1 - /proxy1/apples, /proxy/oranges (new)

Developer App: DevApp1, DevApp2

Can we use (or should we) use scopes to allow DevApp1 to have access to /proxy1/oranges, but prevent DevApp2 from having access to /proxy1/oranges?

I am thinking of defining the following scopes at the API Product level:

urn:foo_apples, urn:foo_oranges

Then adding scopes to the individual Developer Apps as follows:

DevApp1: urn:foo_apples, urn:foo_oranges

DevApp2: urn:foo_apples

When the access token is generated via oauth proxy, the Developer Apps should have the proper scopes which can be verified upon calling Proxy1 and thus prevent DevApp2 from accessing /proxy1/oranges.

Is using scopes the right way to control this type of access or is there a better way? And if scopes is appropriate, how can we easily manage the scopes associated with a Developer App?

Thanks.

2 5 1,651
5 REPLIES 5

Good question! There might be a better way. For each API product entity in Apigee Edge, you can configure a list of resource paths that are allowed for the API product.

This means you'd need to change your situation to have 2 API Products,

  • one that has a wildcard path so that any API key for that API product would allow
    requests to /proxy1/apples, /proxy1/oranges, and any other path
  • One that is configured with /proxy1/apples as its path, so that an API key for this second API product would allow requests only for /proxy1/apples

You'd still have 2 developer apps, but each one would be for a different API proxy.

like this:

API Product proxy configured for this API Product resource paths developer app configured for this product
ProductX proxy1 /** DevApp1
ProductA proxy1 /apples DevApp2

You could also introduce other products with other combinations of paths. For example ProductAO that has two paths: /apples and /oranges. Then it would allow requests on both of those paths, but not on /bananas (if you added that in the future).

Coincidentally, just yesterday I created a 5-minute screencast yesterday explaining how this path restriction works. You can find it here.

Keep in mind that the paths you specify for an API product should not include the "basepath" used for the API proxy. In other words, the paths in the API product will be compared against the "pathsuffix" the API Proxy receives at runtime. If your proxy has a basepath of /proxy1 and then handles requests like /proxy1/apples and /proxy1/oranges, then the pathsuffixes are /apples and /oranges, and those are the paths you might consider using in the API Product.

Scopes are an alternative tool for restricting access, but usually scopes are more fine-grained. In other words, the scopes might not be urn:apples or urn:oranges but rather urn:apples:read and urn:apples:delete and so on. Combining a resource + verb.

Scopes are a nice option when you need the power. The one downside of scopes is that you must embed the scope-checking logic in the API proxy itself. You can include a Scope element in the OAuthV2/VerifyAccessToken, but ... what scope should you check for? the API Proxy needs to include some sort of mapping between the thing being requested and the scope required. It'd be a good idea to externalize the mapping of resource+verb tuple to required scope, but today Apigee Edge does not help you do that. You'd need to build that mapping in the KVM, for example, and then read the KVM and apply those rules at runtime in your proxy logic.

And then, either:

  • include Condition elements in the Proxy flow to check the scope for each resource + verb request.

  • include a Scope element in the OAuthV2/VerifyAccessToken policy

I hope this clarifies. let me know if you have further questions.

Great response! I did work out an example using scopes and it seems pretty straight forward. Basically I figured out these steps:

API Product: Configured scopes - such as urn:apples:query, urn:apples:update, urn:oranges:update (named them in this manner based on Apigee recommended practices)

Developer Apps: Using Apigee Edge API updated the scopes for two Developer Apps giving one of the apps all of the scopes and the other app. only one of the scopes

Proxies: Updated the proxy by adding a new flow for /proxy1/oranges where I verify that the OAuth access token contains a scope with urn:oranges:update

Regenerated the OAuth token and viola, every worked as expect and the caller never had to change which I thought was pretty cool. I liked this approach because it lets me add/remove/update scopes for individual Developer Apps and how we have API Products, Developer Apps, and Proxy's really do not change in terms o f structure (if that even makes sense).

The one piece about this approach that I do not like is that the only way to update the scopes on the Developer Apps is via an API call. I would prefer to see this in the UI as it is for API Products. Not sure why that is missing from the UI to be honest. Bit of a head scratcher unless it was purposefully omitted because there is something inherently wrong with using scopes at the Developer App level. In which case I would like to know what that might be.

I am going to dig into your approach to understand it better and work up a local example to see what that might look like. Thank you for the video link. I will certainly be checking that out. Will likely post back here again once I understand the approach and any pros/cons when matched up with what we have today.

Very much appreciated!

Hi Dino,

I worked with a simple example based on the approach you mentioned and read thru the Apigee docs on API Products and custom resource path. Very cool stuff. I very much like the use of custom resource paths. It would however, as you mentioned, require us to re-work our API Products a bit, but in our current use case it is only a single product so not terrible at all honestly.

Correct me if I am wrong in my understanding, but it seems like we can use API Products long with custom resource paths to sort of slice and dice the Proxy resources in a way that allows us to restrict proxy access to various Developer Apps. Would that be a correct interpretation? If so, then that totally makes sense now.

I do wonder what sort of gotchas are under the covers should we follow this approach. For example, how difficult is it to add/remove/update paths and Developer Apps around as the API grows or shrinks?

One other question for you if you have a moment, in terms of deploying your approach to each environment if appears that this is all about managing Products and Developer Apps, but no real changes to the Proxies themselves (other than adding new resources and following the typical deployment strategy). I am wondering what happens to in-flight calls from our integrators at the time that we shift a Developer App. from one Product to another or add custom resource paths?

Products and Developer Apps do not follow any sort of deployment model so at the time the change is made, I assume the change is immediate across all environments that the Proxies are deployed to, correct?

Thanks again and I really appreciate the feedback on the use of scopes and the alternative approach on using custom resource paths!

Correct me if I am wrong in my understanding, but it seems like we can use API Products long with custom resource paths to sort of slice and dice the Proxy resources in a way that allows us to restrict proxy access to various Developer Apps. Would that be a correct interpretation? If so, then that totally makes sense now.

Yes, you should think of the API Proxy as the entity where you describe the execution or handling of requests, and think of API Product as the unit of exposure of APIs to developers. There are two distinct concerns here. With the API Product, you can wrap up multiple proxies, or filter or subset proxies.

But keep in mind that the resource paths that you configure for a product are not tied to a particular proxy. They apply across all proxies in the product. So if you have /apples as a resource path in your product, then credentials for that API Product will be valid when requesting /apples, regardless which proxy included in the product handles that request, and regardless of the basepath for any of those proxies.

Updating the resource paths is simple - there's a single Admin API call to modify it, or of course you could use the Administrative UI to make such a change. Normally I wouldn't expect rapid change to occur at this layer. Here's why: an API Product is the unit of exposure, and developers build apps that depend on the set of APIs included in a product. If you then change the set of APIs in the product, the developer needs to develop something new. So it perhaps makes most sense to introduce a new API product, that contains new APIs, and have the developer obtain a new credential for that app. That seems natural to me. But even if you don't follow this typical path, you can modify products and the set of resource paths for a product.

I am wondering what happens to in-flight calls from our integrators at the time that we shift a Developer App. from one Product to another or add custom resource paths?

Geez, I can't give an exact answer to that. I don't expect that kind of change to be a normal occurrence. But ... if you add or change settings on an API product, these changes will be available nearly immediately, dependent on the cache of the token in the message processor.

Here's how it works: when a token or key is presented in a request, the message processor, the piece of software responsible for validating the token or key, reads from a persistent store, and evaluates the request. Is this a valid token? What's the list of products available for this token? Is the current proxy included in any of the API products in the list? If so, is the current resource path included in that product?

For performance reasons, the MP caches the token metadata in memory. We don't want the MP to read the mostly unchanging data, over and over again, from persistent store. If an app presents that token again, then the MP uses the cached information to satisfy the token check. This cache has a TTL currently of 180s. We may reduce this in the future. But my point is: if you change things, it may take up to 180s in order for the updated information to be refreshed into cache.

Excellent response to my questions. All of what you said makes sense. Thank you so much for the help and furthering my understanding. I think I have plenty to work with at this point. Very much appreciated!