Was this helpful?

This topic demonstrates how to setup authorization for your APIs using the OAuth 2.0 authorization code grant type for the Simple Proxy in the API Platform Samples available on Github.

In this topic, you configure an OAuth proxy that builds on the policies configured in Secure APIs with OAuth 2.0: client credentials, but instead of the client credential grant type, it supports OAuth 2.0 'authorization code' grant type.

This topic also builds on the API product, developer, and app that you set up in Provision API products, developers and apps

Apps that use the "authorization code" grant type obtain access tokens indirectly. These apps redirect the end user's browser to a pre-defined URL that produces an authorization code, which then redirects the app end user's browser to a login Web application for authentication. This grant type is designed for Web apps that communicate directly with the provider API.

For more background on OAuth, please refer to the OAuth 2.0 Specification, or to Apigee's best practices for securing APIs with OAuth.

Creating OAuth policies

ValidateOAuth Policy

This policy verifies that the request contains a valid OAuth 2.0 bearer token, checks the API product, and authorizes the app.

Create this policy by creating a file called ValidateOAuth.xml under apiproxy/policies with the following content:

<OAuthV2 name="ValidateOAuth">
  <Operation>VerifyAccessToken</Operation>
</OAuthV2>

CheckQuota Policy

This policy maintains a separate quota per app, based on the client id (which is the same value as the app's consumer key), and sets the parameters based on the API product.

Create this policy by creating a file called CheckQuota.xml under apiproxy/policies with the following content:

<Quota name="CheckQuota"> 
  <Interval ref="apiproduct.developer.quota.interval"/>
  <TimeUnit ref="apiproduct.developer.quota.timeunit"/>
  <Allow countRef="apiproduct.developer.quota.limit"/>
  <Identifier ref="client_id"/>
</Quota>

GenerateAuthCode Policy

Compared with Secure APIs with OAuth 2.0: client credentials, you need to add one additional policy : GenerateAuthCode.

This policy configures the authorization endpoint.

Create this policy by creating a file called GenerateAuthCode.xml under apiproxy/policies with the following content:

<OAuthV2 name="GenerateAuthCode">
  <Operation>GenerateAuthorizationCode</Operation>
  <!-- The authorization code expires in 10 minutes -->
  <ExpiresIn>600000</ExpiresIn>
  <GenerateResponse/>
</OAuthV2>

GenerateAccessToken Policy

This is the same policy that is configured for client credentials in Secure APIs with OAuth 2.0: client credentials, only in this case the policy is configured to support the authorization code grant type.

This policy configures the token endpoint.

Create this policy by as a file called GenerateAccessToken.xml under apiproxy/policies with the following content:

<OAuthV2 name="GenerateAccessToken">
  <GenerateResponse/>
  <!-- <Operation>GenerateAccessToken</Operation> -->
  <!-- This is in millseconds, so expire in an hour -->
  <ExpiresIn>3600000</ExpiresIn>
  <SupportedGrantTypes>
    <GrantType>authorization_code</GrantType>
  </SupportedGrantTypes>
<GrantType>request.queryparam.grant_type</GrantType>
<Code>request.queryparam.code</Code>
<ClientId>request.queryparam.client_id</ClientId>
<RedirectUri>request.queryparam.redirect_uri</RedirectUri>
<Scope>request.queryparam.scope</Scope>
</OAuthV2>

Note the main differences: SupportedGrantTypes now specifies authorization_code. It is possible to list multiple grant types here. You must configure expected locations for each parameter that the client app includes in the request. For auth code, you must specify the location of the auth code, the client ID, the redirect URI, and the scope. For flexibility, the GenerateAccessToken policy supports HTTP headers, query parameters, and form parameters as locations for these parameters.

The example below demonstrates the same parameters as HTTP headers:

<GrantType>request.header.grant_type</GrantType>
<Code>request.header.code</Code>
<ClientId>request.header.client_id</ClientId>
<RedirectUri>request.header.redirect_uri</RedirectUri>
<Scope>request.header.scope</Scope>

Or mix and match:

<GrantType>request.header.grant_type</GrantType>
<Code>request.header.code</Code>
<ClientId>request.queryparam.client_id</ClientId>
<RedirectUri>request.queryparam.redirect_uri</RedirectUri>
<Scope>request.queryparam.scope</Scope>

Only one location can be configured per parameter.

Attaching OAuth policies to ProxyEndpoint

3-legged OAuth requires three endpoints. For the client to initiate the three-legged protocol, you need to define the following endpoints and publish the following URIs:

  • A URI to access your API:
    http://{org_name}-test.apigee.net/weather
  • A URI to obtain an authorization code:
    http://{org_name}-test.apigee.net/weather/authorize
  • A URI to obtain an access token:
    http://{org_name}-test.apigee.net/weather/access_token

(Note that for security in production, you must use HTTPS for these URLs. In this case, for simplicity, we use HTTP.) These endpoint URIs are published out-of-band. That is, app developers must be informed of these URIs so that they know how to obtain authorization codes and access tokens before they can access an API.

On the API Platform, these endpoints are created using a ProxyEndpoint configuration that defines three conditional flows..

In this example, the respective conditional flows are named GetAuthCode, GetAccessToken, and default.

A different URI must be assigned to each flow:

  • GetAuthCode: /oauth/authorize
  • GetAccessToken: /oauth/accesstoken
  • default: Any URI other than the two defined above

With this configuration, to obtain an auth code an app developer calls, for example, http://myorg-test.apigee.net/weather/oauth/authorize.

After obtaining the authorization code, the app uses the code to obtain an access token from the token endpoint at  http://myorg-test.apigee.net/weather/oauth/accesstoken.

Finally, the app calls http://myorg-test.apigee.net/weather using the access token to retrieve a weather forecast.

The flow named default is configured with policies that:

  1. Check the URI path suffix (that is, everything after /weather. 
  2. Validate the access token associated with the request
  3. Retrieve and enforce the quota settings associated with the API product

Note that this flow structure makes use of conditional flows. The processing pipeline evaluates the conditional statements for each request received. The conditions are evaluated from top to bottom. If the first two conditions do not evaluate to true, then the 'default' flow is executed for the request, since the default flow has no condition and therefore always executes if a message does not meet the first two conditions.

The following ProxyEndpoint ( apiproxy/proxies/default.xml) implements this flow structure:

<Flows>
  <Flow name="GetAuthCode">
    <!-- This policy flow is selected when the URI path suffix matches /oauth/authorize -->
    <Condition>proxy.pathsuffix == "/oauth/authorize"</Condition>
    <Request>
	<Step><Name>GenerateAuthCode</Name></Step>
    </Request>
  </Flow>
  <Flow name="GetAccessToken">
    <!-- This policy flow is selected when the URI path suffix matches /oauth/accesstoken -->
    <Condition>proxy.pathsuffix == "/oauth/accesstoken"</Condition>
    <Request>
	<Step><Name>GenerateAccessToken</Name></Step>
    </Request>
  </Flow>
  <Flow name="default">
    <!-- This policy flow is selected for all other API calls -->
    <Request>
	<Step><Name>ValidateOAuth</Name></Step>
	<Step><Name>CheckQuota</Name></Step>
    </Request>
  </Flow>
</Flows>

Importing and deploy the API proxy

The deploy tool can be used to import the API proxy. Substitute username and password for myname:mypass, and your Apigee organization for myorg.

$ python tools/deploy.py -n weatherapi -u myname:mypass -o {org_name} -e test -p / -d simpleProxy
Writing simpleProxy/.DS_Store to ./.DS_Store
Writing simpleProxy/apiproxy/.DS_Store to apiproxy/.DS_Store
Writing simpleProxy/apiproxy/weatherapi.xml to apiproxy/weatherapi.xml
Writing simpleProxy/apiproxy/policies/CheckQuota.xml to apiproxy/policies/CheckQuota.xml
Writing simpleProxy/apiproxy/policies/ClassifyRequest.xml to apiproxy/policies/ClassifyRequest.xml
Writing simpleProxy/apiproxy/policies/GenerateAccessToken.xml to apiproxy/policies/GenerateAccessToken.xml
Writing simpleProxy/apiproxy/policies/GenerateAuthCode.xml to apiproxy/policies/GenerateAuthCode.xml
Writing simpleProxy/apiproxy/policies/ValidateOAuth.xml to apiproxy/policies/ValidateOAuth.xml
Writing simpleProxy/apiproxy/proxies/default.xml to apiproxy/proxies/default.xml
Writing simpleProxy/apiproxy/targets/default.xml to apiproxy/targets/default.xml
Imported new proxy version 9
Environment: test
  Revision: 9 BasePath = /
  State: deployed

Retrieving an access token and making an authorized API request

To authenticate the user and get the access token, the user's web browser is redirected to the login page (the callback_url for the app) supported by the API.

In the request below:

  • client_id must be the same consumerKey for the requesting app.
  • redirect_urimust be the same as the callbackUrl for requesting app (in this example login.weatherapp.com).
$ curl  "http://myorg-test.apigee.net/weather/oauth/authorize?response_type=code&client_id={consumer_key}&redirect_uri=login.weatherapp.com&scope=READ&state=foobar"

This command returns a redirect response:

HTTP/1.1 302 Found Location:login.weatherapp.com?scope=READ&state=foobar&code={auth_code}

OAuth client apps must support HTTP 302 redirect responses. In an actual OAuth app, the URL login.weatherapp.com?scope=READ&state=foobar&code={auth_code} would be invoked by a Web browser. The Web browser would redirect to the login page at login.weatherapp.com.

Developing this login app is part of the API provider's responsibility.

Note the state parameter. This parameter is designed to prevent cross-site request forgery attacks and should be unique for every request. While it is not required, it is strongly recommended.

To retrieve the access token, the login page or pages at login.weatherapp.com makes a POST request to the API proxy.

To construct the POST request, the login app must Base64-encode the consumer key and secret for the app, and then set that as the value of the HTTP Basic authorization header, as in this example:

$ curl -H "Authorization: Basic WExvdEwzUFJ4TmtVR1hoR0FGRFBPcjZmcXR2QWh1WmU6aU5VeUVhT09oOTZLUjNZTA==" "http://{org_name}-test.apigee.net/weather/oauth/accesstoken?grant_type=authorization_code&code=neIu0d8e&redirect_uri=login.weatherapp.com&scope=READ"

The API Platform responds with an access token:

{ "issued_at":1345073930832, "scope":"READ", "application_name":"weatherapp", "status":"approved", "organization_id":0, "expires_in":3599, "api_profile_name":"null", "refresh_token":"SlE9lNYf", "access_token":"I29OwHlGRi1AkbBCIRLLMC5efFgu", "refresh_count":0 }

The access token returned by the API Platform is now used by the client app to make a request to the protected resource (the target API):

$ curl -H "Authorization: Bearer I29OwHlGRi1AkbBCIRLLMC5efFgu" https://{org_name}-test.apigee.net/weather/forecastrss?w=12797282

Post questions to the Apigee Developer Forum.

Back to API Platform Developer Guide.