Builtin OAuth1.0 Functionality

Not applicable

We are working on OAuth1.0 generating/verification with apigee. It seemed that the OAuth1.0 policy that generates the signature does not generate a timestamp nor a nonce (values we need in order to communicate with another service). We were able to bypass this easily, as we had a jscript ready, to generate the OAuth values we needed. We have moved on to trying to have apigee verify an incoming OAuth request. The apigee policy that verifies OAuth1.0 mentions that it enforces/validates only the consumer_key, access_token, and signature. We will need to also validate a timestamp and nonce. Therefore, we assume we will once again have to create a custom script, yet we will have to write it from scratch as we have none already available. Considering this will be time consuming, we wanted to know if we were understanding apigee's capabilities correctly. Does apigee maybe generate the nonce and timestamp hidden in the background (not exposed?), it says nothing of this in the documentation.

Thank you in advance

1 6 507
6 REPLIES 6

Not applicable

Hi @aleks1. These values are usually generated by the client, and sent in with the signed request. It is then the server's responsibility (in the case of using Apigee Edge as the gateway, it is the responsibility of the api proxy within Apigee Edge) to verify the nonce has not been reused, and the timestamp is within skew. The Apigee Edge OAuth 1.0a policy verifies the signature.

I created OAuth1.0a tutorial a while ago on how to test these policies step by step. You can also download the code and test it on your own org.

==========================

Apigee OAuth 1.0a API Proxy Example

This API bundle provides examples of OAuth 1.0a flows in Apigee. Additional examples of OAuth 1.0a are available fromofficial Apigee sample proxies.

For API deployment, see Deploying API bundle with Grunt.js section below.

This diagram illustrates the steps in a typical OAuth 1.0a 2 or 3-legged flows. The aim of the commands below is to implement each one of these steps: alt text

Prerequisites

Setup an API Product with API Proxy

alt text

Associate API Product to Developer App

alt text

How to test this API proxy?

Step 1: Generate request token

Generate the signature by leveraging an online tool such as OAuth Google Code. Replace each of the fields as the image below:

alt text

Note Authorization Header in cURL command is replaced by the value generated from OAuth Google Code above.

curl https://testmyapi-test.apigee.net/oauth10a/request-token -H 'Authorization: OAuth realm="",oauth_callback="oob",oauth_version="1.0",oauth_consumer_key="Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf",oauth_timestamp="1419958125",oauth_nonce="lK9pirgOBrz",oauth_signature_method="HMAC-SHA1",oauth_signature="my4Xw73uqCZ4TAsIlFdZniXjCRw%3D"'
Response
<response><oauth_parameter name="issued_at"><![CDATA[1419958149296]]></oauth_parameter><oauth_parameter name="expires_at"><![CDATA[1419959949296]]></oauth_parameter><oauth_parameter name="application_name"><![CDATA[680bdfea-7dd6-466e-ac5a-09460726ab76]]></oauth_parameter><oauth_parameter name="scope"><![CDATA[null]]></oauth_parameter><oauth_parameter name="status"><![CDATA[approved]]></oauth_parameter><oauth_parameter name="oauth_callback_confirmed"><![CDATA[true]]></oauth_parameter><oauth_parameter name="organization_id"><![CDATA[0]]></oauth_parameter><oauth_parameter name="oauth_token"><![CDATA[hEeiY1GdJ8eSfGqqLcZOdAqr3Ud7]]></oauth_parameter><oauth_parameter name="oauth_token_secret"><![CDATA[bGzq6oGpAP8TZMzOtQz3nv2cNcmW]]></oauth_parameter></response>

Step 2: Generate verifier code

curl -i https://testmyapi-test.apigee.net/oauth10a/verifier -H 'requesttoken:hEeiY1GdJ8eSfGqqLcZOdAqr3Ud7' -H 'appuserid:Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf' -H 'verifiercode:RANDOM_VALUE_abc123456'

Note requesttoken from previous step is used as the input to generate a verifier code. This verifier code is similar to the authorization code in a 3-legged OAuth 2.0 flow, which is exchanged later for an access token. Checkout Apigee OAuth 1.0a official documentation for more information about its use case. Also note verifiercode for the sake of convenience has been geenrated from the client side, however in a production environment, it is recommended to handle it by the Identity Provider from the server side.

Response
HTTP/1.1 200 OK
content-type: text/xml
Content-Length: 727
Connection: keep-alive

<response><oauth_parameter name="issued_at"><![CDATA[1419958892788]]></oauth_parameter><oauth_parameter name="expires_at"><![CDATA[1419960692788]]></oauth_parameter><oauth_parameter name="application_name"><![CDATA[680bdfea-7dd6-466e-ac5a-09460726ab76]]></oauth_parameter><oauth_parameter name="app_user_id"><![CDATA[Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf]]></oauth_parameter><oauth_parameter name="organization_id"><![CDATA[0]]></oauth_parameter><oauth_parameter name="oauth_token"><![CDATA[hEeiY1GdJ8eSfGqqLcZOdAqr3Ud7]]></oauth_parameter><oauth_parameter name="oauth_verifier"><![CDATA[RANDOM_VALUE_abc123456]]></oauth_parameter><oauth_parameter name="verifier_code"><![CDATA[RANDOM_VALUE_abc123456]]></oauth_parameter></response>

Step 3: Generate access token

curl -i https://testmyapi-test.apigee.net/oauth10a/access-token -H 'Authorization: OAuth realm="",oauth_verifier="RANDOM_VALUE_abc123456",oauth_version="1.0",oauth_consumer_key="Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf",oauth_token="hEeiY1GdJ8eSfGqqLcZOdAqr3Ud7",oauth_timestamp="1419959776",oauth_nonce="b4QTmy5VHmT",oauth_signature_method="HMAC-SHA1",oauth_signature="9%2FArMBOTBfZMnzkuJlDSCcEC7%2FQ%3D"'

alt textNote oauth_verifier from previous step is included as parameter and also note how token secret from step #1 is also required.

Response
<response><oauth_parameter name="developer.email"><![CDATA[joe@weathersample.com]]></oauth_parameter><oauth_parameter name="issued_at"><![CDATA[1419959797438]]></oauth_parameter><oauth_parameter name="expires_at"><![CDATA[1419961597438]]></oauth_parameter><oauth_parameter name="application_name"><![CDATA[680bdfea-7dd6-466e-ac5a-09460726ab76]]></oauth_parameter><oauth_parameter name="scope"><![CDATA[null]]></oauth_parameter><oauth_parameter name="status"><![CDATA[approved]]></oauth_parameter><oauth_parameter name="organization_id"><![CDATA[0]]></oauth_parameter><oauth_parameter name="oauth_token"><![CDATA[JhfwmIv6vCAnMKVhutaxstkoQNFx]]></oauth_parameter><oauth_parameter name="enduser_id"><![CDATA[Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf]]></oauth_parameter><oauth_parameter name="oauth_token_secret"><![CDATA[7lDfNC5qNLrKrjUZSQroYtPetdmO]]></oauth_parameter><oauth_parameter name="verifier_code"><![CDATA[RANDOM_VALUE_abc123456]]></oauth_parameter></response>

Step 4: Make an authorized call to the API

curl -i https://testmyapi-test.apigee.net/oauth10a/get -H 'Authorization: OAuth realm="",oauth_callback="oob",oauth_version="1.0",oauth_consumer_key="Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf",oauth_token="JhfwmIv6vCAnMKVhutaxstkoQNFx",oauth_timestamp="1419961751",oauth_nonce="JnuQcUqEMTF",oauth_signature_method="HMAC-SHA1",oauth_signature="lWZr%2FNsRGzSyaQw8XMdZIlGXP8c%3D"'

alt text Note token and token secret values are the input from step #3.

Get response from HTTPBIN (default target)
HTTP/1.1 200 OK
Server: gunicorn/18.0
Date: Tue, 30 Dec 2014 17:58:47 GMT
Content-Type: application/json
Content-Length: 694
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Via: 1.1 vegur
Connection: keep-alive

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Authorization": "OAuth realm=\"\",oauth_callback=\"oob\",oauth_version=\"1.0\",oauth_consumer_key=\"Csgk9z6lfHk98Jjaa85UgwsZTVfZqCsf\",oauth_token=\"gqgOwI365thEII8asdH3M0Kp9gP3\",oauth_timestamp=\"1419962312\",oauth_nonce=\"uOShElpCGTR\",oauth_signature_method=\"HMAC-SHA1\",oauth_signature=\"qJwOM8A3pbFxDbxwBmy%2F5COWgVY%3D\"",
    "Connect-Time": "1",
    "Connection": "close",
    "Host": "httpbin.org",
    "Total-Route-Time": "0",
    "User-Agent": "curl/7.37.1",
    "Via": "1.1 vegur",
    "X-Request-Id": "629edece-6dee-45f7-b9b6-44579bc84789"
  },
  "origin": "107.23.127.104",
  "url": "http://httpbin.org/get"
}

Deploying API bundle with Grunt.js

This API Bundle can be deployed by leveraging Apigee Deploy Grunt.js Plugin or via any tool that can import Apigee proxy bundles.

And @Diego Zuluaga , just to be clear, it is the responsibility of the API proxy to verify that all required fields are present in the signature, correct? So that if the API is designed to require a nonce, then it is outside the scope of responsibility of the OAuth1.0a policy to verify the presence and validity of the nonce value. The OAuth1.0a policy merely verifies that the signature is correct. Right?

To verify a nonce, an API designer would place cachelookup and cachepopulate policies, directly after the OAuth1.0a VerifyAccessToken policy. Correct? To verify a timestamp, the api proxy would need to do something similar, but comparing the time as passed in against the actual time on the server. Correct?

@Dino

That's correct. Frankly, I haven't tested replay attacks to verify that the policy validates that nonce is a replay attack or not. But, the sample proxy above is ready for deployment, so hopefully, it helps to reduce the time to get up to speed. I think the implementation you suggest is also correct for the nonce and the timestamp.

Thanks for complementing my answer.

@Diego Zuluaga Thank you for the help. I have one question, after generating the access token, we were able to successfully have communication both ways with OAuth1.0. At some time later, we got an error stating that the access token has expired. We can generate a new access token, and it works. Is it possible though, to make the access token not expire? It seems a waste to have to constantly make calls to regenerate an access token.

Former Community Member
Not applicable

@aleks1 use the ExpiresIn attribute in the OAuth v1.0a policy to set the expiry of the access token to as long as you want it to be.

<ExpiresIn>1000</ExpiresIn>

adas
New Member

@aleks1 Set the expiry to -1 if you do not want tokens to expire.

Add the following in your OAuthV1 policy:

<ExpiresIn>-1</ExpiresIn>