How Remote Access Is Authenticated in VoiceStudio: JWT Ticket Flow Explained
VoiceStudio authenticates remote access by issuing short-lived JSON Web Token (JWT) tickets via the /api/auth/ws-ticket endpoint, which clients must present as a ws_ticket query parameter to establish authorized WebSocket connections.
VoiceStudio (debpalash/VoiceStudio) secures remote API interactions and real-time streams through a token-based authentication architecture. The system generates signed, ephemeral credentials that bind client identities to persistent authorization records, ensuring every remote request is verified against revocable session authorities according to the source code.
WebSocket Ticket Authentication Flow
Remote access in VoiceStudio relies on a two-step ticket mechanism that separates initial authentication from connection establishment.
Ticket Generation at /api/auth/ws-ticket
When a client requires access to protected WebSocket streams or RPC endpoints, it first POSTs to the /api/auth/ws-ticket route. The server validates the existing session or API key, then mints a JWT containing the user identity (sub claim), allowed scopes, and a short expiration time (typically 300 seconds).
In backend/api/routers/auth.py, the @router.post("/ws-ticket") handler implements this logic:
@router.post("/ws-ticket")
async def create_ws_ticket(payload: TicketRequest, request: Request):
user = await authenticate_user(request) # verify session / API key
token = jwt.encode(
{"sub": user.id, "exp": time.time() + 300},
settings.SECRET_KEY,
algorithm="HS256",
)
return {"ws_ticket": token}
The resulting ws_ticket is a signed string that serves as a temporary capability token.
Ticket Presentation and Validation
To open a WebSocket, the client includes the ticket as a ws_ticket query parameter or transmits it as a cookie during the handshake. The server validates the JWT signature against settings.SECRET_KEY and checks the expiration claim before authorizing the connection.
The speech_platform.py router references this flow through the websocket_ticket_endpoint constant, which points clients to the ticket generation route. Once validated, the server binds the connection to a persistent authority record stored in the session, which is checked on every subsequent RPC call to ensure the caller retains valid permissions.
Implementation Files in VoiceStudio
The authentication flow is implemented across dedicated router modules and verified by integration tests:
backend/api/routers/auth.py: Contains the POST handler that mints signed JWTs and defines the ticket payload schemas.backend/api/routers/speech_platform.py: Exposes the WebSocket endpoint constant (websocket_ticket_endpoint) and enforces ticket validation on incoming connections.tests/test_auth_session_api.py: Validates the end-to-end flow, ensuring that requests lacking a validws_ticketare rejected and that properly signed tickets grant access.
Practical Code Examples
Below are runnable snippets demonstrating the client flow and server configuration.
Requesting a Ticket (Client)
import requests
resp = requests.post(
"https://voice.studio/api/auth/ws-ticket",
json={"scopes": ["ws"]},
cookies={"session": "your_session_cookie"}
)
ticket = resp.json()["ws_ticket"]
Establishing an Authenticated WebSocket (Client)
import websockets
import asyncio
async def connect_stream():
ws_url = f"wss://voice.studio/api/ws?ws_ticket={ticket}"
async with websockets.connect(ws_url) as ws:
await ws.send("hello")
response = await ws.recv()
print(response)
asyncio.run(connect_stream())
Server-Side Ticket Verification
The server extracts the ws_ticket from the incoming request, decodes it using the shared secret, and loads the corresponding user authority:
from jose import jwt
def verify_ticket(token: str):
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
return payload["sub"] # user_id bound to authority
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Ticket expired")
Session Lifecycle and Revocation
Once a ticket is consumed, the associated authority record governs the session lifecycle. VoiceStudio supports explicit revocation—when a user logs out or an API key rotates, the authority is invalidated in the store. Subsequent requests using the same ticket signature fail validation, forcing the client to request a fresh ticket from /api/auth/ws-ticket. This design ensures that compromised or expired credentials cannot sustain long-lived unauthorized access.
Summary
- VoiceStudio uses a JWT-based ticket system for remote access authentication.
- Clients obtain short-lived
ws_tickettokens by POSTing to/api/auth/ws-ticketimplemented inbackend/api/routers/auth.py. - Tickets are presented as query parameters or cookies during WebSocket handshakes, validated against
settings.SECRET_KEYusing the HS256 algorithm. - Valid tickets bind the connection to a persistent authority record checked on every RPC call.
- The flow is tested in
tests/test_auth_session_api.pyand integrated into the speech platform viabackend/api/routers/speech_platform.py.
Frequently Asked Questions
How long is a VoiceStudio WebSocket ticket valid?
Tickets are configured with a 300-second (5-minute) expiration by default, as set by the exp claim in backend/api/routers/auth.py. Clients must establish their connection within this window or request a new ticket.
Can I use a session cookie instead of a query parameter for the ticket?
While the primary mechanism uses the ws_ticket query parameter, the authentication middleware also checks for the ticket value in cookies. However, the query parameter approach is recommended for WebSocket compatibility across different client libraries.
What happens if a ticket is stolen or leaked?
Because tickets are short-lived and bound to specific user authorities, the exposure window is limited. Additionally, server-side revocation of the underlying authority record immediately invalidates active sessions derived from that ticket, forcing re-authentication via the /api/auth/ws-ticket endpoint.
Where is the secret key for signing tickets configured?
The JWT signing key is referenced as settings.SECRET_KEY in backend/api/routers/auth.py. This value is typically loaded from environment variables or a secrets manager specified in the VoiceStudio configuration files.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →