Using Third-Party OAuth Tokens

You're viewing Apigee Edge documentation.
Go to the Apigee X documentation.
info

In this topic, we'll discuss how to import externally generated access tokens, refresh tokens, or auth codes into the Edge token store. You can use this technique if you would like to configure Apigee Edge to validate tokens that are generated outside of Apigee Edge.

In the usual case, Apigee Edge will generate and store an OAuth token, and return it to the calling application. The calling app then presents that token back to Apigee Edge when requesting service, and Apigee Edge - via the OAuthV2 policy with Operation = VerifyAccessToken - will verify that the token is valid. This topic describes how you can configure Apigee Edge to store an OAuth token that was generated elsewhere, while keeping the token verification part the same, just as if the token was generated by Edge.

Example

If you want to see a working example that illustrates the technique described in this topic, take a look at the Apigee Delegated Token Management sample.

What is this?

Suppose you have an existing authorization system in place, and you would like to use the token or code values generated by that system in place of the OAuth2 token or code values that Edge generates. You can then make secure API proxy requests with the substituted token or code, and Edge will validate them as if they were generated by Edge.

Some Background

In the usual case, Apigee Edge generates a token by producing a random string of letters and numbers. Apigee Edge associates to that token, other data such as the time the token was issued, the expiry, the list of API Products for which the token is valid, and the scope. All of this information can be returned in a response automatically generated by the OAuthV2 policy configured with Operation = GenerateAccessToken. The response looks like this:

{
  "issued_at": "1469735625687",
  "application_name": "06947a86-919e-4ca3-ac72-036723b18231",
  "scope": "urn://example.com/read",
  "status": "approved",
  "api_product_list": "[implicit-test]",
  "api_product_list_json": ["implicit-test"],
  "expires_in": "1799", //--in seconds
  "developer.email": "joe@weathersample.com",
  "token_type": "BearerToken",
  "client_id": "U9AC66e9YFyI1yqaXgUF8H6b9wUN1TLk",
  "access_token": "zBC90HhCGmGlaMBWeZAai2s3za5j",
  "organization_name": "wwitman",
  "refresh_token_expires_in": "0", //--in seconds
  "refresh_count": "0"
}

The value of the access_token attribute is effectively the lookup key for the response data. An app could make a request to an API proxy hosted in Edge, carrying the bearer token zBC90HhCGmGlaMBWeZAai2s3za5j, and Edge - with the OAuthV2 policy with Operation = VerifyAccessToken - will look up the token, retrieve all the information, and use that information to determine if the token is valid or not, for the requested API Proxy. This is called Token validation. All of the above information comprises the token. The access_token value is just the way to look up that information.

On the other hand, by following the steps described here, you can configure Edge to store a token so that its access_token value is something generated by an external service. All of the other metadata might be the same. For example, suppose you have a system external to Apigee Edge that generates tokens of the form "TOKEN-<16 random numbers>" . In that case, the full token metadata stored by Apigee Edge might be:

{
  "issued_at": "1469735625687",
  "application_name": "06947a86-919e-4ca3-ac72-036723b18231",
  "scope": "urn://example.com/read",
  "status": "approved",
  "api_product_list": "[implicit-test]",
  "api_product_list_json": ["implicit-test"],
  "expires_in": "1799", //--in seconds
  "developer.email": "joe@weathersample.com",
  "token_type": "BearerToken",
  "client_id": "U9AC66e9YFyI1yqaXgUF8H6b9wUN1TLk",
  "access_token": "TOKEN-1092837373654221",
  "organization_name": "wwitman",
  "refresh_token_expires_in": "0", //--in seconds
  "refresh_count": "0"
}

In this case, an app could make a request to an API proxy hosted in Edge, carrying the bearer token TOKEN-1092837373654221, and Edge - via the OAuthV2 policy with Operation = VerifyAccessToken - will be able to validate it. You can apply a similar import pattern to authorization codes and refresh tokens.

Let's Talk about Validating Client Credentials

One pre-requisite to generating a token is validating the requesting client. By default, the OAuthV2/GenerateAccessToken policy in Apigee Edge implicitly verifies the client credentials. Normally in a request for an OAuthV2 token, the client_id and client_secret are passed in the Authorization header, encoded via HTTP Basic Authorization (colon-concatenated, then base64-encoded). The OAuthV2/GenerateAccessToken policy in Apigee Edge decodes that header and looks up the client_id, and verifies that the passed-in client_secret is valid for that client_id. This works if the credentials are known to Apigee Edge - in other words there is a Developer App stored within Apigee Edge which contains a credential, which itself contains the given client_id and client_secret.

In the case that the client credentials are not to be validated by Apigee Edge, you must design your API Proxy, before it generates a token, to explicitly validate the client via some other means. Often this is via a ServiceCallout policy that connects to a remote endpoint in your network.

One way or the other, either implicitly or explicitly, you need to ensure that the API Proxy that generates tokens, first validates the client credentials. Keep in mind that validating the client is independent of generating the access token. You can configure Apigee Edge to do both, or to do one or the other, or neither.

If you want the OAuthV2/GenerateAccessToken policy in Apigee Edge to validate the client credentials against the Edge store, set the <ExternalAuthorization> element to false inside the policy configuration, or omit it entirely. If you want to use an external authorization service to explicitly validate the client credentials, set <ExternalAuthorization> to true.

Though Apigee Edge may not validate the client credentials, it is still necessary for the client_id to be known and managed by Apigee Edge. Every access_token in Apigee Edge, whether generated by Apigee Edge or generated by an external system and then imported into Apigee Edge, must be associated to a client application - indicated by the client_id. So even in the case where the OAuthV2/GenerateAccessToken policy in Apigee Edge will not validate that the client_id and client_secret match, the policy will validate that the client_id is valid, present, and not revoked. So as a pre-requisite setup step, you may have to import client_id's via the Edge administrative API.

Policy Flow for third-party OAuth on Apigee

To use tokens from third-party OAuth systems in Apigee Edge, the flow for generating access tokens should follow one of the following patterns.

External Validation of Client Credentials

  1. ServiceCallout to Verify the inbound client credentials, and acquire an external token.
  2. ExtractVariables or a JavaScript step to extract the externally-generated token from the response.
  3. AssignMessage to set the special well-known-variable called oauth_external_authorization_status. The value must be true to indicate the client credentials are valid.
  4. OAuthV2/GenerateAccessToken with the <ExternalAuthorization> element set to true, and at least one of <ExternalAccessToken>, <ExternalRefreshToken>, or <ExternalAuthorizationCode>.

Internal Validation of Client Credentials

  • ServiceCallout to acquire an external token.
  • ExtractVariables or a JavaScript step to extract the externally-generated token from the response.
  • OAuthV2/GenerateAccessToken with the <ExternalAuthorization> element set to false, and at least one of <ExternalAccessToken>, <ExternalRefreshToken>, or <ExternalAuthorizationCode>.

Notes on the flow and policy configuration

  • In the case that you wish to use an external system to validate client credentials, it is up to you to develop a policy flow that does what is necessary. Normally you would use a ServiceCallout policy to send the externally recognized credentials to the external authentication service. The external authentication service would typically return a response and, if the credentials are valid, also an access token.

  • After the ServiceCallout, the API proxy needs to parse the response to extract the validity status, as well as the externally generated access_token and possibly the refresh_token.

  • In the OAuthV2/GenerateAccessToken policy, set the <StoreToken> element to true, and set the <ExternalAuthorization> element to true or false as appropriate.

    When the OAuthV2/GenerateAccessToken policy executes, it reads the variable oauth_external_authorization_status. If the variable is set and the value is true, then Apigee Edge does not attempt to validate the client credentials. If the variable is not set or the value is not true, then Apigee Edge will attempt to validate client credentials.

  • There are three elements for the OAuthV2 policy that allow you to specify the external data to import: <ExternalAccessToken>, <ExternalRefreshToken>, and <ExternalAuthorizationCode>. Each of these elements accepts a flow variable. The Edge policy will read that variable to find the externally-generated access token, refresh token, or authorization code. It's up to you to implement policies and logic to place the external tokens or codes in the appropriate variables.

    For example, the following configuration in the OAuthV2 policy tells Edge to look for the token in a context variable named external_token.

    <ExternalAccessToken>external_token</ExternalAccessToken>

    You would need to also have a previous step that sets that variable.

  • Regarding setting the oauth_external_authorization_status variable, a common technique for setting this variable is to use an AssignMessage policy with the AssignVariable element, like this:

    <AssignMessage name="AssignMessage-SetVariable">
        <DisplayName>Assign Message - Set Variable</DisplayName>
        <AssignVariable>
            <Name>oauth_external_authorization_status</Name>
            <Value>true</Value>
        </AssignVariable>
        <IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
    </AssignMessage>

    Remember, this policy must fall before the OAuthV2 policy with Operation = GenerateAccessToken.

Example OAuthV2 policy

The following OAuthV2 policy generates an Apigee Edge access token given that Edge finds a token value in the flow variable external_access_token.

<OAuthV2 name="OAuth-v20-Store-External-Token">
    <ExternalAccessToken>external_access_token</ExternalAccessToken>
    <ExternalAuthorization>true</ExternalAuthorization>
    <Operation>GenerateAccessToken</Operation>
    <GenerateResponse enabled="true">
        <Format>FORM_PARAM</Format>
    </GenerateResponse>
    <ReuseRefreshToken>false</ReuseRefreshToken>
    <StoreToken>true</StoreToken>
    <SupportedGrantTypes>
        <GrantType>client_credentials</GrantType>
    </SupportedGrantTypes>
    <ExpiresIn ref='flow.variable'>2400000</ExpiresIn>
</OAuthV2>

In theory, you could apply this pattern with any third-party OAuth2 authorization service.