PhoenixKit.API.Keys.Token (phoenix_kit v2.64.1)

Copy Markdown View Source

The API key token format: pk_<prefix>_<secret>.

  • prefix is 8 lowercase hex characters (4 random bytes). It is stored in the clear, is unique, and is how a key row is found.
  • secret is 32 random bytes written as 43 base64url characters (no padding). It may contain _, so a token is split on its first two underscores only.
  • Only SHA-256(secret bytes) is stored (secret_hash). The token itself is shown once, when the key is created.

The secret has 256 bits of entropy, so a fast hash is enough: there is nothing to brute-force, unlike a human password.

Summary

Functions

Generates a new token.

SHA-256 of the raw secret bytes.

Constant-time comparison of two hashes.

Splits a presented token into its prefix and the hash of its secret.

The prefix of a token, or nil when the text does not start like one.

Functions

generate()

@spec generate() ::
  {prefix :: String.t(), token :: String.t(), secret_hash :: binary()}

Generates a new token.

Returns {prefix, token, secret_hash}. Store prefix and secret_hash; show token to the person once and forget it.

hash(secret)

@spec hash(binary()) :: binary()

SHA-256 of the raw secret bytes.

matches?(presented_hash, stored_hash)

@spec matches?(binary(), binary()) :: boolean()

Constant-time comparison of two hashes.

parse(token)

@spec parse(term()) :: {:ok, prefix :: String.t(), secret_hash :: binary()} | :error

Splits a presented token into its prefix and the hash of its secret.

Returns :error for anything that is not a canonical token: wrong length, another scheme, upper-case prefix, a secret that is not exactly 32 bytes.

prefix(token)

@spec prefix(term()) :: String.t() | nil

The prefix of a token, or nil when the text does not start like one.

Looks only at pk_<8 hex>_; it does not validate the secret. Safe for logging, because the prefix is not secret.