Coturn use-auth-secret: fixing the empty realm and repeated 401s
Repeated 401 responses can make a TURN server look as if it rejects every credential. On
a server using use-auth-secret, the first question is not "is the password
wrong?" It is whether coturn has a realm to challenge the client with at all.
Read the startup log before the request log
A useful log starts like this:
Default realm:
WARNING: CONFIG: you did specify the long-term credentials usage
but you did not specify the default realm option (-r option).
Check your configuration.
realm <> user <>: incoming packet message processed,
error 401: Unauthorized
use-auth-secret uses the long-term credential mechanism. A non-empty realm is
part of the challenge. An empty realm means authentication cannot progress in the normal
way, even if the client is sending a syntactically valid username and password.
Add a stable realm:
realm=turn.example.com
A hostname is convenient if the same server may later use TLS. If the deployment is intentionally IP-only, use a stable value consistently. Treat the realm as deployment configuration, not as a secret or a second password.
Make sure the configuration is actually loaded
Start the server with an absolute configuration path:
turnserver -c /root/turnserver.conf
The startup output should show a non-empty Default realm. This matters because
a misspelled option or a different working directory can leave you editing one file while
the process runs with another.
A single 401 on the first request is normal. The client is being challenged and is expected to retry with credentials. The symptom to fix is an endless stream of 401s that ends in a browser error such as:
TURN allocate request timed out.
Generate the REST credential exactly
For coturn's TURN REST mode, the username and password are:
username = <unix-expiry>:<label>
password = base64(HMAC-SHA1(secret, username))
A shell verification using the same secret is:
SECRET='mysecret'
EXP=$(($(date +%s) + 3600))
USER="$EXP:bob"
PASS=$(printf '%s' "$USER" | openssl dgst -binary -sha1 -hmac "$SECRET" | openssl base64)
printf 'username=%s\npassword=%s\n' "$USER" "$PASS"
The expiry must be in the future. The secret must contain exactly the bytes configured on the server, with no accidental trailing whitespace or quotes from a shell file. The password is the base64 representation of the HMAC result, not a direct HMAC string and not an MD5 digest.
Test with the Trickle ICE page
Create the credentials above, then paste them into the browser's Trickle ICE page with:
turn:12.34.567.89:3478?transport=udp
The useful result is a relay candidate. Seeing the TURN URL in the configuration is not enough. A STUN response from the same host also does not prove that TURN authentication works.
If the page finds a relay candidate but a real call still fails, keep the successful credential output and compare the exact values sent by the application. The browser may be generating a different username, expiry or password from the same secret.
Use a command-line client to remove the browser
After generating credentials, test the relay directly. The exact flags vary with the
coturn build, so confirm them with turnutils_uclient -h, then run:
turnutils_uclient -v \
-u "$USER" \
-w "$PASS" \
-p 3478 \
12.34.567.89
If this succeeds and the browser does not, the relay is reachable and the remaining problem is in credential generation or browser configuration. If it fails, read the coturn response code instead of stopping at the browser timeout.
Read the response code, not just the symptom
- 401: the challenge. One on the first request is expected; repeating forever is not.
- 438: stale nonce. A conforming client should retry with a fresh nonce.
- 441: wrong credentials or an invalid expiry.
- 486: allocation quota reached, not an authentication failure.
If the server is behind one-to-one NAT, also set the public relay address correctly:
external-ip=12.34.567.89/10.0.0.4
Replace 10.0.0.4 with the local interface coturn sees. The relayed candidate
must contain the public address. A successful allocation with a private relay candidate
will still fail connectivity checks from the internet.
Check the UDP relay range in both firewalls
TURN authentication and media reachability are different tests. Even after the realm and credential generation are correct, RTP can fail if the relay range is closed.
min-port=49152
max-port=65535
Open that range over UDP in the provider security group and the host firewall. Port 3478 is not the media port. A successful TURN allocation is not proof that the relay path is usable.
Do not enable use-auth-secret and static users at the same time unless there
is a deliberate reason. Pick one credential path, set its options explicitly and test it
with a command-line TURN client before returning to the WebRTC application.
Minimal verification checklist
- Start coturn with an absolute config path.
- Confirm that
Default realmis non-empty. - Generate a future-dated REST credential with the configured secret.
- Use Trickle ICE and confirm that a
relaycandidate appears. - Run a command-line TURN client and read the response code.
- Confirm
external-ipand the UDP relay range from an outside network.
The empty realm is easy to miss because the client sees only the timeout. Fixing the realm does not automatically fix every TURN failure, but it removes the first blocker and makes the later credential and media tests meaningful.
If you want the working server, browser clients and deployment reference in one source package, see Low-Latency Kit.