api_keys 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 62087b06751cfbfe1b08bc1db54179fa1ce72844da86166a5198887d7b834a3f
4
- data.tar.gz: 51ddeccbaee055eaf9d1cec6677b9514b478bf0ff4764768dee7e40e731c8685
3
+ metadata.gz: 465a7a5c57c770d52195e2c8a182b00d60ea19dc8cc879ebfdb83b3e16d810f2
4
+ data.tar.gz: daa04e5c07e2dbfe8200c9a43189a2249b680e00737f6c201646a1495847fb06
5
5
  SHA512:
6
- metadata.gz: 5b8adba5c3631e272a77bfd600f4667981bf91048e529aeac7b5f6f0c6d9b1967c989edff1ee23ee9d2a2bb9da3d83a9206e7b0a20ca26f741ebb8bae5abc586
7
- data.tar.gz: e0bbcb9c1bf0be08bdbe9ffe035e7dbe60a2cddc0520474fb94c40ca6cba53c46fc8f4ce5cb9537fd14ab4392149ac294a89d7599a61ba95e66a5f8e1864fd6b
6
+ metadata.gz: a4b8b2a0f2386cbbe52505afc68e6ca972aaf4106efa27d72aaa09441805760eea8772666133734c288c39cd8542db096a712bc17ad514a1be8331875faf741d
7
+ data.tar.gz: 3bd195acb458ff535ed41ceb699d3bf6972ace5cc7d46e80a58ac743849ef3549baaaf29baacf836c5ede49587cd93c1a2532ea002f9d5667802d6c830712de6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.8.0] - 2026-10-25
2
+
3
+ ### Added
4
+
5
+ - `config.require_bearer_scheme`. When true, the authentication header is accepted only as `Bearer <token>` (any case, spaces or tabs after the scheme); a bare token, another scheme such as `Basic`, or a lone `Bearer` gets the ordinary 401 `invalid_token`, is never looked up, and never falls back to `config.query_param`. It applies to whichever header `config.header` names. The default, false, keeps accepting a bare token as every earlier version did. Asked for by ReplyBrain, whose public API should parse one credential shape only.
6
+
1
7
  ## [0.7.0] - 2026-10-24
2
8
 
3
9
  Four features that an invoicing app on the fleet runs in production today, moved into the gem with their tests so that apps on the fleet can delete their own copies. Each one is opt-in: an app that does not use it sees no change.
data/README.md CHANGED
@@ -1298,9 +1298,19 @@ See the [Key Types](#key-types-stripe-style-publishable--secret-keys) section be
1298
1298
 
1299
1299
  Some highlights:
1300
1300
 
1301
+ ### Require the `Bearer` scheme
1302
+
1303
+ Clients send `Authorization: Bearer ak_123abc...`. For compatibility, a bare `Authorization: ak_123abc...` is accepted too. A public API should accept one shape only, so turn the bare token off:
1304
+
1305
+ ```ruby
1306
+ config.require_bearer_scheme = true
1307
+ ```
1308
+
1309
+ Now a header without the scheme (a bare token, `Basic …`, `Token …`) gets the same 401 `invalid_token` as any wrong key, and is never looked up. It applies to whichever header you configured in `config.header`, and a refused header never falls back to the query parameter.
1310
+
1301
1311
  ### Accept API keys via query params instead of Authentication HTTP headers
1302
1312
 
1303
- By default, the `api_key` gem expects API keys to come *exclusively* as HTTP Authentication Bearer tokens, for security purposes. But you can allow users to make requests to your endpoints with the API key token passed as a URL query param too, like this:
1313
+ By default, the `api_key` gem expects API keys to come *exclusively* in the `Authorization` header, for security purposes. But you can allow users to make requests to your endpoints with the API key token passed as a URL query param too, like this:
1304
1314
 
1305
1315
  ```
1306
1316
  https://example.com/api/endpoint?api_key=ak_123abc...
@@ -16,7 +16,13 @@ module ApiKeys
16
16
  # == Accessors ==
17
17
 
18
18
  # Core Authentication
19
- attr_reader :header, :query_param
19
+ #
20
+ # @!attribute [rw] require_bearer_scheme
21
+ # @return [Boolean] Accept the header only as "Bearer <token>". A value
22
+ # without the scheme is refused like any invalid token (401
23
+ # `invalid_token`). false (the default) also accepts the bare token,
24
+ # as every earlier version did.
25
+ attr_reader :header, :query_param, :require_bearer_scheme
20
26
 
21
27
  # Token Generation
22
28
  attr_reader :token_prefix, :token_length, :token_alphabet
@@ -192,7 +198,7 @@ module ApiKeys
192
198
  BOOLEAN_SETTINGS = %i[
193
199
  require_key_name track_requests_count https_only_production https_strict_mode
194
200
  enable_async_operations debug_logging strict_environment_isolation
195
- dashboard_allow_cross_environment
201
+ dashboard_allow_cross_environment require_bearer_scheme
196
202
  ].freeze
197
203
 
198
204
  def header=(value)
@@ -633,6 +639,7 @@ module ApiKeys
633
639
  # Core Authentication
634
640
  @header = "Authorization" # Expects "Bearer <token>"
635
641
  @query_param = nil # No query param lookup by default
642
+ @require_bearer_scheme = false # Also accept a bare token in the header
636
643
 
637
644
  # Token Generation
638
645
  @token_prefix = -> { "ak_" }
@@ -18,8 +18,9 @@ module ApiKeys
18
18
  # The longest authentication header that can still carry a valid token:
19
19
  # the token plus the "Bearer" scheme and generous separator whitespace.
20
20
  MAX_CREDENTIAL_HEADER_BYTESIZE = MAX_TOKEN_BYTESIZE + 64
21
- # Stands in for a header refused before parsing. It is not a String, so
22
- # it can never match a key and always yields :invalid_token.
21
+ # Stands in for a refused header: malformed, or without the Bearer
22
+ # scheme the app requires. It is not a String, so it can never match a
23
+ # key and always yields :invalid_token.
23
24
  MALFORMED_CREDENTIAL = Object.new.freeze
24
25
  MAX_BCRYPT_CANDIDATES = 32
25
26
  MAX_KNOWN_PREFIXES = 1_024
@@ -146,7 +147,13 @@ module ApiKeys
146
147
  log_debug "[ApiKeys Auth] Extracted token from Bearer scheme."
147
148
  return match[1]
148
149
  end
149
- # Fallback: return the raw header value if no Bearer scheme
150
+ # A bare token is accepted unless the app requires the scheme.
151
+ # Refused, it is an invalid token: the header was present, so it
152
+ # never falls through to the query parameter.
153
+ if config.require_bearer_scheme
154
+ log_debug "[ApiKeys Auth] Refused a header without the Bearer scheme."
155
+ return MALFORMED_CREDENTIAL
156
+ end
150
157
  log_debug "[ApiKeys Auth] No Bearer scheme, using raw header value as token."
151
158
  return header_value
152
159
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiKeys
4
- VERSION = "0.7.0"
4
+ VERSION = "0.8.0"
5
5
  end
@@ -9,6 +9,11 @@ ApiKeys.configure do |config|
9
9
  # Default: "Authorization" (expects "Bearer <token>")
10
10
  # config.header = "Authorization"
11
11
 
12
+ # Accept the header only as "Bearer <token>", refusing a bare token with
13
+ # 401 invalid_token. Recommended for public APIs.
14
+ # Default: false (a bare token is accepted too)
15
+ # config.require_bearer_scheme = true
16
+
12
17
  # The query parameter name to check as a fallback if the header is missing.
13
18
  # WARNING: Query parameters appear in logs, browser history, and Referer headers.
14
19
  # Only enable for development/testing. Set to nil to disable (recommended).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rameerez