HMAC-signed URLs with expiry, for the cases where a client can't set request
headers (e.g. <video src>, <img src>, WebSocket upgrade without a
subprotocol).
The approach mirrors S3 presigned URLs: ExAtlas appends two query parameters —
exp (a Unix timestamp when the URL stops working) and sig (the HMAC-SHA256
of the URL's path + exp). The pod-side inference server recomputes the HMAC
and compares with Plug.Crypto.secure_compare/2.
The signing secret is not the bearer token from ExAtlas.Auth.Token. It's a
longer-lived per-pod (or per-user) secret you generate yourself, pass to the
pod via an env var, and use to sign URLs on the Phoenix side. ExAtlas gives you
both halves — signing and verification — to keep everything symmetric.
Example
secret = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
signed =
ExAtlas.Auth.SignedUrl.sign(
"https://abc-8000.proxy.runpod.net/stream/session-42",
secret: secret,
expires_in: 3600
)
# => "https://abc-8000.proxy.runpod.net/stream/session-42?exp=1747000000&sig=..."
# In the pod's Plug pipeline:
:ok = ExAtlas.Auth.SignedUrl.verify(conn.request_path <> "?" <> conn.query_string,
secret: secret)
Summary
Functions
Sign a URL. Returns the URL with exp and sig query parameters appended.
Verify a signed URL.
Types
Functions
Sign a URL. Returns the URL with exp and sig query parameters appended.
Options
:secret(required) — HMAC signing secret.:expires_in— seconds from now until the signature expires (default 3600).:now— override the current time (integer Unix seconds; for testing).
Verify a signed URL.
Accepts either the full URL or just the path + query string.
Returns :ok if the signature is valid and unexpired, or an error tuple:
{:error, :expired | :bad_signature | :malformed}.