Congrats on the release of the ODA APIs!
Requirements:
- An API consumer (App registered in developer portal) should be issued a token only if : the appId(apiKey) is a registered in APIgee dev portal.
2.And, if the tenantApiCode and appId (apigee secret key) combination is authorized(This state sits in our Cloud can be accessed from an API) to server data it is trying to pull is authorized by the tenant admin.
OK, regarding the first requirement, that is how OAuth tokens work. You didn’t say anything about authorizing individual users at the 3rd party integrators. So I’ll assume that you’re using a client-credentials flow. In that case, the 3rd party integrator will pass the client_id + client_secret (aka consumer_id + consumer_secret) to the /token endpoint at Apigee Edge. Apigee Edge then examines the credentials and if
- the credentials are known
- the creds are not revoked
- the creds are not expired
- the owning app is not marked “revoked”
- the owning developer is not marked “inactive”
..then Apigee Edge will issue a token . This is all done by the magic policy called OAuthV2/GenerateAccessToken .
Regarding the tenantApiCode… what you can do is, prior to requesting the generation of the access token, you can call the authorization API,which queries the tenants authorized per API Key. Then, attach the list of tenants as a custom attribute to the token.
On subsequent requests bearing that token, verification of the token will implicitly retrieve the list of tenants authorized for that token.
You can then enforce a check on the tenant-in-the-request against the list of tenants authorized for the appid. I think JavaScript would be the easiest way to do this.
This is really straightforward, and is a really common approach.
For token issuance, the OAuthV2/GeneratorAccessToken looks like this:
<OAuthV2 name='OAuthV2-GenerateAccessToken-CC'>
<ExpiresIn>1800000</ExpiresIn>
<RefreshTokenExpiresIn>691200000</RefreshTokenExpiresIn>
<SupportedGrantTypes>
<GrantType>client_credentials</GrantType>
</SupportedGrantTypes>
<GrantType>request.formparam.grant_type</GrantType>
<Attributes>
<Attribute name="tenant_list" ref="tenant_list_from_api_service" display="true"/>
</Attributes>
<GenerateResponse enabled='true'/>
</OAuthV2>
This policy assumes that the context variable “tenant_list” contains the list of tenants for this app (client id).
OK that’s how you issue tokens. In subsequent arriving at the API Proxy you will want to verify tokens. Do that with OAuthV2/VerifyAccessToken. That step implicitly retrieves the list of tenants authorized for that token, and stores it into a context variable. Assuming you have the GenerateAccessToken policy configured as above, the variable name is accesstoken.tenant_list. Subsequently, you will want to check the tenant in the request against the tenant list in the token. So, you can use ExtractVariables to extract the tenantid from the request URI. Like this:
<ExtractVariables name="ExtractVariables-1">
<DisplayName>Extract a portion of the url path</DisplayName>
<Source>request</Source>
<URIPath>
<Pattern ignoreCase="true">/oda/apis/get/reports/assets/tenant/{tenantCode}/devicedistribution</Pattern>
</URIPath>
<VariablePrefix>urirequest</VariablePrefix>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</ExtractVariables><br>
Then use a JS policy to compare/check. Assuming the tenant_list is a comma-separated string, the JS might look like this:
var authorizedTenants = context.getVariable('access_token.tenant_list').split(', ');
var tenantInUrl = context.getVariable('urirequest.tenantCode');
var found = (authorizedTenants.indexOf(tenantInUrl) >= 0);
context.setVariable('requested_tenant_is_authorized', found +''); //coerce to String
Then, in the policy flow, you can raise a fault if the tenant is not authorized:
<Step>
<Name>OAuthV2-VerifyAccessToken</Name>
</Step>
<Step>
<Name>ExtractVariables-1</Name>
</Step>
<Step>
<Name>JS-CheckTenant</Name>
</Step>
<Step>
<Condition>requested_tenant_is_authorized != "true"</Condition>
<Name>RaiseFault-NotAuthorized</Name>
</Step>
..other steps follow here...