Skip to main content
JWT Authentication lets you control who can publish and subscribe to your Red5 Pro streams using JSON Web Tokens (JWTs). Your backend issues a signed token for each authenticated user; the client passes that token when connecting to Red5 Pro, which validates it locally using a shared secret — with no external calls during the stream. This approach scales naturally and integrates with any existing identity provider or authentication service.

How it works

1

User authenticates with your backend

Your application handles user login however you prefer — password, OAuth, SSO, or any other mechanism.
2

Your backend issues a signed JWT

After validating the user, your backend generates a JWT signed with the shared secret and returns it to the client over HTTPS.
3

Client passes the token to Red5 Pro

When the client connects to publish or subscribe, it includes the JWT as a connection parameter.
4

Red5 Pro validates the token locally

Red5 Pro verifies the cryptographic signature, checks expiration, and evaluates any role, transport, or room claims — all without contacting your backend.
Because validation is local, JWT Authentication adds no latency to stream operations. It is ideal for scaled and distributed deployments.

Prerequisites

Before configuring JWT Authentication, you need:
  • The red5pro-simple-auth-plugin installed on your Red5 Pro server (present by default)
  • A JWT-issuing service or identity provider
  • A shared secret key of at least 32 bytes, used by both your issuing service and Red5 Pro

Step 1 — Configure the application

Add the jwtAuthenticator and simpleAuthSecurity beans to your application’s context file. For the live application, that file is RED5_HOME/webapps/live/WEB-INF/red5-web.xml. These beans are already present in the file but commented out — uncomment and adjust them.
<!-- RED5_HOME/webapps/live/WEB-INF/red5-web.xml -->

<bean id="jwtAuthenticator"
      class="com.red5pro.server.plugin.simpleauth.datasource.impl.jwt.JwtAuthenticator"
      init-method="initialize">
    <property name="adapter"       ref="web.handler" />
    <property name="context"       ref="web.context" />
    <property name="jwtSecret"     value="${jwt.secret}" />
    <property name="jwtTtlMinutes" value="${jwt.ttl.minutes}" />
    <property name="expectedIssuer" value="${jwt.issuer}" />
</bean>

<bean id="simpleAuthSecurity"
      class="com.red5pro.server.plugin.simpleauth.Configuration"
      depends-on="jwtAuthenticator">
    <property name="active"                     value="${auth.active}" />
    <property name="rtmp"                       value="${auth.rtmp}" />
    <property name="rtsp"                       value="${auth.rtsp}" />
    <property name="rtc"                        value="${auth.rtc}" />
    <property name="srt"                        value="${auth.srt}" />
    <property name="mpegts"                     value="${auth.mpegts}" />
    <property name="rtmpAllowQueryParamsEnabled" value="${auth.rtmpAllowQueryParamsEnabled}" />
    <property name="allowedRtmpAgents"           value="*" />
    <property name="validator"                   ref="jwtAuthenticator" />
</bean>

Step 2 — Set properties

Add the following section to RED5_HOME/webapps/live/WEB-INF/red5-web.properties. The property values are substituted into red5-web.xml at runtime.
# RED5_HOME/webapps/live/WEB-INF/red5-web.properties

# Authentication on/off per protocol
auth.active=true
auth.rtmp=true
auth.rtsp=true
auth.rtc=true
auth.srt=false
auth.mpegts=false
auth.rtmpAllowQueryParamsEnabled=true

# JWT settings
jwt.secret=changeme-to-a-strong-32-byte-secret-key
jwt.ttl.minutes=60

# Optional: if set, tokens must have a matching iss claim
jwt.issuer=
Replace jwt.secret with a cryptographically random value of at least 32 bytes. This secret must match the secret your token-issuing service uses to sign JWTs. Never commit it to version control.

Configuration properties reference

Core (simpleAuthSecurity bean)
PropertyTypeDescription
activeBooleanEnables or disables authentication for the application
rtmpBooleanRequires a valid JWT for RTMP connections
rtspBooleanRequires a valid JWT for RTSP connections
rtcBooleanRequires a valid JWT for WebRTC connections
srtBooleanRequires a valid JWT for SRT connections
mpegtsBooleanRequires a valid JWT for MPEG-TS connections
rtmpAllowQueryParamsEnabledBooleanAllows RTMP clients to pass the token in the URL query string
allowedRtmpAgentsStringSemicolon-separated list of allowed RTMP agent strings; * allows all
Authenticator (jwtAuthenticator bean)
PropertyTypeDescription
jwtSecretStringShared secret for validating JWT signatures
jwtTtlMinutesLongMaximum allowed token lifetime in minutes; tokens with a longer exp are rejected
expectedIssuerStringOptional: if set, tokens must have a matching iss claim

Step 3 — Generate tokens in your backend

Your backend service signs JWTs with the shared secret using the HS256 algorithm. Never generate tokens on the client side or embed the secret in client code.
const jwt = require('jsonwebtoken');

const payload = {
  sub: 'user123',                          // recommended: identifies the user
  iss: 'your-auth-service',               // required if jwt.issuer is set
  roles: 'red5-publisher,red5-subscriber', // controls publish/subscribe access
  'red5-transport': 'WHIP,WHEP',           // optional: restrict to WebRTC only
  'red5-room': 'room1',                    // optional: restrict to a specific room
  exp: Math.floor(Date.now() / 1000) + (60 * 60)  // 1 hour; must not exceed jwtTtlMinutes
};

const token = jwt.sign(payload, process.env.RED5_JWT_SECRET, { algorithm: 'HS256' });

Token examples for common scenarios

Issue this to a broadcaster who should only be able to publish, not subscribe.
const payload = {
  sub: 'broadcaster42',
  roles: 'red5-publisher',
  'red5-transport': 'WHIP',
  exp: Math.floor(Date.now() / 1000) + 3600
};
Issue this to a viewer who should only be able to subscribe, not publish.
const payload = {
  sub: 'viewer99',
  roles: 'red5-subscriber',
  'red5-transport': 'WHEP',
  exp: Math.floor(Date.now() / 1000) + 3600
};
Restrict the token to a specific room within the application scope.
const payload = {
  sub: 'user789',
  roles: 'red5-publisher,red5-subscriber',
  'red5-room': 'conference',
  exp: Math.floor(Date.now() / 1000) + 3600
};
Use a 60-second expiry for highly sensitive access scenarios.
const payload = {
  sub: 'tempuser',
  roles: 'red5-subscriber',
  exp: Math.floor(Date.now() / 1000) + 60
};
Set jwt.ttl.minutes=2 on the server to tolerate up to 60 seconds of clock drift while still rejecting overly generous tokens.

JWT claims reference

Standard claims

ClaimRequiredDescription
expYesExpiration timestamp (Unix seconds). Must be in the future and within jwtTtlMinutes.
subRecommendedSubject (username). Stored as a connection attribute for logging and application use.
issConditionalIssuer. Required only if expectedIssuer is configured on the server. Case-sensitive.
nameOptionalUser’s full name. Stored as a connection attribute if present.

Red5 Pro-specific claims

ClaimRequiredDescription
rolesOptionalComma-separated roles. Recognized values: red5-publisher, red5-subscriber. If absent, both roles are allowed.
red5-transportOptionalComma-separated allowed protocols: RTMP, RTSP, WHIP, WHEP. Case-insensitive. If absent, any protocol is allowed.
red5-roomOptionalComma-separated allowed room names (sub-scopes under the application). If absent, any room is allowed.
For the stream path live/room1/stream1, the room name is room1. If you set red5-room, clients cannot access application-level streams that have no room prefix.
Role matching is case-sensitive. Use red5-publisher and red5-subscriber in lowercase. If your identity provider adds its own roles to the token (e.g., admin, editor), include the Red5 Pro roles alongside them:
{
  "roles": "admin,editor,red5-publisher,red5-subscriber"
}

Step 4 — Pass the token from your client

Clients send the JWT as the token parameter in their connection configuration. Set username and password to any non-empty value (conventionally "jwt").
var baseConfiguration = {
  host: 'your-server.com',
  app: 'live',
  iceServers: iceServers,
  bandwidth: desiredBandwidth,
  connectionParams: {
    username: 'jwt',
    password: 'jwt',
    token: jwtToken   // the JWT string from your backend
  }
};
Pass the complete JWT string — header.payload.signature — without a Bearer prefix in the token parameter.

Security best practices

The shared secret is the foundation of JWT security. Anyone who possesses it can mint valid tokens.
  • Store it in a secrets manager (HashiCorp Vault, AWS Secrets Manager, etc.)
  • Never commit it to version control
  • Use environment variables or files with restricted permissions
  • Rotate the secret on a regular schedule, coordinating the rotation between Red5 Pro and your issuing service
Generate a strong secret:
# OpenSSL
openssl rand -base64 32

# Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
Short-lived tokens limit the impact of token theft.
  • Standard interactive sessions: 15–60 minutes
  • High-security or near-one-time access: 60 seconds
  • Configure jwt.ttl.minutes slightly higher than your token TTL to tolerate clock drift between servers
  • Send tokens from your backend to clients over HTTPS only
  • Use RTMPS, RTSPS, or WebRTC (encrypted by default) for the streaming connection
  • Never log or expose token strings in debug output or error messages
Set jwt.issuer on the server to prevent tokens issued by other services from being accepted by Red5 Pro.

When to use JWT Authentication

Good fits

  • Production deployments with many users
  • Microservices or API-gateway architectures that already issue JWTs
  • Single Sign-On integrations (Auth0, Okta, AWS Cognito, Azure AD)
  • Multi-tenant systems using room or issuer claims for isolation
  • Applications requiring per-user role control (publisher vs. subscriber)
  • Scenarios needing low-latency, stateless authentication

Poor fits

  • Simple deployments with a small, fixed set of users — use Simple Authentication instead
  • Situations with no JWT-issuing service available
  • When you need real-time validation against live user state — use Round-Trip Authentication instead