PrefixedApiKey (PrefixedApiKey v0.2.2)

View Source

PrefixedAPIKey is a quick Elixir port of Seam's Javascript Prefixed API Key library. It creates and verifies simple, random authentication tokens that have two useful prefix parts.

This version is a little different (it has a smaller set of functions and uses a slightly bigger character set) but appears to be compatible with the original.

Overview

Example key:

mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG

Seam-style API Keys have these benefits:

  • Double clicking the api key usually selects the entire api key
  • The alphabet is standard across languages (the original uses Base58, this uses a slightly larger alphanumeric ASCII set)
  • They are shorter than hex and base32 api keys
  • They have prefixes allowing secret scanning by github
  • They have a hashed component so the server doesn't need to store the api key (reducing attack surface)
  • They have unhashed short tokens which can be mutually used by the server and key bearer/customer to identify the api key
  • They default to roughly the same number of entropy bits as UUIDv4

The Format

Prefixed API Keys look like this:

mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG

Components:

mycompany ..._...  BRTRKFsL ..._...  51FwqftsmMDHHbJAMEXXHCgG
^                  ^                 ^
Prefix             Short Token       Long Token
  • The Prefix is used to identify the company or service creating the API Key. This is very helpful in secret scanning.
  • The Short Token is stored by both the server and the key bearer/customer, it can be used to identify an API key in logs or displayed on a customer's dashboard. A token can be deny-listed by its short token.
  • The Long Token is how we authenticate this key. The long token is never stored on the server, but a hash of it is stored on the server. When we receive an incoming request, we search our database for short_token and hash.

Example

iex> {:ok, key} = PrefixedApiKey.generate("example")
iex> api_key = key.api_key
iex> {:ok, _key} = PrefixedApiKey.parse(api_key)

iex> PrefixedApiKey.verify?("myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7", "d5264a8fef50459c35306c35396c446cf88f8755c06ff70c341eb3fbd606ca44")
true

Summary

Functions

Generates a new API key, using the specified prefix

Parses a prefixed API Key string into its component parts, and produces a hash of the long token.

Compares an Prefixed API Key string (or struct) to a hash and short token that you have previously stored, for verification of the key.

Compares an Prefixed API Key string (or struct) to a hash you have previously stored, for verification of the key.

Types

t()

@type t() :: %PrefixedApiKey{
  api_key: binary(),
  hash: binary(),
  long_token: binary(),
  prefix: binary(),
  short_token: binary()
}

Functions

generate(prefix)

@spec generate(prefix :: binary() | t()) :: {:ok, t()} | {:error, binary()}

Generates a new API key, using the specified prefix

Use the prefix to identify keys for your application.

Example

iex> PrefixedApiKey.generate("doormouse")

parse(api_key)

@spec parse(api_key :: binary() | t()) :: {:ok, t()} | {:error, binary()}

Parses a prefixed API Key string into its component parts, and produces a hash of the long token.

(You can also parse an already-parsed key)

Example

iex> PrefixedApiKey.parse("mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG") {:ok, %PrefixedApiKey{

         prefix: "mycompany",
         short_token: "BRTRKFsL",
         long_token: "51FwqftsmMDHHbJAMEXXHCgG",
         api_key: "mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG",
         hash: "d70d981d87b449c107327c2a2afbf00d4b58070d6ba571aac35d7ea3e7c79f37"
       }}

verify?(api_key, hash)

@spec verify?(api_key :: binary() | t(), hash :: binary()) :: true | false

Compares an Prefixed API Key string (or struct) to a hash and short token that you have previously stored, for verification of the key.

Example

iex> PrefixedApiKey.verify?(
...>   "mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG",
...>   "d70d981d87b449c107327c2a2afbf00d4b58070d6ba571aac35d7ea3e7c79f37",
...>   "BRTRKFsL")
true

verify?(api_key, hash, short)

@spec verify?(api_key :: binary() | t(), hash :: binary(), short :: binary()) ::
  true | false

Compares an Prefixed API Key string (or struct) to a hash you have previously stored, for verification of the key.

Example

iex> PrefixedApiKey.verify?(
...>        "mycompany_BRTRKFsL_51FwqftsmMDHHbJAMEXXHCgG",
...>        "d70d981d87b449c107327c2a2afbf00d4b58070d6ba571aac35d7ea3e7c79f37")
true