GluuFederation / oxAuth

OAuth 2.0 server and client; OpenID Connect Provider (OP) & UMA Authorization Server (AS)
https://gluu.org/docs/ce
MIT License
423 stars 150 forks source link

Basic Auth for token endpoint not compliant with OAuth2.0 spec #677

Closed will-dee closed 6 years ago

will-dee commented 6 years ago

According to the OAuth2.0 spec, when doing auth code <-> token exchange the client ID and client secret should be url encoded prior to joining with a comma and then base46 encoding. For example:

client_id = '@!89FB.4D62.3A51.A9EB!0001!96E5.AE89!0008!B1AF.DB7D.1586.84F3'
client_secret = 'secret'
auth_header = 'Basic ' + base64(urlEncode(client_id) + ':' + urlEncode(client_secret))
// Final auth header according to spec:
//'Basic JTQwJTIxODlGQi40RDYyLjNBNTEuQTlFQiUyMTAwMDElMjE5NkU1LkFFODklM
// jEwMDA4JTIxQjFBRi5EQjdELjE1ODYuODRGMzpzZWNyZXQ=

However, oxAuth doesn't appear to respect the url encoding. If I try perform token exchange with the above authorisation header, I get a 401 error but if I instead compute the header without url encoding, the token exchange succeeds. For example:

auth_header = 'Basic ' + base64(client_id + ':' + client_secret)
// Final auth header that is accepted by oxAuth:
// Basic QCE4OUZCLjRENjIuM0E1MS5BOUVCITAwMDEhOTZFNS5BRTg5ITAwMDghQjFBRi
// 5EQjdELjE1ODYuODRGMzpzZWNyZXQ=

The returned response body and Headers are as follows:

// Response Body
{
  "error":"invalid_client",
  "error_description":"Client authentication failed (e.g. unknown client, no client authentication included, or unsupported authentication method). The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the Authorization request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code, and include the WWW-Authenticate response header field matching the authentication scheme used by the client."
}

// Headers
Connection →Keep-Alive
Content-Length →586
Content-Type →application/json;charset=iso-8859-1
Date →Wed, 25 Oct 2017 11:23:29 GMT
Keep-Alive →timeout=5, max=100
Server →Jetty(9.3.15.v20161220)
Strict-Transport-Security →max-age=31536000; includeSubDomains
WWW-Authenticate →Basic realm="oxAuth"
X-Content-Type-Options →nosniff
X-Xss-Protection →1; mode=block

This issue only appears when using client IDs/secrets that use 'special' characters as simple alphanumeric strings are invariant under url encoding.

qbert2k commented 6 years ago

There is a confusion in the specs because in rfc6749 it says:

Clients in possession of a client password MAY use the HTTP Basic
authentication scheme as defined in [RFC2617] to authenticate with
the authorization server. The client identifier is encoded using the
"application / x-www-form-urlencoded" encoding algorithm per
Appendix B, and the encoded value is used as the username; the client
password is encoded using the same algorithm and used as the
password The authorization server MUST support the HTTP Basic
authentication scheme for authenticating clients that were issued to
client password.

So the right way should be: auth_header = 'Basic' + base64 (urlEncode (client_id) + ':' + urlEncode (client_secret))

https://tools.ietf.org/html/rfc6749#section-2.3.1

But, in rfc2617 it says:

To receive authorization, the client sends the userid and password,
separated by a single colon (":") character, within a base64 [7]
encoded string in the credentials.

      basic-credentials = base64-user-pass
      base64-user-pass = <base64 [4] encoding of user-pass,
      user-pass = userid ":" password
      userid = * <TEXT excluding ":">
      password = * TEXT

Userids might be case sensitive.

If the user agent wishes to send the userid "Aladdin" and password
"open sesame", it would use the following header field:

   Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ ==

Base64 Decoding the example we got "open sesame" instead of "open% 20sesame". So the right way should be: auth_header = 'Basic' + base64 (client_id + ':' + client_secret)

https://tools.ietf.org/html/rfc2617#section-2

https://github.com/GluuFederation/oxAuth/blob/master/Client/src/test/java/org/xdi/oxauth/ws/rs/ClientSecretBasicTest.java

Anyway, I have modified the code in oxAuth Server and now it supports both rfc6749 and rfc2617 way.

qbert2k commented 6 years ago

Clarifying the above change, oxAuth is URL decoding the clientId and clientSecret so it will work with both cases:

Base64Encode(URLEncode(clientId) + ":" + URLEncode(clientSecret))
Base64Encode(clientId + ":" + clientSecret)

https://github.com/GluuFederation/oxAuth/commit/6431ada6ede8fa2e338b0f40896bf3f31a473333

username = URLDecoder.decode(token.substring(0, delim), Util.UTF8_STRING_ENCODING);
password = URLDecoder.decode(token.substring(delim + 1), Util.UTF8_STRING_ENCODING);

Also to make it more clear, I have added now a new test case using HTTP Basic Authentication in oxAuth with Url encoding client Id and client secret before perform the Base 64 encoding:

Base64Encode(URLEncode(clientId) + ":" + URLEncode(clientSecret))
Base64Encode(URLEncode("@!90CC.2E38.774C.610B!0001!FD3B.B0A0!0008!B011.91DC.5EAA.D899") + ":" + URLEncode("3fc14805-7bdb-4ce0-9861-f6ead000ac73"))
Base64Encode("%40%2190CC.2E38.774C.610B%210001%21FD3B.B0A0%210008%21B011.91DC.5EAA.D899" + ":" + "3fc14805-7bdb-4ce0-9861-f6ead000ac73")
Base64Encode("%40%2190CC.2E38.774C.610B%210001%21FD3B.B0A0%210008%21B011.91DC.5EAA.D899:3fc14805-7bdb-4ce0-9861-f6ead000ac73")
JTQwJTIxOTBDQy4yRTM4Ljc3NEMuNjEwQiUyMTAwMDElMjFGRDNCLkIwQTAlMjEwMDA4JTIxQjAxMS45MURDLjVFQUEuRDg5OTozZmMxNDgwNS03YmRiLTRjZTAtOTg2MS1mNmVhZDAwMGFjNzM=

https://github.com/GluuFederation/oxAuth/commit/227f79d8aba8e5a7c15151eb4083243e583aa308#diff-58a42c9f996cf0170d1de06a81d5580fR87

POST /restv1/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: ce.gluu.info
Authorization: Basic JTQwJTIxOTBDQy4yRTM4Ljc3NEMuNjEwQiUyMTAwMDElMjFGRDNCLkIwQTAlMjEwMDA4JTIxQjAxMS45MURDLjVFQUEuRDg5OTozZmMxNDgwNS03YmRiLTRjZTAtOTg2MS1mNmVhZDAwMGFjNzM=

grant_type=authorization_code&code=f327363e-de4e-4408-ad18-6fadf4e3f32a&redirect_uri=https%3A%2F%2Fce.gluu.info%3A8443%2Foxauth-rp%2Fhome.htm

Support ticket: https://support.gluu.org/authentication/4947/token-endpoint-is-not-compliant-with-oauth-2-spec-for-basic-auth/