Clarification on scopes.

Hello, 

In our previous technology, each credential had a set scope.  I tried to replicate the key-level scopes through the Apigee REST API, but these scopes are deleted every time I add a product to the credentials from the UI . It's normal?

Clearly it seems to me that I am going in a direction opposite to that desired by the product.

Could you please help me understand how to properly handle scopes?

In our scenario we have:
- 1 proxy that acts as an OPENID Server
- 5 proxies that manage the application components.

The scopes that we handle are three:
-"openid", "INFO" for every api
-"PAYMENT" for payments api.

I thought it was okay to have a single product for this application with these 3 scopes declared, where OIDC server proxy + 5 app proxies are built into it as operations.

Is this ok? I find it strange to add the oauth server as operation in the product, but without that configuration some policies in the OIDC proxy doesn't works.

Another question: if I add more products, the AccessEntity policy does not extract all the products, but only 1, so how do I iterate on the OIDC proxy or tell which scopes are authorized?

I'm following this https://github.com/DinoChiesa/Apigee-Edge-OIDC-Demonstration/tree/master/proxies/oidc-core/apiproxy/... of @dchiesa1 

Thanks

0 7 328
7 REPLIES 7

I tried to replicate the key-level scopes through the Apigee REST API, but these scopes are deleted every time I add a product to the credentials..


what specifically did you try? Can you tell me the command you ran? From your description I am not clear what you're doing.

Could you please help me understand how to properly handle scopes?


Normally scopes are set on the API PRODUCT.

where-do-i-set-scopes.png

I thought it was okay to have a single product for this application with these 3 scopes declared, where OIDC server proxy + 5 app proxies are built into it as operations.


That seems fine to me.

I find it strange to add the oauth server as operation in the product, but without that configuration some policies in the OIDC proxy doesn't works.

Yes, I think I understand what you're talking about here, and I agree. It's strange.

if I add more products, the AccessEntity policy does not extract all the products, but only 1,

Why are you adding more products?  Previously you wrote that you have a SINGLE PRODUCT - you wrote that in bold. What problem are you trying to solve by creating more products?  AccessEntity will extract the information on THE API PRODUCT. At the time of an API request, there is AT MOST ONE API product that is in use. Iterating through a set of API products seems like an administrative task, not something you would do at the time of handling an API request. If you have a credential (a key or token) that is authorized for more than one API Product, at runtime, at the time of OAuthV2/VerifyAccessToken (or VerifyApiKey), Apigee will get the list of API Products the credential is authorized for, and then examine the current request (the proxy), and then select a product from the list that includes that proxy.  If, for a single credential, you have more than one product that wraps a given proxy,  then... the runtime behavior is undefined.  So don't do that. But again, why would you do this? What problem are you trying to solve by doing that?

Here's how it works in general. 

Create a product. Specify a set of one or more proxies, each with one or more operations, on that product. Specify a set of scopes on the product. 

Create an app.  and a credential. Authorize the cred on the product. There is no "scope" setting on the app, or on the credential. 

At the time of request-for-token, the app can request a token, optionally with a set of scopes. If I recall correctly, Apigee will issue a token with a scope that represents the intersection of the requested scopes and the offered scopes. If on the product you have configured scope A,B,C and you request A,Z, you will get a token with scope A.  As far as I recall there is no error if the client app requests a scope which is undefined . (This seems troublesome to me, but I guess it's not a blocker). 

Then, at the time the app uses the token, it sends in a request. Configure the OAuthV2/VerifyAccessToken policy to specify a scope to verify, for the given request. (check the docs on the OAuthV2 policy for how to do this; look at the Scope element) At this time I believe it is not possible to verify more than one scope in one policy.  To check multiple scopes, you must use multiple VerifyAccessToken policies, one after the other.  

Does this make sense?

Hello Dino, 

I show you the first problem related to deleting scopes. I have this app "testapp":

 

{
  "appId": "101262a0-d4ee-4c2d-8960-cd56e8dfed1f",
  "createdAt": "1670404716438",
  "credentials": [
    {
      "apiProducts": [
        {
          "apiproduct": "Product1",
          "status": "approved"
        }
      ],
      "consumerKey": "Sr31GFe66oTCITgKWnQwEAGoxhDOriYQHv4k26qmkGWA8XAk",
      "consumerSecret": "RYJpPkIpplKitnkmx3P882PUKmzGTkUGQGEFolJtCK9NURGw7cSdAME2qkCP8vGD",
      "expiresAt": "-1",
      "issuedAt": "1670404716467",
      "status": "approved"
    }
  ],
  "developerId": "a717caaf-b834-49a3-aa05-499f62f79a7e",
  "lastModifiedAt": "1670404716438",
  "name": "testapp",
  "status": "approved",
  "appFamily": "default"
}

 

 If I add scopes to the keys with this request:

 

curl --request PUT 'https://apigee.googleapis.com/v1/organizations/my-org/developers/test@test.com/apps/testapp/keys/Sr31GFe66oTCITgKWnQwEAGoxhDOriYQHv4k26qmkGWA8XAk' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer XXXXX' \
--data-raw '{
  "scopes": [
    "openid"
  ]
}
'

 

 then the output of the app has the new "Scopes" field:

 

{
  "appId": "101262a0-d4ee-4c2d-8960-cd56e8dfed1f",
  "createdAt": "1670404716438",
  "credentials": [
    {
      "apiProducts": [
        {
          "apiproduct": "Product1",
          "status": "approved"
        }
      ],
      "consumerKey": "Sr31GFe66oTCITgKWnQwEAGoxhDOriYQHv4k26qmkGWA8XAk",
      "consumerSecret": "RYJpPkIpplKitnkmx3P882PUKmzGTkUGQGEFolJtCK9NURGw7cSdAME2qkCP8vGD",
      "expiresAt": "-1",
      "issuedAt": "1670404716467",
      "scopes": [
        "openid"
      ],
      "status": "approved"
    }
  ],
  "developerId": "a717caaf-b834-49a3-aa05-499f62f79a7e",
  "lastModifiedAt": "1670404716438",
  "name": "testapp",
  "status": "approved",
  "appFamily": "default"
}

 

If I add a product with PUT or POST method (i don't noticed differences):

 

curl --request PUT 'https://apigee.googleapis.com/v1/organizations/my-org/developers/test@test.com/apps/testapp/keys/Sr31GFe66oTCITgKWnQwEAGoxhDOriYQHv4k26qmkGWA8XAk' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer XXXXX' \
--data-raw '{
    "apiProducts": ["Product2"]
}'

 

 The new app output become:

 

{
  "appId": "101262a0-d4ee-4c2d-8960-cd56e8dfed1f",
  "createdAt": "1670404716438",
  "credentials": [
    {
      "apiProducts": [
        {
          "apiproduct": "Product1",
          "status": "approved"
        },
        {
          "apiproduct": "Product2",
          "status": "approved"
        }
      ],
      "consumerKey": "Sr31GFe66oTCITgKWnQwEAGoxhDOriYQHv4k26qmkGWA8XAk",
      "consumerSecret": "RYJpPkIpplKitnkmx3P882PUKmzGTkUGQGEFolJtCK9NURGw7cSdAME2qkCP8vGD",
      "expiresAt": "-1",
      "issuedAt": "1670404716467",
      "status": "approved"
    }
  ],
  "developerId": "a717caaf-b834-49a3-aa05-499f62f79a7e",
  "lastModifiedAt": "1670404716438",
  "name": "testapp",
  "status": "approved",
  "appFamily": "default"
}

 

as you can see, scopes are deleted...

Probably every time I have to submit the entire key object in order not to lose information?

I expected the PUT method to change only some fields... otherwise what is the difference with the POST?


@Giupo wrote:

Probably every time I have to submit the entire key object in order not to lose information?


Yes, it seems so. I also observed what you described. I tried some other things too, and this is what I found:

  • adding an API Product is additive, it adds to the list of existing api products.
  • adding a scope is not additive. Your evidence does not show adding a scope to an existing set of scopes. It shows adding a scope to a credential that has no scopes at all. But I tested it and passing "scopes" : ["scopename"] does not add a scope to the existing list. It replaces the existing list. Passing no scopes causes... the existing scopes to be erased.

This is obviously inconsistent. It's confusing, and it seems like a bug to me. I filed one on your behalf. internal ref: b/262315977

> EDIT - 2022 Dec 13 - the team was able to file a fix for this pretty quickly. It will be in the next Apigee X control plane update, which will probably be in a few weeks. Not sure of the holiday update schedule. But for sure, by some time in January.

In the meantime, to avoid or workaround this bug, PUT a payload that includes both the product(s) to be added and the scopes to be retained.

 

PUT :gaambo/v1/organizations/:org/developers/:developerId/apps/:app/keys/:key
Authorization: Bearer :token
Content-type: application/json

{
  "apiProducts": [ "NewProduct-2022dec02"],
  "scopes": ["Existing-Scope1", "Existing-Scope2"]
}

 

I think the same is true with attributes - you need to submit the existing values in your update request, in order to keep those values.

In addition to what I said above, let me explain the problem of multiple products.

We have a scenario where we provide one key per customer, and this customer who can buy multiple products, will consume them with the same key.

When the client calls the /authorize api, in this phase only authorized scopes were accepted on the old product. Instead, on apigee do you say that scope control should be demanded to the policy that generate the token?

Our requests flow starts with:

GET 'https://XXXX.com/auth/oauth/v2/authorize
?client_id=12345
&redirect_uri=my-callback
&scope=openid%20ScopeX%20ScopeY%20ScopeZ
&response_type=id_token%20token'

Here, in addition to validating the key, we already verify the scopes. To do this I should either iterate all the scopes of all the products (but i can't with AccessEntity Policy, as you said) or I should register the union of the scopes of all the products through the REST API within the key (but if I add products, these scopes are deleted). Do you have any suggestions for implementing OpenID Server?


@Giupo wrote:

When the client calls the /authorize api, in this phase only authorized scopes were accepted on the old product.


Can you elaborate or clarify?  Are you saying, that if you update the app, to add a new product, then use the /authorize endpoint (and eventually invoke GenerateAccessToken I guess), that the list of scopes in the generated token does not include scopes that are valid for the new product?  This may be due to a caching issue. In other words if you want a few minutes you may see what you expect. 

If not that, then can you explain in more verbose terms what you are seeing?


@Giupo wrote:

I should register the union of the scopes of all the products through the REST API within the key (but if I add products, these scopes are deleted).


I believe if you have an app with no scopes, then it will be able to request tokens with a subset of all the scopes on all the products.

Given these circumstances:

  • a key is authorized for Product P1 and P2 
  • P1 has scopes SA and SB
  • P2 has scopes SC and SD
  • the app/key itself has no scopes defined on it. 

In that case,  the app can request a token with scopes of any combination of (SA, SB, SC, SD).  If you want to restrict the app from requesting a token for all of those scopes, for example if you want an app authorized for P1 and P2 but want to prevent the app from getting a token with Scope SD, then you must explicitly set the scopes on the app credential (aka key) to the subset of (SA SB SC). 

Also keep in mind the runtime may* keep a cache of keys, apps, and products. The cache gets populated, I believe when the runtime generates a token or validates a token.  If, after the cache is populated, you update a Key, to add or change scopes or products, then request a new token, it is possible that the runtime may not immediately use the updated configuration.  I believe the TTL on the cache is 3 minutes. So consider that when testing. Wait a while after using the apigee API to update the config entities, before re-testing. 

(* I say it "may" keep a cache because while there is a cache, I am not sure of its use in your scenario specifically. I haven't tested that scenario specifically. It should be easy for you to test. )

Thanks Dino,

How can we avoid generating a token if none of the scopes are granted?

I wouldn't want them filling up the db with unscoped tokens

@dchiesa1 any advice? At least to understand if it is better to leave out the scope check in the authorize api, which you instead did in the OpenID proxy, but I think you had a single key per product there.