This page applies to Apigee and Apigee hybrid.
View Apigee Edge documentation.
In this topic, we'll discuss how to import externally generated access tokens, refresh tokens, or auth codes into the Apigee token store. You can use this technique if you would like to configure Apigee to validate tokens that are generated outside of Apigee.
In the usual case, Apigee will generate and store an OAuth token, and return it to the calling application. The calling app then presents that token back to Apigee when requesting service, and Apigee - via the OAuthV2 policy with Operation = VerifyAccessToken - will verify that the token is valid. This topic describes how you can configure Apigee 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 Apigee.
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 Apigee generates. You can then make secure API proxy requests with the substituted token or code, and Apigee will validate them as if they were generated by Apigee.
Some Background
In the usual case, Apigee generates a token by producing a random string of letters and numbers. Apigee 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": "myorg", "refresh_token_expires_in": "0", //--in seconds "refresh_count": "0" }
The access_token value is used by Apigee to retrieve the token metadata.
For example, suppose an API proxy request includes
the bearer token zBC90HhCGmGlaMBWeZAai2s3za5j
. Using the token value, Apigee
retrieves the token metadata to determine if the token is valid or not.
By following the steps described here, you can configure Apigee to store a token whose access_token value was generated by an external service. For example, suppose you have a system external to Apigee that generates tokens of the form "TOKEN-<16 random numbers>" . In that case, the full token metadata stored by Apigee 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": "myorg", "refresh_token_expires_in": "0", //--in seconds "refresh_count": "0" }
In this case, an app could make a request to an API proxy, carrying the bearer
token TOKEN-1092837373654221
, and Apigee 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 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 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 - in other words there is a
Developer App stored within Apigee 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, 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 to do both, or to do one or the other, or neither.
If you want the OAuthV2/GenerateAccessToken policy in Apigee to validate the client
credentials against the Apigee 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 may not validate the client credentials, it is still necessary for the
client_id
to be known and managed by Apigee. Every access_token in Apigee, whether
generated by Apigee or generated by an external system and then imported into Apigee,
must be associated to a client application - indicated by the client_id
. So even in the case
where the OAuthV2/GenerateAccessToken policy in Apigee 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 Apigee
administrative API.
Policy Flow for third-party OAuth on Apigee
To use tokens from third-party OAuth systems in Apigee, the flow for generating access tokens should follow one of the following patterns.
External Validation of Client Credentials
- ServiceCallout to Verify the inbound client credentials, and acquire an external token.
- ExtractVariables or a JavaScript step to extract the externally-generated token from the response.
- 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. - OAuthV2/GenerateAccessToken with the
<ExternalAuthorization>
element set totrue
, 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 tofalse
, 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 totrue
, and set the<ExternalAuthorization>
element totrue
orfalse
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 does not attempt to validate the client credentials. If the variable is not set or the value is not true, then Apigee 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 Apigee 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 Apigee 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 access token given that Apigee finds a
token value in the flow variable external_access_token
.
<OAuthV2 name="OAuth-v20-Store-External-Token"> <DisplayName>OAuth v2.0 1</DisplayName> <Attributes/> <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.