diff --git a/httplib.h b/httplib.h index 4f81ad49..294bae1e 100644 --- a/httplib.h +++ b/httplib.h @@ -10458,18 +10458,15 @@ inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) { } inline std::string get_bearer_token_auth(const Request &req) { - if (req.has_header("Authorization")) { - constexpr auto bearer_header_prefix_len = detail::str_len("Bearer "); - auto value = req.get_header_value("Authorization"); - // Only strip the prefix when the value actually carries the "Bearer " - // scheme (case-insensitive per RFC 7235). Without this the fixed-length - // substr throws out_of_range on a value shorter than the prefix, and a - // different scheme (e.g. "Basic ...") is mistaken for a bearer token. - if (value.size() >= bearer_header_prefix_len && - detail::case_ignore::equal(value.substr(0, bearer_header_prefix_len), - "Bearer ")) { - return value.substr(bearer_header_prefix_len); - } + // The auth scheme is case-insensitive (RFC 9110 11.1), and a value shorter + // than the prefix carries no token. + constexpr const char bearer_prefix[] = "Bearer "; + constexpr auto bearer_prefix_len = detail::str_len(bearer_prefix); + auto value = req.get_header_value("Authorization"); + if (value.size() >= bearer_prefix_len && + detail::case_ignore::equal(value.substr(0, bearer_prefix_len), + bearer_prefix)) { + return value.substr(bearer_prefix_len); } return ""; } diff --git a/test/test.cc b/test/test.cc index 5a0b13af..b610c21b 100644 --- a/test/test.cc +++ b/test/test.cc @@ -1560,34 +1560,6 @@ TEST(GetHeaderValueTest, RegularInvalidValueInt) { EXPECT_TRUE(is_invalid_value); } -TEST(BearerTokenAuthTest, SchemeValidation) { - // A value shorter than "Bearer " must not throw from the fixed-length - // substr, and a non-Bearer scheme must not be reported as a token. - { - Request req; - req.set_header("Authorization", "x"); - EXPECT_EQ("", get_bearer_token_auth(req)); - } - { - Request req; - req.set_header("Authorization", "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="); - EXPECT_EQ("", get_bearer_token_auth(req)); - } - - // A well-formed header still yields the token; the scheme is - // case-insensitive. - { - Request req; - req.set_header("Authorization", "Bearer abc123"); - EXPECT_EQ("abc123", get_bearer_token_auth(req)); - } - { - Request req; - req.set_header("Authorization", "bearer abc123"); - EXPECT_EQ("abc123", get_bearer_token_auth(req)); - } -} - TEST(GetHeaderValueTest, OutOfRangeValueInt) { // An all-digit value that overflows size_t must be reported as invalid, not // silently saturated/truncated: parsing at size_t width would otherwise wrap @@ -1646,6 +1618,26 @@ TEST(GetHeaderValueTest, Range) { } } +TEST(BearerTokenAuthTest, SchemeValidation) { + // A value shorter than "Bearer " must not throw from the fixed-length + // substr, and a non-Bearer scheme must not be reported as a token. + struct { + const char *value; + const char *expected; + } cases[] = { + {"x", ""}, + {"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==", ""}, + {"Bearer abc123", "abc123"}, + {"bearer abc123", "abc123"}, // scheme match is case-insensitive + }; + + for (const auto &c : cases) { + Request req; + req.set_header("Authorization", c.value); + EXPECT_EQ(c.expected, get_bearer_token_auth(req)) << "value: " << c.value; + } +} + TEST(HeadersOrderTest, DuplicateFieldsKeepInsertionOrder) { // RFC 9110 5.3: the order of fields sharing a name is significant. This used // to depend on the standard library (libstdc++ handed back duplicates in