ProtoRune. Security
(proto_rune v0.5.2)
Copy Markdown
Safe defaults for persisting AT Protocol session tokens.
Bot and app authors often need to keep a session across restarts, but an access/refresh JWT pair written to disk in plaintext is a liability. This module combines the two halves of a safer default:
ProtoRune.Security.Crypto- AES-256-GCM encryption of the session payload (:cryptoonly, no extra dependencies).ProtoRune.Security.TokenStore- a behaviour for storage backends, withProtoRune.Security.TokenStore.Detsas the default.
The encryption key must be kept outside the token storage itself, typically in an environment variable or a secret manager:
# once, to provision the key:
key = ProtoRune.Security.generate_key()
System.put_env("PROTO_RUNE_TOKEN_KEY", ProtoRune.Security.encode_key(key))
# on boot:
{:ok, key} = ProtoRune.Security.decode_key(System.fetch_env!("PROTO_RUNE_TOKEN_KEY"))Then persist and restore sessions with:
:ok = ProtoRune.Security.save_session(session, key)
{:ok, session} = ProtoRune.Security.load_session("did:plc:alice", key)
:ok = ProtoRune.Security.delete_session("did:plc:alice")Sessions are keyed by their DID. To use another storage backend, pass
a {module, opts} tuple implementing ProtoRune.Security.TokenStore
as the last argument.
Summary
Functions
Decodes a Base64-encoded key. See Crypto.decode_key/1.
Deletes the session stored under did. Deleting a missing session returns :ok.
Encodes a key as Base64 for storage in env vars or config. See Crypto.encode_key/1.
Generates a random 32-byte encryption key. See Crypto.generate_key/0.
Loads and decrypts the session stored under did.
Encrypts session and stores it under its DID in store.
Functions
@spec decode_key(String.t()) :: {:ok, ProtoRune.Security.Crypto.key()} | {:error, :invalid_key_size | :invalid_key_encoding}
Decodes a Base64-encoded key. See Crypto.decode_key/1.
@spec delete_session( ProtoRune.Security.TokenStore.id(), ProtoRune.Security.TokenStore.backend() ) :: :ok | {:error, term()}
Deletes the session stored under did. Deleting a missing session returns :ok.
@spec encode_key(ProtoRune.Security.Crypto.key()) :: String.t()
Encodes a key as Base64 for storage in env vars or config. See Crypto.encode_key/1.
@spec generate_key() :: ProtoRune.Security.Crypto.key()
Generates a random 32-byte encryption key. See Crypto.generate_key/0.
@spec load_session( ProtoRune.Security.TokenStore.id(), ProtoRune.Security.Crypto.key(), ProtoRune.Security.TokenStore.backend() ) :: {:ok, ProtoRune.Atproto.Session.t()} | {:error, term()}
Loads and decrypts the session stored under did.
Returns {:error, :not_found} when no session is stored for did,
{:error, :decrypt_failed} when the key is wrong or the stored blob
was tampered with, and {:error, :invalid_session} when the decrypted
payload is not a ProtoRune.Atproto.Session.
Examples
{:ok, session} = ProtoRune.Security.load_session("did:plc:alice", key)
@spec save_session( ProtoRune.Atproto.Session.t(), ProtoRune.Security.Crypto.key(), ProtoRune.Security.TokenStore.backend() ) :: :ok | {:error, term()}
Encrypts session and stores it under its DID in store.
Returns :ok on success. store defaults to
{ProtoRune.Security.TokenStore.Dets, []}.
Examples
:ok = ProtoRune.Security.save_session(session, key)
:ok = ProtoRune.Security.save_session(session, key, {MyApp.TokenStore, []})