Time-limited TURN credentials
Never ship a static TURN password inside a web or mobile app: anyone can copy it and use your bandwidth. Generate short-lived credentials on your server instead.
How it works
Section titled “How it works”ngTurn uses the widely supported shared-secret scheme from the TURN REST API proposal, the same one
coturn implements with use-auth-secret:
- The username is an expiry time in Unix seconds, optionally followed by a colon and your own user
ID:
1767225600:user-42. - The credential is the Base64-encoded HMAC-SHA1 of that username, keyed with your shared secret.
- The relay recomputes the HMAC and rejects the credential after the expiry time.
Keep lifetimes short, typically one hour for calls. Clients only need a credential that is valid when the session starts.
Generate them on your server
Section titled “Generate them on your server”import { createHmac } from 'node:crypto';
export function turnCredentials(userId, secret, ttlSeconds = 3600) {const username = `${Math.floor(Date.now() / 1000) + ttlSeconds}:${userId}`;const credential = createHmac('sha1', secret).update(username).digest('base64');return { username, credential, ttl: ttlSeconds };}import base64, hashlib, hmac, time
def turn_credentials(user_id: str, secret: str, ttl: int = 3600): username = f"{int(time.time()) + ttl}:{user_id}" digest = hmac.new(secret.encode(), username.encode(), hashlib.sha1).digest() return {"username": username, "credential": base64.b64encode(digest).decode(), "ttl": ttl}func TurnCredentials(userID, secret string, ttl time.Duration) (username, credential string) { username = fmt.Sprintf("%d:%s", time.Now().Add(ttl).Unix(), userID) mac := hmac.New(sha1.New, []byte(secret)) mac.Write([]byte(username)) credential = base64.StdEncoding.EncodeToString(mac.Sum(nil)) return}Serve credentials to clients
Section titled “Serve credentials to clients”Expose an authenticated endpoint in your backend that returns { username, credential } for the
signed-in user, and call it right before creating the peer connection.