OAuth (obsolete info)/Issues

This page serves as an overview of issues in both the OAuth 1 and OAuth 2 specs.


 * https://tools.ietf.org/html/rfc5849 - OAuth 1
 * https://tools.ietf.org/html/draft-ietf-oauth-v2-30 - OAuth 2
 * https://tools.ietf.org/html/draft-ietf-oauth-v2-bearer-22 - OAuth 2 Bearer tokens
 * https://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-01 - OAuth 2 MAC tokens
 * https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-04 - OAuth 2 JSON Web Tokens Bearer tokens
 * https://tools.ietf.org/html/draft-jones-json-web-token-10 - JSON Web Tokens (TWT)
 * https://tools.ietf.org/wg/jose/ - JOSE; The WG behind JWA, JWS, JWE, JWT, ...
 * http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/

Please use the talkpage for any questions or comments on the topic.

--Daniel Friesen (Dantman) (talk)

Signatures
Signatures are an important part of the security of these specs. Signatures protect the important client secrets from being extracted by 3rd parties.
 * They are absolutely necessary when the request is being sent over HTTP due to it's inherent insecurity.
 * While TLS is supposed to protect the transport, that only protects the secret from middlemen. If a bug in the client or something related to discovery causes the client to make the OAuth request to a server on a different domain name all the owner of that domain name needs to do is use a valid certificate for their own domain name and the client will happily hand over it's primary secret credentials to the malicious party when signatures are not used.
 * Additionally there are known issues with the use of TLS by client developers.
 * The roots for TLS are not always configured by default and this often gets in the way of client developers.
 * As stupid as it is, many client developers decide to deal with this not by installing the root certificates but instead by setting verify_peer to false and effectively completely disabling all of TLS' security features.
 * In fact while cURL's default for verify_peer is True, the verify_peer setting for ssl:// https:// in PHP's own network libraries is False. ie: Insecurity by default.
 * Basically this means that client developers can't even be trusted to use TLS correctly. They will happily write apps that will leak client secrets to middlemen. So signatures become an important method of protecting the secret from 3rd parties.

OAuth 1
OAuth 1 defines 3 different signatures:
 * PLAINTEXT - This method isn't actually a signature. The client secret is simply sent to the server.
 * HMAC-SHA1 - This signature utilizes a shared secret that both the client and server know. A HMAC signature using the sha1 hash algorithm is used to sign the request keeping the actual signature from being sent over the wire.
 * RSA-SHA1 - This signature utilizes a RSA private key that the client knows for which the server is told what the client's public key is. A RSASSA-PKCS1-v1_5 signature is used to sign the request with the client's private key.

Overall OAuth 1 is better in the signature area than OAuth 2. Though it does have one sticking point. And a side point.

Issues with the signature base string
The OAuth 1 base string (the text that is signed and verified) includes the following:
 * The HTTP method
 * A base string uri consisting of:
 * http:// or https:// depending on if SSL/TLS is used
 * The hostname in the Host header
 * The port after a : if the port is non-default. Nothing if the port is default (ie: 80 for http, 443 for https)
 * The parameters to the request:
 * The query component of the request uri
 * The parameters inside of the HTTP Authorization header if used
 * The parameters inside the entity body (eg: POST body) if application/x-www-form-urlencoded is used

This data is encoded and put together into an & separated string and used to create the signature.

There are some tricky details to this whole ordeal:
 * The use of a http://example.org:8080/ style base string incurs extra code. You now have to be careful to make sure you omit the port if it is a specific port. This gives a situation where someone can easily make the mistake of including an :80 into the base url breaking their signature algorithm implementation. Basically it just makes signatures trickier to implement with no benefit.
 * The whole query component bit has a number of issues with it:
 * The spec tells clients and servers to work with the query data after it is decoded and work with it as a whole blob. Rather than just using the raw query strings used inside the request. Implementations end up needing to be careful how they order a long list of query parameters. If the order that parameters are in gets changed slightly the signature implementation is completely broken.
 * Url encoding itself is even inherently troublesome. There are multiple ways to encode one chunk of text for use in a url (ie: it's not a consistent encoding) as a result the OAuth spec actually goes to the trouble to define the exact url encoding algorithm that must be used inside of a signature.

While not impossible to implement. OAuth signatures are basically more complex than necessary. Simply due to adding a conditional that doesn't need to exist, lumping data together that could have been serialized as separate items, and picking methods of encoding the data that are historically not consistent enough to trust in signing. An ideal spec could have found a way to build the signature base string in a way that was much less prone to mistakes without affecting the security of the signature.

This is an example OAuth 1 signature base string:

RSA-SHA1
OAuth 1 includes the ability to use a client private key for the client credentials. Unfortunately using it isn't as ideal as one would wish.

OAuth 1 has these two things to say about the use of private keys in OAuth: 4.1. RSA-SHA1 Signature Method

Authenticated requests made with "RSA-SHA1" signatures do not use the token shared-secret, or any provisioned client shared-secret. This means the request relies completely on the secrecy of the private key used by the client to sign requests.

[...]

4.11. SHA-1 Cryptographic Attacks

SHA-1, the hash algorithm used in "HMAC-SHA1" and "RSA-SHA1" signature methods, has been shown to have a number of cryptographic weaknesses that significantly reduce its resistance to collision attacks. While these weaknesses do not seem to affect the use of  SHA-1 with the Hash-based Message Authentication Code (HMAC) and should not affect the "HMAC-SHA1" signature method, it may affect the use of the "RSA-SHA1" signature method. NIST has announced that it  will phase out use of SHA-1 in digital signatures by 2010 [NIST_SHA-1Comments].

Practically speaking, these weaknesses are difficult to exploit, and by themselves do not pose a significant risk to users of this protocol. They may, however, make more efficient attacks possible, and servers should take this into account when considering whether SHA-1 provides an adequate level of security for their applications.

As a result the use of private keys in OAuth 1 is less than ideal. I have a feeling that it's possible to write a standard that can make secure and effective use of private keys. Likely by using some algorithm that succeeds RSA-SHA1. And perhaps even by making use of a combination of an RSA key and a shared HMAC secret key (ie: Something like encrypting a temporary shared secret using a key then using HMAC-SHA1). But that won't happen anywhere under either version of OAuth.

OAuth 2
OAuth 2 takes signatures a whole different direction. Well, actually it doesn't take them anywhere. Instead of talking about signatures OAuth 2 just talks about 'tokens' and says that some types of access tokens can use signatures.

Some key points here:
 * Client credentials don't use signatures anywhere. OAuth 2 says to use the insecure HTTP Basic auth and relies entirely on broken TLS for the security of client<->server communication. It says that you 'can' use other http authentication systems however you must store a mapping of what clients use what authentication system.
 * Access tokens are actually the only part of the spec that support signatures. This means that the all important to keep secure refresh token that has more rights and persistence than the access token is sent entirely in the clear and relies on broken TLS. This also means that it is impossible to use OAuth 2 without the authorization server using TLS. The only place you can use http (and I'll point out why that's not even as secure as OAuth 1 later) is on the resource server using MAC based access tokens. However in reality the authorization server and the resource server are typically the same server. That means that a wiki actually has to have https support to safely use OAuth. (Something I'd like to say is not as cheap and easy as people would like to say it is, nor something to expect of the thousands of small wiki which have as much right to an auth scheme as Wikipedia).
 * OAuth 2 actually doesn't have any access token implementation inside of it. It defers that to two different specs oauth-v2-bearer and oauth-v2-http-mac.
 * OAuth 2 doesn't use client credentials in any request using an access token. As a result if an access token is leaked it can be used by a third party without needing to also get access to the client credentials.
 * OAuth 2 does not include any public/private key based signature. Unlike OAuth 1 you have no option besides shared secrets.
 * While not referred to from the OAuth 2 spec there is another method of signing things proposed. JWT, JSON Web Tokens. But it has issues too:
 * While JWT is based off of the JOSE WG's standards which include JWE (JSON Web Encryption) JWT tokens for OAuth 2 still don't use private keys
 * Ultimately you'd best not even consider JWT. JWT is non-trivial and completely excessive. It's designed entirely for the enterprise space where they can sell high priced consulting services to people. It's not something designed to make OAuth 2 something securely usable for everyone.

Bearer tokens have no security. They are basically the same as PLAINTEXT in OAuth 1.

OAuth 2 MAC access tokens
Signatures in OAuth 2 access tokens are defined by oauth-v2-http-mac. These signatures have some nice points as well as some faults.

Firstly the format of the MAC signature base string consists of:
 * The timestamp of the request (included in the MAC Authorization header)
 * The noonce
 * The HTTP Request method
 * The raw request-uri (The part before {GET,HEAD,...} and before HTTP/1.1, ie: just /foo no http://example.com:8080/)
 * The hostname inside of the Host header
 * The port used in the request (unconditionally, unlike in OAuth 1 where you have to omit a :80 over http)
 * The value of the ext="" field in the Authorization if used ("" otherwise)

These components are combined together into a simple string separated by newlines. For example: (\n to show where newlines are): 1336363200\n dj83hs9s\n GET\n /resource/1?b=1&a=2\n example.com\n 80\n \n

As a result besides all the issues I listed before with signatures in OAuth 2 the actual format of the signatures in OAuth 2 is much cleaner. This format is more consistent and easier to implement without making a critical mistake.

Unfortunately... the actual tokens themselves have an inferiority to the signatures used inside of OAuth 1.

In OAuth 1 the entity-body of a request was included inside of the signature if it was application/x-www-form-urlencoded data. However OAuth 2 signatures do not include the entity-body anywhere in the signature. As a result OAuth 2 signs the url of a request but it's possible for a middleman to freely modify the post body of a request without breaking the signature. So if you go and include API query parameters entirely inside the POST body instead of putting most of them in the query a middleman will be free to make an entirely different api request.

Protocol
OAuth 2 actually doesn't define a protocol. OAuth 1 was developed as a "protocol". While OAuth 2 is a "framework".

This basically means OAuth 1 and 2 are completely different in fundamental goals. OAuth 1 was going to be a standardized protocol. Meaning you could take nearly any properly implemented OAuth client and any properly implemented OAuth server and connect them together. OAuth 1 could actually be used in combination with discovery to use random websites for user auth.

OAuth 2 on the other hand calls itself a "framework" and lists a disclaimer "this specification is likely to produce a wide range of non-interoperable implementations."

Basically OAuth 2 is in essence a how-to guide on writing a proprietary auth setup for your own proprietary service. Implementation of the specification gives you no actual advantages. It doesn't give you security advantages because you could do just as good in your own proprietary home brewed system. And it doesn't give you any interoperability so you still end up writing your own server and client implementation because OAuth 2 isn't a protocol you can implement, it's a guide on how to write something yourself. And when it comes down to it there is absolutely no advantage to even sticking to the spec.

The spec says "This framework was designed with the clear expectation that future work will define prescriptive profiles and extensions necessary to achieve full web-scale interoperability." But that's basically just some hand-waving saying "Not our problem. Someone in the future will magically turn this into something interoperable across the whole web." which has no value because someone could just as easily write a brand new standard that serves that purpose and doesn't have the flaws.

Temporary credentials and flows
OAuth 1 uses the following pattern for grants:
 * Client makes a request to the server's "Temporary Credential Request" endpoint with a callback URI signed by the client credentials.
 * Some temporary credentials are returned from the server.
 * Client sends user agent to the server's "Resource Owner Authorization" with the public token given in the temporary credentials request.
 * User logs in and authorizes the client.
 * Server authorizes the temporary credentials.
 * Server redirects user back to the client's endpoint with the same token and a verifier attached.
 * Client makes a request to the server's "Token Request" endpoint with the public token signed by the client credentials and the secret temporary credentials.
 * Server returns permanent credentials to the client.
 * Client now makes a resource/api requests to the server as the user by signing the request with the client credentials and permanent credentials returned.

OAuth 2 uses the following pattern for the closest type of grants:
 * Client sends user agent to the server's "Authorization Endpoint" with response_type=code, the client id, a state parameter, and the callback URI.
 * User logs in and authorizes the client.
 * Server redirects user back to the client's endpoint with an authorization code and the same state parameter.
 * Client makes a request to the server's "Token Endpoint" with grant_type=authorization_code, the authorization code, and the client credentials.
 * Server returns an access token, expires time, and refresh token

The issue with temporary credentials
One problem with the OAuth 1 pattern was the "Temporary Credentials". The server first grants and stores some credentials. The user authorizes them. And then the client asks for permanent credentials. However a good portion of the time the user might decide they don't want to authorize the client and just close the window. As a result any OAuth 1 implementation is stuck storing an authorization for every single time someone says they want to let a user authorize their client. And they are stuck holding this data on for an indefinite amount of time because if they let it go and the user tries to authorize the client they will then get an error.

In the OAuth 2 flow. Instead of asking for temporary credentials first. The client sends the user to the server. The user authorizes the client. And only then after the user has authorized the client does the server create some temporary credentials and send them back along with the user to the site. The authorization code is protected from 3rd parties by requiring a combination of both the access code and the client credentials to get the code back. Though this does mean you need multiple authorization flows for non-browser authorizations to work.

Multiple flows
The final version of OAuth 1 included a single authorization flow for applications (multiple flows were merged into one). While this one flow was designed to work in multiple environments in practical use it was discovered that using this one authorization flow for multiple profiles of users lead to a substandard user experience on these other profiles.

OAuth 2 includes multiple authorization flows to fix this. However while it defines a flow to work with semi-offline, client side JavaScript only, and potentially browser built-in profiles and includes various other flows. It still does not provide an explicit description of how authorization of things such as mobile devices should work without the use of passwords.

An ideal spec can probably go to the effort to think of profiles at the user level and then come up with what authorization flows fit each one. Rather than just listing authorization flows with no reference to all the cases they may be used.