Best practices for passing an access token without using a header

What's the best way to pass OAuth V2 access token without using the Authorization header?

Scenario:

A company understands the benefits of OAuth 2 over Basic Authentication. Limitations of their application mean that headers cannot be dynamically set. This was never an issue with Basic Auth, which always had the same credentials. However, with OAuthV2, the Bearer token will change once an hour. The request itself is a JSON POST over HTTPS.

Here are some options I am considering:

Option 1)

Pass the token in the message body (https://tools.ietf.org/html/rfc6750#section-2.2)

The spec states that this should be form encoded. As the API is JSON, the access token could be included in the payload. I have never seen an API do this, and it isn't very RESTful as the access token is not associated with the resource information present in the payload.

Option 2)

Pass the token as a query parameter (https://tools.ietf.org/html/rfc6750#section-2.3)

This is defined in the spec (unlike using a JSON property), and is common amongst public APIs. For example, Facebook uses access tokens in query parameters (https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#checktoken). There are two main issues I see here. (A) It is likely that the query parameter will be stored in access logs which could be viewed in plain text. (B) Additionally, we will need to ensure that this access token is not used for response caching. The access token will be encrypted over HTTPS still (http://stackoverflow.com/questions/2629222/are-querystring-parameters-secure-in-https-http-ssl).

Option 2 seems more common however Option 1 seems more secure. Any thoughts on this?

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

Some background:

Back in 2012, the OAuth specification explicitly said...

"Don't pass bearer tokens in page URLs: Bearer tokens SHOULD NOT be passed in page URLs (for example as query string parameters). Instead, bearer tokens SHOULD be passed in HTTP message headers or message bodies for which confidentiality measures are taken."

(https://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-16#section-4.3)

However in the final document for RFC 6750, the "SHOULD NOT" seems to be softened a bit. The spec seems to recognize the need for exceptions, and there are recommendations for maxiizing security when the Authorization header cannot be used.

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

Proposed Solution:

Based on the answers below, query parameters appear to be the best option. The following mitigating factors can be used:

- Ensure the tokens are very short-lived, so the potential attack window is reduced

- Ensure the tokens are scoped, so the potential attack damage is reduced

- If possible, implement IP Whitelisting using the Access Control policy for additional security when using query params.

- pass Cache-Control:No-Store in the HTTP Headers

Note: In this scenario, caching is not a concern as the request is a POST. In future, caching should be managed through the use of standard HTTP headers.

(http://dev.mobify.com/blog/beginners-guide-to-http-cache-headers/)

Solved Solved
5 5 161K
1 ACCEPTED SOLUTION

@Sean Davis

By saying "header value cannot be dynamically changed", I am assuming that in the past they were hardcoding the header value and it wasn't changing during runtime. If they had to change it, they do another code deployment. Now that access token is dynamically changing every x mins, they are unable to implement it.

Logically, there are 3 places where any input to server can exist (not counting URL): query, header, body.

Header doesn't work for them as their client can't support dynamic headers.

Body would work, it would look ugly but real problem is that it won't work with GET, HEAD.

So query is all you are left with...

Tokens appearing in logs and caching will be your problems. For caching you will need to populate Vary header.

View solution in original post

5 REPLIES 5

I'm generally more inclined toward the query param approach, because sticking auth alongside the rest of the payload makes me feel funny. Why not support the query param as an option if the client has trouble with the header (option 2) and in the Apigee Edge layer rewrite the URL to move the bearer into the Authorization header? Obv still has logs in Edge… which brings me to a curveball—could you pass the bearer token using the basic auth header, base64encoding `bearer:SECRET` where the caps are the token. Then you'd check for username `bearer` and then extract and validate the token passed as the password.

Agreed that using JSON payload just feels wrong. From an implementation perspective, I like the idea of a preflow step that just moves the value from the QP to the header, to improve readability.

@Sean Davis

By saying "header value cannot be dynamically changed", I am assuming that in the past they were hardcoding the header value and it wasn't changing during runtime. If they had to change it, they do another code deployment. Now that access token is dynamically changing every x mins, they are unable to implement it.

Logically, there are 3 places where any input to server can exist (not counting URL): query, header, body.

Header doesn't work for them as their client can't support dynamic headers.

Body would work, it would look ugly but real problem is that it won't work with GET, HEAD.

So query is all you are left with...

Tokens appearing in logs and caching will be your problems. For caching you will need to populate Vary header.

That assumption is correct. There is currently no caching (as request is a POST...), however it is an important future consideration.

I have added my proposed solution based on this above.