Apigee OAuth with REST authentication API

Not applicable

This question was previously asked on Stack Overflow. The original question can be found here. The text of the question and it's answer are below:

I already have working services and wanted to use Apigee to add OAuth v2 Authentication to them. Currently my services uses REST authentication. This means access token generates based on basic authorization, and then kept in each request in authorization header.

I want Apigee to authenticate users through my services. This should be possible according to Apigee documentation. But I can't figure out how Apigee should get access token from my service, before or after OAuth? Can it store access token and refresh it in background if needed?

Solved Solved
1 1 6,208
1 ACCEPTED SOLUTION

Not applicable

The simplest method is to generate OAuth tokens using Apigee, and store the backend token or backend credentials (depending upon requirements) in custom token attributes at the Apigee layer. Details on using the SetOAuthV2Info policy to set custom attributes can be found here. Note that the custom token attributes are automatically set in variables when validating a token, so you generally won't have to use the corresponding GetOAuthV2Info policy.

You'll require the backend username and password (or some way of identifying the user in the backend) as part of your request for a token. Then you'll use those credentials to call the backend API to get a backend token. Success in that backend call means that the token should be created at the Apigee layer, and the backend token will be saved in a custom attribute. Failure in that backend call means the user is not authorized, and no token should be created (return 401 Unauthorized).

You mention the need to refresh your backend access token when needed. I assume that you do not have a refresh token mechanism for your backend, and you'll need to resubmit backend credentials when you want to get a new backend token.

You could use your backend to manage the token lifetime. You'd have the backend token stored in a custom token attribute, using that token for backend requests, and then when the backend returns that the token has expired, you would invalidate the token (using the OAuthV2 policy with the InvalidateToken operation) and return the token expired return from your API. The app would likely then need to get a new access token by collecting and providing credentials again.

If you are managing token lifetime at the Apigee layer, then you'll store the backend basic auth credentials (along with the backend token) in custom attributes attached to the token during the same call in which you mint the token. Best practice would be to encrypt them before storing, and then decrypt before usage -- this would require custom code, probably using JavaScript code that uses an encryption library like CryptoJS.

Your normal flow would then be as following:

  1. make a normal request to the target
  2. in the target or proxy PostFlow, check the target response to determine whether the token was authorized
  3. if an unauthorized error was not returned, skip the following steps -- no need to resubmit the request
  4. use the backend credentials stored in the token custom attributes to request a new backend token using a ServiceCallout using new message variables for the request and response like sc_request and sc_response (decrypting the credentials before usage if you stored them encrypted)
  5. take the new backend token and store it in the token custom attributes using SetOAuthV2Info
  6. replace the backend token in the original request message using AssignMessage
  7. resubmit the request using a ServiceCallout

Example code following the target request:

<Step>
  <!-- condition should be whatever matches the backend token expired condition -->
  <Name>decryptBackendCredentialsFromCustomAttributes</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- create message to send to backend token request API -->
  <Name>createTokenRequest</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- request backend token using ServiceCallout -->
  <Name>getBackendToken</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- store backend token using sc_request.something -->
  <Name>updateCustomAttributesFromCallout</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- replace token in original request -->
  <Name>updateRequestWithNewToken</Name>
  <Condition>response.status.code == 401 and sc_response.status.code == 200</Condition>
</Step>
<Step>
  <!-- call backend again using ServiceCallout and original request -->
  <Name>resubmitRequestCallout</Name>
  <Condition>response.status.code == 401 and sc_response.status.code == 200</Condition>
</Step>

AssignMessage example:

<AssignMessage name="updateRequestWithNewToken">
  <AssignTo transport="http" type="request" createNew="false">request</AssignTo>
  <Set>
    <Headers>
      <Header name="BackendTokenLocation">{refreshedToken}</Header>
    </Headers>
  </Set>
</AssignMessage>

View solution in original post

1 REPLY 1

Not applicable

The simplest method is to generate OAuth tokens using Apigee, and store the backend token or backend credentials (depending upon requirements) in custom token attributes at the Apigee layer. Details on using the SetOAuthV2Info policy to set custom attributes can be found here. Note that the custom token attributes are automatically set in variables when validating a token, so you generally won't have to use the corresponding GetOAuthV2Info policy.

You'll require the backend username and password (or some way of identifying the user in the backend) as part of your request for a token. Then you'll use those credentials to call the backend API to get a backend token. Success in that backend call means that the token should be created at the Apigee layer, and the backend token will be saved in a custom attribute. Failure in that backend call means the user is not authorized, and no token should be created (return 401 Unauthorized).

You mention the need to refresh your backend access token when needed. I assume that you do not have a refresh token mechanism for your backend, and you'll need to resubmit backend credentials when you want to get a new backend token.

You could use your backend to manage the token lifetime. You'd have the backend token stored in a custom token attribute, using that token for backend requests, and then when the backend returns that the token has expired, you would invalidate the token (using the OAuthV2 policy with the InvalidateToken operation) and return the token expired return from your API. The app would likely then need to get a new access token by collecting and providing credentials again.

If you are managing token lifetime at the Apigee layer, then you'll store the backend basic auth credentials (along with the backend token) in custom attributes attached to the token during the same call in which you mint the token. Best practice would be to encrypt them before storing, and then decrypt before usage -- this would require custom code, probably using JavaScript code that uses an encryption library like CryptoJS.

Your normal flow would then be as following:

  1. make a normal request to the target
  2. in the target or proxy PostFlow, check the target response to determine whether the token was authorized
  3. if an unauthorized error was not returned, skip the following steps -- no need to resubmit the request
  4. use the backend credentials stored in the token custom attributes to request a new backend token using a ServiceCallout using new message variables for the request and response like sc_request and sc_response (decrypting the credentials before usage if you stored them encrypted)
  5. take the new backend token and store it in the token custom attributes using SetOAuthV2Info
  6. replace the backend token in the original request message using AssignMessage
  7. resubmit the request using a ServiceCallout

Example code following the target request:

<Step>
  <!-- condition should be whatever matches the backend token expired condition -->
  <Name>decryptBackendCredentialsFromCustomAttributes</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- create message to send to backend token request API -->
  <Name>createTokenRequest</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- request backend token using ServiceCallout -->
  <Name>getBackendToken</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- store backend token using sc_request.something -->
  <Name>updateCustomAttributesFromCallout</Name>
  <Condition>response.status.code == 401</Condition>
</Step>
<Step>
  <!-- replace token in original request -->
  <Name>updateRequestWithNewToken</Name>
  <Condition>response.status.code == 401 and sc_response.status.code == 200</Condition>
</Step>
<Step>
  <!-- call backend again using ServiceCallout and original request -->
  <Name>resubmitRequestCallout</Name>
  <Condition>response.status.code == 401 and sc_response.status.code == 200</Condition>
</Step>

AssignMessage example:

<AssignMessage name="updateRequestWithNewToken">
  <AssignTo transport="http" type="request" createNew="false">request</AssignTo>
  <Set>
    <Headers>
      <Header name="BackendTokenLocation">{refreshedToken}</Header>
    </Headers>
  </Set>
</AssignMessage>