Verify product scope on external access token

We use Okta to generate the client credential access tokens and then we verify those tokens against the Okta JWKs. But, we would like to use the API products to authorize access to proxies based on the scopes specified in the API Product. Does anyone have an example of what policies i need to use to do this? 

I've looked at - Working with OAuth2 scopes  |  Apigee Edge  |  Apigee Docs and Using Third-Party OAuth Tokens  |  Apigee Edge  |  Apigee Docs. But, neither of those documents explain how the scope validation works with external tokens and API Products.

Solved Solved
0 5 1,472
1 ACCEPTED SOLUTION

we would like to use the API products to authorize access to proxies based on the scopes specified in the API Product.

EXCELLENT. I like this idea.

So you set up an API Product, and you can add some scopes to it. These values are useful when you specify a Scope element within an OAuthV2 policy with Operation = VerifyAccessToken. Like this:

 

 

<OAuthV2 name='OAuth-Verify-Access-Token'>
    <Operation>VerifyAccessToken</Operation>
    <Scope>xyz</Scope>
</OAuthV2>

 

 

This policy configuration will check that the bearer token being presented in the Authorization header, has attached to it a Scope that equals "xyz". That scope on the token must have been set when the OAuth token was issued, by a previous invocation of the OAuthV2 policy with Operation = GenerateAccessToken. And...during generation of the token, the generated token can have only scopes that are included in the set of scopes on the API product. (A t the time of token generation, the presented credential will include a client id, which identifies an App Credential, which is authorized for a set of products.  Therefore the token generation logic can limit the scopes on the token to be a subset of the possible scopes on all the Products for that credential). Does this all make sense?  Perhaps surprisingly, the scopes listed on the API Product do not  affect the token verification logic.  

But the OAuthV2 policy deals with opaque tokens, issued by Apigee. The policy does not deal with JWT issued by third parties, like Okta, which is what you're asking about. How can you accomplish what you want?

I see a couple ways

  1. Build your system so that the app can present the Okta-issued token to Apigee , to exchange it for an opaque OAuth token. The logic in Apigee would verify the JWT (via the VerifyJWT policy), verify the issuer and expiry and so on. Then extract the client id from the JWT, and verify THAT: check that it is known and valid within Apigee. Then, issue an oauth token (OAuthV2 policy with Operation = GenerateAccessToken) with the scopes extracted from the Okta token. You can set the expiry on this opaque oauth token so that it is less-than-or-equal-to the expiry on the JWT if you like. In any case, Now you have an opaque Apigee-issued token with a scope set on it. And that means you can use OAuthV2 / VerifyAccesstoken to check scopes. done.

  2. Roll-your-own. Here, you'd allow the client to present the Okta-issued token to Apigee, and configure Apigee to validate that token, via VerifyJWT. Subsequent to the VerifyJWT policy you should include a VerifyAPIKey policy, which uses the client_id extracted from the Okta-issued JWT, and verifies it. This will get you the coarse "product level" authorization. (In other words, the answer to the question, "is this client id valid for an API product which includes the proxy handling the current request?"). THEN, You also need to include a Condition in your proxy flow to check the scope value extracted from the JWT. Basically something like this:

    <Step>
      <Name>VerifyJWT-1</Name>
    </Step>
    <Step>
      <!-- policy must refer to jwt.VerifyJWT-1.decoded.claim.client_id as the api key -->
      <Name>VerifyAPIKey</Name>
    </Step>
    <Step>
      <Name>RaiseFault-MissingScope</Name>
      <Condition>jwt.VerifyJWT-1.decoded.claim.scope NotEqual "required_scope"</Condition>
    </Step>

In case 1 you use the "automatic" scope check that is possible with OAuthV2/VerifyAccessToken. In case 2 you are explicitly checking the scope in a Condition. In both cases, Scope is just a thing, an attribute of the token. The mapping between scope value, and API operation, is sort of up to you. In case it's not clear what I mean, here's an example. Suppose you have an API product with possible scopes of READ, WRITE, DELETE. And your intent is to require a token with Scope=READ for GET operations, a token with Scope=WRITE for POST operations, and a token with Scope=DELETE for DELETE operations. Apigee does not read your mind. It's your responsibility to codify that association between scope value and operation in the proxy flow, with the correct placement of OauthV2/VerifyAccessToken policies with the right scope values, for each different kind of operation.

Now consider the case in which you want to use Okta-issued scopes and just use VerifyJWT + VerifyAPIKey. Those two policies will verify that the token is good, issued by a trusted party, not expired. And also for a client_id that is valid and known. In other words, a basic coarse-grained authorization check. If you now want to check the scope, in such a way as to require Scope1 for GET requests and require Scope2 for POST requests, it is up to you to encode that logic in the Proxy flow, with the right conditions and steps.

Helpful?

View solution in original post

5 REPLIES 5

we would like to use the API products to authorize access to proxies based on the scopes specified in the API Product.

EXCELLENT. I like this idea.

So you set up an API Product, and you can add some scopes to it. These values are useful when you specify a Scope element within an OAuthV2 policy with Operation = VerifyAccessToken. Like this:

 

 

<OAuthV2 name='OAuth-Verify-Access-Token'>
    <Operation>VerifyAccessToken</Operation>
    <Scope>xyz</Scope>
</OAuthV2>

 

 

This policy configuration will check that the bearer token being presented in the Authorization header, has attached to it a Scope that equals "xyz". That scope on the token must have been set when the OAuth token was issued, by a previous invocation of the OAuthV2 policy with Operation = GenerateAccessToken. And...during generation of the token, the generated token can have only scopes that are included in the set of scopes on the API product. (A t the time of token generation, the presented credential will include a client id, which identifies an App Credential, which is authorized for a set of products.  Therefore the token generation logic can limit the scopes on the token to be a subset of the possible scopes on all the Products for that credential). Does this all make sense?  Perhaps surprisingly, the scopes listed on the API Product do not  affect the token verification logic.  

But the OAuthV2 policy deals with opaque tokens, issued by Apigee. The policy does not deal with JWT issued by third parties, like Okta, which is what you're asking about. How can you accomplish what you want?

I see a couple ways

  1. Build your system so that the app can present the Okta-issued token to Apigee , to exchange it for an opaque OAuth token. The logic in Apigee would verify the JWT (via the VerifyJWT policy), verify the issuer and expiry and so on. Then extract the client id from the JWT, and verify THAT: check that it is known and valid within Apigee. Then, issue an oauth token (OAuthV2 policy with Operation = GenerateAccessToken) with the scopes extracted from the Okta token. You can set the expiry on this opaque oauth token so that it is less-than-or-equal-to the expiry on the JWT if you like. In any case, Now you have an opaque Apigee-issued token with a scope set on it. And that means you can use OAuthV2 / VerifyAccesstoken to check scopes. done.

  2. Roll-your-own. Here, you'd allow the client to present the Okta-issued token to Apigee, and configure Apigee to validate that token, via VerifyJWT. Subsequent to the VerifyJWT policy you should include a VerifyAPIKey policy, which uses the client_id extracted from the Okta-issued JWT, and verifies it. This will get you the coarse "product level" authorization. (In other words, the answer to the question, "is this client id valid for an API product which includes the proxy handling the current request?"). THEN, You also need to include a Condition in your proxy flow to check the scope value extracted from the JWT. Basically something like this:

    <Step>
      <Name>VerifyJWT-1</Name>
    </Step>
    <Step>
      <!-- policy must refer to jwt.VerifyJWT-1.decoded.claim.client_id as the api key -->
      <Name>VerifyAPIKey</Name>
    </Step>
    <Step>
      <Name>RaiseFault-MissingScope</Name>
      <Condition>jwt.VerifyJWT-1.decoded.claim.scope NotEqual "required_scope"</Condition>
    </Step>

In case 1 you use the "automatic" scope check that is possible with OAuthV2/VerifyAccessToken. In case 2 you are explicitly checking the scope in a Condition. In both cases, Scope is just a thing, an attribute of the token. The mapping between scope value, and API operation, is sort of up to you. In case it's not clear what I mean, here's an example. Suppose you have an API product with possible scopes of READ, WRITE, DELETE. And your intent is to require a token with Scope=READ for GET operations, a token with Scope=WRITE for POST operations, and a token with Scope=DELETE for DELETE operations. Apigee does not read your mind. It's your responsibility to codify that association between scope value and operation in the proxy flow, with the correct placement of OauthV2/VerifyAccessToken policies with the right scope values, for each different kind of operation.

Now consider the case in which you want to use Okta-issued scopes and just use VerifyJWT + VerifyAPIKey. Those two policies will verify that the token is good, issued by a trusted party, not expired. And also for a client_id that is valid and known. In other words, a basic coarse-grained authorization check. If you now want to check the scope, in such a way as to require Scope1 for GET requests and require Scope2 for POST requests, it is up to you to encode that logic in the Proxy flow, with the right conditions and steps.

Helpful?

Thanks for the quick and thorough response! So, to give a little more detail, we are just wanting the scopes to authorized at a proxy level, not an endpoint level for right now. So, this should be easier to accomplish. I have tried your case 1 and I couldn't get it to work, and that's why I posted my question. This tells me I'm on the right track and need to check my setup, as I'm probably missing a configuration. I'll look through it again and post my setup if I can't figure out why it won't work. Thanks!

I was able to get it to work. I had a mistake in the scope setting on the GenterateAccessToken OAuth policy. Thanks!

Great to hear!  Glad you're moving forward. Thanks for the followup. 

Just to report some issues, NotEqual should be either changed to NotEquals or != Operators

More details at: