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.
The package can be installed by adding prefixed_api_key
to your list of
dependencies in mix.exs
:
def deps do
[
{:prefixed_api_key, "~> 0.2.0"}
]
end
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
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
andhash
.
{:ok, key} = PrefixedApiKey.generate("myapp")
# =>
# {:ok,
# %PrefixedApiKey{
# api_key: "myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7",
# hash: "d5264a8fef50459c35306c35396c446cf88f8755c06ff70c341eb3fbd606ca44",
# long_token: "E34CUQSRtlmf0CMLsKFjMOf7",
# prefix: "myapp",
# short_token: "ZLXZ3PYn"
# }}
key.api_key
# => "myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7"
{:ok, key} = PrefixedApiKey.parse("myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7")
# =>
# {:ok,
# %PrefixedApiKey{
# api_key: "myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7",
# hash: "d5264a8fef50459c35306c35396c446cf88f8755c06ff70c341eb3fbd606ca44",
# long_token: "E34CUQSRtlmf0CMLsKFjMOf7",
# prefix: "myapp",
# short_token: "ZLXZ3PYn"
# }}
PrefixedApiKey.verify?("myapp_ZLXZ3PYn_E34CUQSRtlmf0CMLsKFjMOf7", "d5264a8fef50459c35306c35396c446cf88f8755c06ff70c341eb3fbd606ca44")
# => true
Full API documentation can be found at https://hexdocs.pm/prefixed_api_key.
You can request new features by creating an issue, or submit a pull request with your contribution.
- The original Prefixed API Key
- Discussion on the original library at Hacker News
- secure_compare function was borrowed from Plug Crypto
Copyright (c) 2022 Digital Identity Ltd, UK
PrefixedAPIKey is MIT licensed.