Repository navigation
api_keys 0.8.0: config.require_bearer_scheme
config.require_bearer_scheme makes the authentication header accept one credential shape only: Bearer <token>. One opt-in setting, no migration, no generator. The default keeps accepting a bare token exactly as every earlier version did.
Why
Since 0.1.0 the header has accepted two shapes: Authorization: Bearer ak_… and, as a fallback, the bare Authorization: ak_…. That fallback is convenient for curl, but a public API should parse one credential shape only. Two shapes mean two things to document, to test and to support forever, and a header such as Basic … or Token … was handed to the key lookup as if it were a token. ReplyBrain's public API asked for the strict shape; any app with a public API wants the same.
What changed
Added
-
config.require_bearer_scheme(Boolean, defaultfalse).ApiKeys.configure do |config| config.require_bearer_scheme = true end
When
true, the header named byconfig.headeris accepted only when it matchesBearer <token>: the scheme in any case (bearer,BEARER), followed by one or more spaces or tabs, then the token. Everything else is refused:Header value false(default)trueBearer ak_…(any case, spaces or tabs)token token ak_…(bare token)token 401 invalid_tokenBasic …,Token …looked up as a token, fails 401 invalid_token, never looked upBeareralonelooked up as a token, fails 401 invalid_token, never looked upno header, config.query_paramsetquery parameter query parameter A refused header is answered exactly like any wrong key: the ordinary 401
invalid_tokenthroughrender_unauthorized, with no hint that the shape was the problem. It is never looked up, so it costs no database query or bcrypt comparison. The header was present, so a refused header never falls back toconfig.query_param. The setting applies to whichever headerconfig.headernames, not onlyAuthorization.Like the other boolean settings, the writer accepts only
trueorfalseand raisesArgumentErrorotherwise. -
Initializer template:
rails g api_keys:installwrites the commented setting underconfig.header, recommended for public APIs. -
README: a new section, "Require the
Bearerscheme".
Changed
- README: the query parameter section says the gem expects keys in the
Authorizationheader, instead of "exclusively as HTTP Authentication Bearer tokens", which the bare-token fallback contradicted.
Security
- With the setting on, a credential in another scheme can no longer be mistaken for an API key, and the header is never parsed as a raw token. The refusal reuses the internal
MALFORMED_CREDENTIALsentinel that 0.7.0 introduced for oversized and invalid UTF-8 headers, so it takes the same constant-shape failure path and adds no new answer an attacker could distinguish. - No refusal statistics are stamped for a refused header: no key was identified.
Compatibility
- Default
false: nothing changes for any app that does not set it. - Turning it on breaks clients that send the bare token. Check your logs, or your SDKs and documentation, before you turn it on. With
config.debug_logging = truethe gem logs "Refused a header without the Bearer scheme." for each refusal.
Upgrade notes
No migration and no generator. Update the gem:
# Gemfile
gem "api_keys", "~> 0.8"bundle update api_keysTo require the scheme, add one line to config/initializers/api_keys.rb:
config.require_bearer_scheme = trueThe 0.7.0 upgrade notes still apply to apps coming from 0.6.x or earlier.
Tests
test/services/authenticator_test.rb: a bare token authenticates by default; with the setting, a bare token is an invalid token and is never looked up; the Bearer scheme still authenticates in any case and spacing; a refused header does not fall back to the query parameter; the setting applies to a customconfig.header.test/controllers/authentication_test.rb: through a controller, a bare token gets the ordinary 401invalid_tokenanswer.- Locally: 774 runs, 3424 assertions, 0 failures, 0 errors, 0 skips.
Automatically generated notes
What's Changed
Full Changelog: v0.7.0...v0.8.0