Xirsys.XTurn.Auth.SharedSecret (xturn v2.0.0)

Copy Markdown View Source

Stateless TURN REST API credentials (coturn-compatible shared secret).

What problem this solves

Signaling servers and Web backends need to mint time-limited TURN username/ password pairs without storing per-user secrets on the TURN server. The TURN REST API pattern embeds expiry in the username and derives the password from a shared HMAC key, matching coturn's use-auth-secret deployment model.

Usernames are "<expiry-unix>:<user-id>"; passwords are Base64(HMAC-SHA1(secret, username)). Verification recomputes the HMAC and rejects expired usernames before Allocate proceeds.

RFCs

  • RFC 8489 (long-term credentials, MESSAGE-INTEGRITY key derivation from the REST password)
  • TURN REST shared-secret credentials (coturn-compatible; widely deployed alongside RFC 5766 TURN)

Summary

Functions

Generates a time-limited REST username/password pair for user_id.

Returns true when username matches the <expiry>:<user-id> REST format.

Validates a REST username and returns the expected password on success.

Functions

generate(user_id, ttl_seconds, secret)

@spec generate(String.t(), pos_integer(), binary()) :: {String.t(), String.t()}

Generates a time-limited REST username/password pair for user_id.

The username embeds the Unix expiry timestamp; the password is the Base64-encoded HMAC-SHA1 of the username under secret.

rest_username?(username)

@spec rest_username?(String.t()) :: boolean()

Returns true when username matches the <expiry>:<user-id> REST format.

Examples

iex> Xirsys.XTurn.Auth.SharedSecret.rest_username?("1700000000:alice")
true

iex> Xirsys.XTurn.Auth.SharedSecret.rest_username?("alice")
false

verify_username(username, secret)

@spec verify_username(String.t(), binary()) :: {:ok, String.t()} | :expired | :error

Validates a REST username and returns the expected password on success.

Returns :expired when the embedded timestamp is in the past, or :error when the username format is invalid.

Examples

iex> secret = "test-secret"
iex> {username, password} = Xirsys.XTurn.Auth.SharedSecret.generate("alice", 3600, secret)
iex> {:ok, ^password} = Xirsys.XTurn.Auth.SharedSecret.verify_username(username, secret)

iex> past = System.system_time(:second) - 10
iex> Xirsys.XTurn.Auth.SharedSecret.verify_username(to_string(past) <> ":bob", "secret")
:expired