New token authentication mechanism #11547

Closed
opened 2025-12-29 21:46:37 +01:00 by adam · 2 comments
Owner

Originally created by @jeremystretch on GitHub (Aug 29, 2025).

Originally assigned to: @jeremystretch on GitHub.

NetBox version

v4.3.7

Feature type

New functionality

Proposed functionality

This FR proposes a new type of token for authenticating REST & GraphQL API requests. Unlike current tokens, which are monolithic strings, the new format includes both a unique public ID and a secret plaintext in the format <id>.<token>.

Changes

1. Extend the Token model

  • Add a version PositiveSmallIntegerField to track the token version. Legacy tokens are v1; new, hashed tokens are v2.
  • Add a nullable pepper PositiveSmallIntegerField to track the pepper used to generate to hash (see below).
  • Add a nullable key fixed-length CharField. This will hold the alphanumeric ID for v2 tokens.
  • Add a nullable digest CharField. This will hold the SHA-256 digest for v2 tokens.

2. Extend TokenAuthentication backend

  • Update the TokenAuthentication class to support two header formats:
    • Authorization: Token ... will be retained for v1 tokens.
    • The new format Authorization: Bearer ... will be used for v2 tokens.

3. Introduce a configuration parameter to store versions peppers

  • Introduce the API_TOKEN_PEPPERS configuration parameter to store versioned peppers.
    • Peppers will be versioned to allow rotating token hashes over time.
    • Pepper values can be populated either from the configuration file or from environment variables.

Workflows

Token creation

The process for creating a token is as follows:

  1. A new random token ID and plaintext are generated.
  2. The plaintext is hashed along with the currently configured global pepper.
  3. The ID and digest are stored in the database.
  4. The plaintext is immediately returned to the user. This is the only time the plaintext token is sent from the server.

Token authentication

  1. An API request is sent to the server with the header Authorization: Bearer <id>.<token>.
  2. The token (if one exists) is retrieved from the database by its ID.
  3. The presented plaintext is hashed along with the pepper specified by the database record.
  4. The resulting digest is compared to the stored digest. If they are the same, authentication passes.

Token retrieval

As with user passwords, the retrieval of existing v2 tokens is not possible, as only cryptographic digests are stored.

All other token features (expiration times, IP restrictions, etc.) will continue to be supported for both v1 and v2 tokens. Support for v1 tokens will be discontinued in a future NetBox release.

Use case

This new generation of API tokens provides a migration path away from storing plaintext token values in the database (see, for example, #20125), while retaining backward compatibility for legacy API tokens. The employment of HMAC-SHA-256 with peppers stored outside the database embody current best practices with regard to secure token storage. Additionally, the move to the Bearer header format ensures compliance with RFC 6750.

Database changes

Add the version, pepper, key, and digest fields to the Token model as described above. No modification to existing fields is anticipated.

External dependencies

N/A

Originally created by @jeremystretch on GitHub (Aug 29, 2025). Originally assigned to: @jeremystretch on GitHub. ### NetBox version v4.3.7 ### Feature type New functionality ### Proposed functionality This FR proposes a new type of token for authenticating REST & GraphQL API requests. Unlike current tokens, which are monolithic strings, the new format includes both a unique public ID and a secret plaintext in the format `<id>.<token>`. ### Changes #### 1. Extend the `Token` model * Add a `version` PositiveSmallIntegerField to track the token version. Legacy tokens are v1; new, hashed tokens are v2. * Add a nullable `pepper` PositiveSmallIntegerField to track the pepper used to generate to hash (see below). * Add a nullable `key` fixed-length CharField. This will hold the alphanumeric ID for v2 tokens. * Add a nullable `digest` CharField. This will hold the SHA-256 digest for v2 tokens. #### 2. Extend `TokenAuthentication` backend * Update the `TokenAuthentication` class to support two header formats: * `Authorization: Token ...` will be retained for v1 tokens. * The new format `Authorization: Bearer ...` will be used for v2 tokens. #### 3. Introduce a configuration parameter to store versions peppers * Introduce the `API_TOKEN_PEPPERS` configuration parameter to store versioned peppers. * Peppers will be versioned to allow rotating token hashes over time. * Pepper values can be populated either from the configuration file or from environment variables. ### Workflows #### Token creation The process for creating a token is as follows: 1. A new random token ID and plaintext are generated. 2. The plaintext is hashed along with the currently configured global pepper. 3. The ID and digest are stored in the database. 4. The plaintext is immediately returned to the user. This is the only time the plaintext token is sent from the server. #### Token authentication 1. An API request is sent to the server with the header `Authorization: Bearer <id>.<token>`. 2. The token (if one exists) is retrieved from the database by its ID. 3. The presented plaintext is hashed along with the pepper specified by the database record. 4. The resulting digest is compared to the stored digest. If they are the same, authentication passes. #### Token retrieval As with user passwords, the retrieval of existing v2 tokens is not possible, as only cryptographic digests are stored. All other token features (expiration times, IP restrictions, etc.) will continue to be supported for both v1 and v2 tokens. Support for v1 tokens will be discontinued in a future NetBox release. ### Use case This new generation of API tokens provides a migration path away from storing plaintext token values in the database (see, for example, #20125), while retaining backward compatibility for legacy API tokens. The employment of HMAC-SHA-256 with peppers stored outside the database embody current best practices with regard to secure token storage. Additionally, the move to the `Bearer` header format ensures compliance with [RFC 6750](https://datatracker.ietf.org/doc/html/rfc6750#section-2.1). ### Database changes Add the `version`, `pepper`, `key`, and `digest` fields to the Token model as described above. No modification to existing fields is anticipated. ### External dependencies N/A
adam added the status: acceptedtype: featurecomplexity: high labels 2025-12-29 21:46:37 +01:00
adam closed this issue 2025-12-29 21:46:37 +01:00
Author
Owner

@rfdrake commented on GitHub (Sep 10, 2025):

You may have already considered this and resolved it another way, but I would also put a timestamp in the Bearer line, and encode that as part of the HMAC signature. This will help to prevent replay attacks.

Something like: "Authorization: Bearer id.token.timestamp"

Then the HMAC signature would be computed by appending the timestamp to the message you're encoding.

@rfdrake commented on GitHub (Sep 10, 2025): You may have already considered this and resolved it another way, but I would also put a timestamp in the Bearer line, and encode that as part of the HMAC signature. This will help to prevent replay attacks. Something like: "Authorization: Bearer id.token.timestamp" Then the HMAC signature would be computed by appending the timestamp to the message you're encoding.
Author
Owner

@jeremystretch commented on GitHub (Oct 6, 2025):

Going to add a token prefix (e.g. nbt_) as well, to aid in secrets detection.

@jeremystretch commented on GitHub (Oct 6, 2025): Going to add a token prefix (e.g. `nbt_`) as well, to aid in secrets detection.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/netbox#11547