Need to Develop oauth token caching proxy

Hi I am new to Apigee & i have a following requirement to create apigee proxy to call REST API

1. need to get oauth token to get access for REST API call

2. use that oauth in REST API call header to access that API

here oauth token need to be cached in same proxy & if it got expired after time limit need to get the new token.

if you guys already have any tutorial or blog for above requirement please share it or share your own process. 

Thanks in advance!.

Solved Solved
1 4 747
1 ACCEPTED SOLUTION

Welcome to Apigee community.

For your usecase there are  different ways one can achieve but one way you can do is drop below policies in postflow - https://docs.apigee.com/api-platform/fundamentals/what-are-flows

High level:

0.Create a cache by name OAuthAccessToken with what ever oauth token expiry time.

https://docs.apigee.com/api-platform/cache/manage-caches-environment

1.Perform lookup cache first (first time it will be cache miss)  -(https://docs.apigee.com/api-platform/reference/policies/lookup-cache-policy)
>>if cache fails then
a.invoke the actual oauth endpoint using service callout.(https://docs.apigee.com/api-platform/reference/policies/service-callout-policy)
b.extract the Response. (https://docs.apigee.com/api-platform/reference/policies/extract-variables-policy)
c.populate to the cache.(https://docs.apigee.com/api-platform/reference/policies/populate-cache-policy)

>>if cache hit's (meaning lookup is successful) then it will skip rest of the steps (like invoke oauth endpoint, extract & populate cache)

2.Either case you will have the access token  to backend rest api using Assign message in the target flow. -- didn't add it assuming you are aware of it.

Just FYI:
Assuming you are aware of CORS - https://docs.apigee.com/api-platform/develop/adding-cors-support-api-proxy (which is added to the conditional flow)

<PostFlow name="PostFlow">
        <Request>
            <Step>
                <Name>LC-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS"</Condition>
            </Step>
            <Step>
                <Name>SC-OAuthTokenRequest</Name>
                <Condition>request.verb != "OPTIONS" and lookupcache.LC-OAuthToken.cachehit = false</Condition>
            </Step>
            <Step>
                <Name>EV-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS" and tokenResponse.content != null"</Condition>
            </Step>
            <Step>
                <Name>PC-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS" and lookupcache.LC-OAuthToken.cachehit = false</Condition>
            </Step>
        </Request>
  
    </PostFlow>

Individual Policy information:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LookupCache async="false" continueOnError="false" enabled="true" name="LC-OAuthToken">
    <DisplayName>LC-OAuthToken</DisplayName>
    <CacheResource>OAuthAccessToken</CacheResource>
    <AssignTo>tokenResponse.content</AssignTo>
    <Scope>Exclusive</Scope>
    <CacheKey>
        <KeyFragment>access_token</KeyFragment>
    </CacheKey>
</LookupCache>    

Make sure you read below private.* values from encrypted KVM in pre-flow.

Below is the service callout to invoke authorization server token endpoint.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-OAuthTokenRequest">
    <DisplayName>SC-OAuthTokenRequest</DisplayName>
    <Properties/>
    <Request clearPayload="true" variable="OAuthTokenRequest">
        <Set>
            <FormParams>
                <FormParam name="grant_type">{private.grantType}</FormParam>
                <FormParam name="client_id">{private.clientId}</FormParam>
                <FormParam name="client_secret">{private.clientSecret}</FormParam>
            </FormParams>
            <Headers>
                <Header name="Content-Type">application/x-www-form-urlencoded</Header>
            </Headers>
            <Verb>POST</Verb>
        </Set>
        <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    </Request>
    <Response>tokenResponse</Response>
    <HTTPTargetConnection>
        <Properties/>
        <URL>https://{private.tokenUrl}</URL>
    </HTTPTargetConnection>
</ServiceCallout>

Extracting the response from the service callout (check the tokenResponse.content)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-OAuthToken">
    <DisplayName>EV-OAuthToken</DisplayName>
    <Properties/>
    <JSONPayload>
        <Variable name="access_token">
            <JSONPath>$.access_token</JSONPath>
        </Variable>
        <Variable name="token_type">
            <JSONPath>$.token_type</JSONPath>
        </Variable>
        <Variable name="expires_in">
            <JSONPath>$.expires_in</JSONPath>
        </Variable>
    </JSONPayload>
    <Source clearPayload="false">tokenResponse.content</Source>
</ExtractVariables>

Now populate the cache

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PopulateCache async="false" continueOnError="false" enabled="true" name="PC-OAuthToken">
    <DisplayName>PC-OAuthToken</DisplayName>
    <CacheResource>OAuthAccessToken</CacheResource>
    <Source>tokenResponse.content</Source>
    <Scope>Exclusive</Scope>
    <CacheKey>
        <KeyFragment>access_token</KeyFragment>
    </CacheKey>
    <ExpirySettings>
        <TimeoutInSec>890</TimeoutInSec>
        <!--example Token expires in 900 sec you can provide little lower time to be safer side-->
    </ExpirySettings>
</PopulateCache>

Good Luck

View solution in original post

4 REPLIES 4

Welcome to Apigee community.

For your usecase there are  different ways one can achieve but one way you can do is drop below policies in postflow - https://docs.apigee.com/api-platform/fundamentals/what-are-flows

High level:

0.Create a cache by name OAuthAccessToken with what ever oauth token expiry time.

https://docs.apigee.com/api-platform/cache/manage-caches-environment

1.Perform lookup cache first (first time it will be cache miss)  -(https://docs.apigee.com/api-platform/reference/policies/lookup-cache-policy)
>>if cache fails then
a.invoke the actual oauth endpoint using service callout.(https://docs.apigee.com/api-platform/reference/policies/service-callout-policy)
b.extract the Response. (https://docs.apigee.com/api-platform/reference/policies/extract-variables-policy)
c.populate to the cache.(https://docs.apigee.com/api-platform/reference/policies/populate-cache-policy)

>>if cache hit's (meaning lookup is successful) then it will skip rest of the steps (like invoke oauth endpoint, extract & populate cache)

2.Either case you will have the access token  to backend rest api using Assign message in the target flow. -- didn't add it assuming you are aware of it.

Just FYI:
Assuming you are aware of CORS - https://docs.apigee.com/api-platform/develop/adding-cors-support-api-proxy (which is added to the conditional flow)

<PostFlow name="PostFlow">
        <Request>
            <Step>
                <Name>LC-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS"</Condition>
            </Step>
            <Step>
                <Name>SC-OAuthTokenRequest</Name>
                <Condition>request.verb != "OPTIONS" and lookupcache.LC-OAuthToken.cachehit = false</Condition>
            </Step>
            <Step>
                <Name>EV-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS" and tokenResponse.content != null"</Condition>
            </Step>
            <Step>
                <Name>PC-OAuthToken</Name>
                <Condition>request.verb != "OPTIONS" and lookupcache.LC-OAuthToken.cachehit = false</Condition>
            </Step>
        </Request>
  
    </PostFlow>

Individual Policy information:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<LookupCache async="false" continueOnError="false" enabled="true" name="LC-OAuthToken">
    <DisplayName>LC-OAuthToken</DisplayName>
    <CacheResource>OAuthAccessToken</CacheResource>
    <AssignTo>tokenResponse.content</AssignTo>
    <Scope>Exclusive</Scope>
    <CacheKey>
        <KeyFragment>access_token</KeyFragment>
    </CacheKey>
</LookupCache>    

Make sure you read below private.* values from encrypted KVM in pre-flow.

Below is the service callout to invoke authorization server token endpoint.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ServiceCallout async="false" continueOnError="false" enabled="true" name="SC-OAuthTokenRequest">
    <DisplayName>SC-OAuthTokenRequest</DisplayName>
    <Properties/>
    <Request clearPayload="true" variable="OAuthTokenRequest">
        <Set>
            <FormParams>
                <FormParam name="grant_type">{private.grantType}</FormParam>
                <FormParam name="client_id">{private.clientId}</FormParam>
                <FormParam name="client_secret">{private.clientSecret}</FormParam>
            </FormParams>
            <Headers>
                <Header name="Content-Type">application/x-www-form-urlencoded</Header>
            </Headers>
            <Verb>POST</Verb>
        </Set>
        <IgnoreUnresolvedVariables>false</IgnoreUnresolvedVariables>
    </Request>
    <Response>tokenResponse</Response>
    <HTTPTargetConnection>
        <Properties/>
        <URL>https://{private.tokenUrl}</URL>
    </HTTPTargetConnection>
</ServiceCallout>

Extracting the response from the service callout (check the tokenResponse.content)

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ExtractVariables async="false" continueOnError="false" enabled="true" name="EV-OAuthToken">
    <DisplayName>EV-OAuthToken</DisplayName>
    <Properties/>
    <JSONPayload>
        <Variable name="access_token">
            <JSONPath>$.access_token</JSONPath>
        </Variable>
        <Variable name="token_type">
            <JSONPath>$.token_type</JSONPath>
        </Variable>
        <Variable name="expires_in">
            <JSONPath>$.expires_in</JSONPath>
        </Variable>
    </JSONPayload>
    <Source clearPayload="false">tokenResponse.content</Source>
</ExtractVariables>

Now populate the cache

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PopulateCache async="false" continueOnError="false" enabled="true" name="PC-OAuthToken">
    <DisplayName>PC-OAuthToken</DisplayName>
    <CacheResource>OAuthAccessToken</CacheResource>
    <Source>tokenResponse.content</Source>
    <Scope>Exclusive</Scope>
    <CacheKey>
        <KeyFragment>access_token</KeyFragment>
    </CacheKey>
    <ExpirySettings>
        <TimeoutInSec>890</TimeoutInSec>
        <!--example Token expires in 900 sec you can provide little lower time to be safer side-->
    </ExpirySettings>
</PopulateCache>

Good Luck

Thanks @API-Evangelist will try with above 

Is this solution safe ? 
If found in documentation (https://docs.apigee.com/api-platform/cache/manage-caches-environment) that: "Cache is encrypted only in PCI- and HIPAA-enabled organizations"

 

Can KVM be used instead? Values in KVM are encrypted, but before they are used they are kept in cache. Are the KVM values stored in cache already encrypted? In this case KVM also cannot be used.