Scope not respected in OAuthV2 GenerateAccessToken operation

Hello,

I have a OAuthv2 policy with the following -

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 name="OA2-GenerateAccessToken" continueOnError="false" enabled="true">
    <DisplayName>OA2-GenerateAccessToken</DisplayName>
    <Scope>request.formparam.scope</Scope>
    <Operation>GenerateAccessToken</Operation>
    <ExpiresIn ref="verifyapikey.VAK-IdentifyConsumer.apiproduct.expires_in">1800000</ExpiresIn>
    <!-- 30 minutes. -->
    <RefreshTokenExpiresIn>28800000</RefreshTokenExpiresIn>
    <!-- 8 hours -->
    <SupportedGrantTypes>
        <GrantType>authorization_code</GrantType>
    </SupportedGrantTypes>
    <GrantType>public.accesstoken.grant_type</GrantType>
    <GenerateResponse enabled="true"/>
</OAuthV2>

As you can see above, I'm requesting Scope in the operation by providing

<Scope>request.formparam.scope</Scope>

This is basically Step 2 of the 3-legged OAuth operation where the authorization code is exchanged for an access_token.

The scope is a formparam as you can see -

The curl of this request is below (sensitive details masked)

curl --location --request POST 'https://example.com/auth-test/v1/accesstoken' --header 'Accept: application/json' --header 'Authorization: Basic 123==' --header 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=authorization_code' --data-urlencode 'code=zMnsJMGd' --data-urlencode 'scope=actionShipmentDelete'

However, when Apigee generates the access_token it does not seem to respect the Scope requested in the above operation, instead it has generated the access_token with the master list of all scopes included in the API Product.

I verified this by building a accesstoken/validate endpoint which is a OAuthv2 policy with VerifyAccessToken operation -

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 name="OA2-ValidateFormParamToken" continueOnError="false" enabled="true">
    <DisplayName>Validate Form Param OAuth2 Token</DisplayName>
    <ExternalAuthorization>false</ExternalAuthorization>
    <Operation>VerifyAccessToken</Operation>
    <AccessTokenPrefix>Bearer</AccessTokenPrefix>
    <Scope>actionShipmentCreate</Scope>
    <SupportedGrantTypes/>
    <GenerateResponse enabled="false"/>
    <AccessToken>public.access_token</AccessToken>
    <Tokens/>
</OAuthV2>

As you can see I'm restricting the scope of this policy to

<Scope>actionShipmentCreate</Scope>

and I'm expecting this policy to fail but it passes as the acces_token is minted with master scopes.

How can I have Apigee use the scope that I provide in the GenerateAccessToken operation through a HTTP parameter?

Solved Solved
0 9 418
1 ACCEPTED SOLUTION

I think maybe your expectations are not quite correct.

You are showing a request to redeem a code for a token. This is the 3-legged flow, in which

  • a client app requests /authorization, passing client credentials
  • the token dispensary redirects the client app to a login-and-consent experience
  • the user signs in, approves the scopes
  • the login-and-consent experience generates a code, sends it back to the client app
  • the client app sends the code to the token dispensary to redeem the code for a token

I think you are showing the final step in this process. In that step, the client app says "I have a code here, and I want to exchange it for a token." The client app, at that point, must not specify a set of scopes.

The client app can request a set of scopes (in the call to /authorize), but the actual set of scopes to be associated to the token must be explicitly approved by the user at the time of consent. Apigee associates the set of approved scopes to the code that it generates at that time.

Specifying the scope at the time of code-for-token redemption must not be permitted, because a malicious client application could specify any scopes it wants, though the user approved a different set. That would not be ok.

Does this explanation make sense?

Maybe you have the specific case in which the client is trying to *narrow* the set of scopes on the token, after the user already approved a different set of scopes. In other words, the scopeset the client app passes to Apigee when redeeming the code for a token is a subset of the scopeset that the user approved. That would also be the wrong time to try to set scopes. If the client app wants a small set of scopes, it should request a small set of scopes, and then the login-and-consent experience would prompt the user to approve those.

View solution in original post

9 REPLIES 9

I think maybe your expectations are not quite correct.

You are showing a request to redeem a code for a token. This is the 3-legged flow, in which

  • a client app requests /authorization, passing client credentials
  • the token dispensary redirects the client app to a login-and-consent experience
  • the user signs in, approves the scopes
  • the login-and-consent experience generates a code, sends it back to the client app
  • the client app sends the code to the token dispensary to redeem the code for a token

I think you are showing the final step in this process. In that step, the client app says "I have a code here, and I want to exchange it for a token." The client app, at that point, must not specify a set of scopes.

The client app can request a set of scopes (in the call to /authorize), but the actual set of scopes to be associated to the token must be explicitly approved by the user at the time of consent. Apigee associates the set of approved scopes to the code that it generates at that time.

Specifying the scope at the time of code-for-token redemption must not be permitted, because a malicious client application could specify any scopes it wants, though the user approved a different set. That would not be ok.

Does this explanation make sense?

Maybe you have the specific case in which the client is trying to *narrow* the set of scopes on the token, after the user already approved a different set of scopes. In other words, the scopeset the client app passes to Apigee when redeeming the code for a token is a subset of the scopeset that the user approved. That would also be the wrong time to try to set scopes. If the client app wants a small set of scopes, it should request a small set of scopes, and then the login-and-consent experience would prompt the user to approve those.

ok I understand that.

Are you saying then the following should work?

The scope is being requested when an authorization code is requested

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<OAuthV2 name="OA2-GenerateAuthorizationCode" continueOnError="false" enabled="true">
    <DisplayName>OA2-GenerateAuthorizationToken</DisplayName>
    <!-- This policy generates an OAuth 2.0 access token using the client_credentials grant type -->
    <Operation>GenerateAuthorizationCode</Operation>
    <Scope>request.formparam.scope</Scope>
    <ExpiresIn ref="verifyapikey.VAK-IdentifyConsumer.apiproduct.expires_in">180000000</ExpiresIn>
    <!-- 30 minutes. -->
    <RefreshTokenExpiresIn>28800000</RefreshTokenExpiresIn>
    <GenerateResponse enabled="true"/>
</OAuthV2>

In the above OAuthv2 policy with operation GenerateAuthorizationCode I'm seeking to set the scope coming from a formparam.

And when the scope is built-in to auth code, the same scope will be included in the access_token issued for the auth code?

Yes, you set the scope for the to-be-generated token when Apigee generates the code.

when the scope is built-in to auth code, the same scope will be included in the access_token issued for the auth code?

yes.

Thanks, that worked.

I went down this path yesterday and it didn't work and now realized it was just a rookie mistake.

I sent scope in queryparam and tried to set it from formparam. Because there was no formparam called scope, I guess Apigee was giving the auth code master list of scopes.

@dino-at-google in the following Apigee documentation page,

https://docs.apigee.com/api-platform/security/oauth/access-tokens#requestinganaccesstokenauthorizati...

When requesting an access token using an authorization code, why is it mentioned that scope is allowed as an optional parameter if this is wrong place to set a scope?

yes, I think that is incorrect. Let me raise this issue with the team.

Hi team, We are also facing same issue, the OAuth is not respecting Scope value, it generating token with any scope value.

has this issue still not resolved I think, Please clarify the same. 

Hi

It sounds like you are requesting a token with a specific scope, and you are getting a token with no scope. Taking a guess ,  one possibility for this is that you have no API product configured with the specific scope you are requesting. So, check your API Product configuration.

To review, a token is issued for an APP CREDENTIAL, the app credential is authorized on one or more PRODUCTS , and each product has a set of SCOPES. When you present app credentials and ask for a scoped token, Apigee will generate a token that has as its scopes, the intersection of the requested scopes and the available scopes.  The available scopes is the set of all possible scopes for all of the products authorized for the app credential. This is explained in this document.  As an example, If you have a credential authorized for productA, and productA has READ scope (only) , and you request WRITE scope , then ... you will get a token with no scopes. Conversely If you have a credential authorized for productB, and productB has READ,WRITE,UPDATE scopes, and you request READ,DELETE scopes, then you will get READ scope on your token.  No DELETE scope because that scope is not configured for the API Product.


If my guess is wrong, and you're having a different problem, then you'll need to elaborate more on what you're experiencing. The original poster here, 2 years ago, had a misunderstanding around the setting of scopes on a token. (This misunderstanding may have been partly due to unclear documentation). I explained how things work, and that was that. You are now saying "we are facing the same issue." Well if it's "the same issue" then you are facing a misunderstanding, and the solution to your problem is to read and understand my explanation. You further ask "has this issue still not resolved" which I think is an invalid question. There was no issue with Apigee that needed to be resolved. The problem was a lack of understanding of how scopes worked in 3-legged flows.

In general, if you are having a problem with Apigee, then I suggest that you do all of these things:

  • create a new post. Don't post a "me, too" comment on a 2-year old post that is RESOLVED.
  • explain what you are doing (in detail), what results you are observing (with screenshots or code snips), what you are expecting to see, and an explanation for how these latter two things differ. Eg "I am seeing a token expiry of 4500 and I am expecting a token expiry of 100000". Provide details. Put some effort into it.  If you use a total of 20 words to ask your question, then it's highly likely that you haven't provided enough detail to enable anyone  to help you. 
  • In your particular case, I guess there is a problem with tokens and scopes. Therefore "explain what you are doing" means:
    • provide the XML for the relevant policy configuration(s);
    • describe the grant type, the scopes you want;
    • describe the API Product configuration (in detail with screenshots) describing all of the scopes on the various products;
    • provide the REST API call you are making.  If it's 3-legged flow, then all of the calls;
    • and, if you'd like to maximize your chances of getting help, attach or show a trace session that shows the results you are getting.


@dchiesa1 Thank you for the quick revert.

Let me explain our scenario in detail .. 

What if the client does not attach a scope parameter? In this case, Apigee generates a token that includes all of the scopes recognized by the developer app. It's important to understand that the default behavior is to return an access token that contains the union of all scopes for all of the products included in the developer app ( As per document)

Our requirement is .. token should not generate if we pass wrong scope or NO scope. Is feature available in Apigee?