Specifications aim to find a common way to interoperate. This is why they need to be precise and unambiguous. But sometimes wrong interpretations occur, leading to implementations that break that desired interoperability. The “case” of the bearer scheme in OAuth 2.0 is one of these situations.
The Problem with the Bearer Scheme
Imagine your application is calling a protected third-party API and getting an error you don’t understand. Everything worked fine in your test environment when you mocked the API. Why does it fail when you call the actual API? This happens many times, but you have checked everything this time, and your code looks fine. You are passing an access token, which you are sure is valid and not expired. Yet you still have that 500 - Internal Server Error as response status code or, if you're lucky, 401 - Unauthorized.
After hours of investigations, you find that you were using “bearer” as the string that qualifies the scheme of your access token while the API expects “Bearer”. That's to say, your HTTP request is like the following:
GET /shop/orders
Host: api.example.com
Authorization: bearer eyJhbGciOiJIUzI1...
But the API expects a request like this:
GET /shop/orders
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1...
The letter case of "b" in "bearer" is the only difference! 😳
You can’t change the third-party API, of course, so you need to change your application. Is it fair?
The Discussion on the Bearer Scheme Case
You may think that the situation described in the previous section is an exceptional case. Actually, it's more common than you think (see this, this, this, and this, just for a few public examples).
There are many discussions about the correct letter case of the bearer scheme identifier in OAuth 2.0. Some say it should be capitalized, while others say it should be case-insensitive. Someone turned from case-insensitive to capitalized just to honor the specifications, not because they had any issue ( here and here, for example).
Many argue that the first letter should be capitalized and mention the specifications:
The syntax for Bearer credentials is as follows:
b64token = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="
credentials = "Bearer" 1*SP b64token
Others claim that it should be case-insensitive and mention the specifications as well:
token_type
REQUIRED. The type of the token issued as described in
Section 7.1. Value is case insensitive.
It sounds like a philosophical discussion, but it has practical consequences. Who is right?
Solving the Bearer Scheme Case
The OAuth 2.0 specifications clearly state that the token type (i.e., the "bearer" string) must be case insensitive. However, the syntax defined in the Bearer Token Usage specifications seems to contradict what the other one established. The ABNF expression defining the syntax uses "Bearer", the capitalized version:
b64token = 1*( ALPHA / DIGIT /
"-" / "." / "_" / "~" / "+" / "/" ) *"="
credentials = "Bearer" 1*SP b64token
Apparently the specifications seem inconsistent. 🤔
However, according to the specifications of the ABNF grammar:
NOTE:
ABNF strings are case insensitive and the character set for these
strings is US-ASCII.
Hence:
rulename = "abc"
and:
rulename = "aBc"
will match "abc", "Abc", "aBc", "abC", "ABc", "aBC", "AbC", and
"ABC".
To specify a rule that is case sensitive, specify the characters
individually.
For example:
rulename = %d97 %d98 %d99
or
rulename = %d97.98.99
will match only the string that comprises only the lowercase
characters, abc.
That means that the case-insensitive party is right! 🎉
You can write the "bearer" keyword however you like. This is more of a warning to API implementers than client builders because to be truly OAuth 2.0 compliant, they MUST accept any form of "bearer" keyword.
To remove any doubt, the OAuth discussion group added a note to the upcoming OAuth 2.1 specifications:
As described in Section 2.3 of [RFC5234], the string Bearer is case-insensitive. This means all of the following are valid uses of the Authorization header:
Authorization: Bearer mF_9.B5f-4.1JqM
Authorization: bearer mF_9.B5f-4.1JqM
Authorization: BEARER mF_9.B5f-4.1JqM
Keep in mind that even auThoriZation: bEAReR mF_9.B5f-4.1JqM
must be accepted. The HTTP header field names are case-insensitive, too. 😜
Conclusion
Small details that may seem insignificant can sometimes create incompatibility problems, and it may take a lot of time to figure out what's going on and find the correct solution. Writing (and reading) protocol specifications is complex. You must avoid ambiguities in their drafting and have a good technical background to read and implement them correctly.
I hope that this brief digression into the details of the OAuth specifications will be helpful to you and save you hours of debugging when an API is a little reluctant to accept creative ways of writing "bearer". 😇