181 lines
4.7 KiB
Go
181 lines
4.7 KiB
Go
package httpbearer
|
||
|
||
import (
|
||
"strings"
|
||
)
|
||
|
||
// Parse parses the value of an HTTP "Authorization" header and returns a bearer token, if there was one.
|
||
// If there was no bearer token in the HTTP "Authorization" header, then the returned value of ‘successful’ will be false.
|
||
//
|
||
// The full HTTP request header will look something like this:
|
||
//
|
||
// "Authorization: Bearer WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au\r\n"
|
||
//
|
||
// This function expected to receive just the value. So, with our previous example, that would be:
|
||
//
|
||
// "Bearer WW91IGFyZSBub3QgYSBkcm9wIGluIHRoZSBvY2Vhbi4gWW91IGFyZSB0aGUgZW50aXJlIG9jZWFuLCBpbiBhIGRyb3Au"
|
||
//
|
||
// Note that HTTP headers allow for extra "\t" and " " to be put in any place where a "\t" or " " already is.
|
||
//
|
||
// So, for example, this:
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// And these:
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\t\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\t\t\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\t\t\t\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\t\t\t\t\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\t\t\t\t\t\tabcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer \t \t abcde12345\r\n"
|
||
//
|
||
// Are all equivalent.
|
||
//
|
||
// Also note that HTTP headers also allow for the values to be broken up in multiple lines.
|
||
// The rule is that if a "\r\n" is followed by a " " or "\t" the that next line is part of the previous line.
|
||
//
|
||
// So, for example, this:
|
||
//
|
||
// "Authorization: Bearer abcde12345\r\n"
|
||
//
|
||
// And these:
|
||
//
|
||
// "Authorization:\r\n Bearer abcde12345\r\n"
|
||
//
|
||
// "Authorization:\r\n\tBearer abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\r\n abcde12345\r\n"
|
||
//
|
||
// "Authorization: Bearer\r\n\tabcde12345\r\n"
|
||
//
|
||
// "Authorization:\r\n \r\n\t Bearer \r\n\t \t\t\t abcde12345\r\n"
|
||
//
|
||
// Are all equivalent.
|
||
//
|
||
// Parse deals with all of these, too.
|
||
func Parse(value string) (bearerToken string, successful bool) {
|
||
|
||
// Although the first important thing we expected it the string "Bearer",
|
||
// there could be zero or more of these characters.
|
||
//
|
||
// • "\t" i.e,. horizontal tab (␉)
|
||
// • " " i.e., space (␠)
|
||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||
//
|
||
// In IETF RFC822 LWSP-char is defined as a horizontal tab (␉) or space (␠).
|
||
//
|
||
// Technically "\r\n" should always be followed by a " " or a "\t".
|
||
// But we don't have to worry about that here. As the parser for the request
|
||
// already dealt with that.
|
||
value = trimleft(value)
|
||
|
||
// The first important thing we should see it "Bearer".
|
||
//
|
||
//@TODO: should this be case insensitive?
|
||
{
|
||
const expected string = "Bearer"
|
||
|
||
if !strings.HasPrefix(value, expected) {
|
||
return "", false
|
||
}
|
||
|
||
value = value[len(expected):]
|
||
}
|
||
|
||
// The next thing we should see is one of these 3:
|
||
//
|
||
// • "\t" i.e,. horizontal tab (␉)
|
||
// • " " i.e., space (␠)
|
||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||
//
|
||
// In IETF RFC822 LWSP-char is defined as a horizontal tab (␉) or space (␠).
|
||
//
|
||
// Technically "\r\n" should always be followed by a " " or a "\t".
|
||
// But we don't have to worry about that here. As the parser for the request
|
||
// already dealt with that.
|
||
//
|
||
// Note that what we are doing here is safe, even if we are dealing with the UTF-8 Unicode encoding.
|
||
{
|
||
if len(value) <= 0 {
|
||
return "", false
|
||
}
|
||
|
||
c0, value := value[0], value[1:]
|
||
|
||
switch c0 {
|
||
case ' ','\t':
|
||
// Nothing here. We got LWSP-char. So we will continue.
|
||
case '\r':
|
||
if len(value) <= 0 {
|
||
return "", false
|
||
}
|
||
c1, value := value[0], value[1:]
|
||
value = value[1:]
|
||
if '\n' != c1 {
|
||
return "", false
|
||
}
|
||
|
||
// Nothing else here. We got "\r\n". So we will continue.
|
||
default:
|
||
return "", false
|
||
}
|
||
}
|
||
|
||
// There could be more of these characters:
|
||
//
|
||
// • "\t" i.e,. horizontal tab (␉)
|
||
// • " " i.e., space (␠)
|
||
// • "\r\n" i.e., carriage return (␍), line feed (␊)
|
||
//
|
||
// We will consume them (and ignore them) if they are there.
|
||
value = trimleft(value)
|
||
|
||
// What should be left is the bearer token (with possibly some LWSP-chars or "\r\n" after it).
|
||
//
|
||
// Note that what we are doing here is safe, even if we are dealing with the UTF-8 Unicode encoding.
|
||
{
|
||
if len(value) <= 0 {
|
||
return "", true
|
||
}
|
||
|
||
for i,c := range value {
|
||
switch c {
|
||
case ' ','\t':
|
||
return value[:i], true
|
||
case '\r':
|
||
if len(value) < i+2 {
|
||
return value, true
|
||
}
|
||
|
||
next := value[i+1]
|
||
if '\n' != next {
|
||
return value[:i+1], true
|
||
}
|
||
|
||
return value[:i], true
|
||
}
|
||
}
|
||
|
||
return value, true
|
||
}
|
||
|
||
}
|