JWT Authentication
The JWT Authentication system enables secure player access to tournament games through cryptographically signed tokens. Game Platform validates these tokens to authenticate players and authorize their participation in tournaments.
JWT Token Overview
JWT tokens are issued by the Tournament System when players register for tournaments and contain all necessary information for game platform authentication and authorization.
Token Structure
JWT tokens follow the standard format: header.payload.signature
Example Token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9.eyJ0b3VybmFtZW50SWQiOiIxMjM0NSIsInBsYXllcklkIjoidHBfcGxheWVyXzQ1NiIsInJvb21JZCI6InJvb21fYWJjMTIzIiwiZ2FtZVNsdWciOiJjcmFzaC1jbGFzc2ljIiwicGVybWlzc2lvbnMiOlsicGxheSIsInJlYnV5Il0sImlhdCI6MTY0MjA4OTAwMCwiZXhwIjoxNjQyMDkyNjAwfQ.signature
Token Payload
{
"tournamentId" : "12345" ,
"playerId" : "tp_player_456" ,
"roomId" : "room_abc123" ,
"gameSlug" : "crash-classic" ,
"permissions" : [ "play" , "rebuy" , "chat" ],
"operatorId" : "op_1234567890abcdef" ,
"playerInfo" : {
"displayName" : "PlayerName" ,
"entryCount" : 2 ,
"totalPaid" : 20.00 ,
"vipLevel" : "gold"
},
"iat" : 1642089000 ,
"exp" : 1642092600
}
Token Claims
Claim Type Description tournamentIdString Tournament identifier playerIdString Tournament-specific player ID roomIdString Game room identifier gameSlugString Game type identifier permissionsArray Allowed actions (play, rebuy, chat, spectate) operatorIdString Operator identifier for multi-tenant isolation playerInfoObject Player metadata and statistics iatNumber Token issued at timestamp expNumber Token expiration timestamp
Token Validation API
Validate JWT Token
Validate player JWT tokens for game access authorization:
POST /internal/auth/validate-token
Authorization : Bearer {internal-api-key}
Content-Type : application/json
{
"token" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJFZERTQSJ9..."
}
Response (Valid Token):
{
"valid" : true ,
"tournamentId" : "12345" ,
"playerId" : "tp_player_456" ,
"roomId" : "room_abc123" ,
"gameSlug" : "crash-classic" ,
"permissions" : [ "play" , "rebuy" , "chat" ],
"operatorId" : "op_1234567890abcdef" ,
"playerInfo" : {
"displayName" : "PlayerName" ,
"entryCount" : 2 ,
"totalPaid" : 20.00 ,
"currentPosition" : 15 ,
"points" : 1250 ,
"status" : "active" ,
"vipLevel" : "gold" ,
"preferredLanguage" : "en"
},
"tournamentInfo" : {
"name" : "Daily Championship" ,
"status" : "in_progress" ,
"currentRound" : 3 ,
"totalRounds" : 5 ,
"startTime" : "2024-01-15T20:00:00Z"
},
"expiresAt" : "2024-01-15T21:30:00Z" ,
"remainingTime" : 3420
}
Response (Invalid Token):
{
"valid" : false ,
"error" : "token_expired" ,
"message" : "JWT token has expired" ,
"expiredAt" : "2024-01-15T21:30:00Z"
}
Token Validation Errors
Error Code Description Action Required token_expiredToken past expiration time Request new token from Casino System token_malformedInvalid token format Check token format and encoding signature_invalidCryptographic signature failed Token may be tampered - reject access tournament_not_foundTournament doesn’t exist Verify tournament is still active player_eliminatedPlayer eliminated from tournament Allow spectate mode only insufficient_permissionsToken lacks required permissions Check permission requirements
Authentication Flow
Player Connection Process
Implementation Examples
JavaScript Token Validation
class TournamentAuth {
constructor ( internalApiKey ) {
this . apiKey = internalApiKey ;
this . baseUrl = 'https://tournament-system.internal' ;
}
async validateToken ( jwtToken ) {
try {
const response = await fetch ( ` ${ this . baseUrl } /internal/auth/validate-token` , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ this . apiKey } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({ token: jwtToken })
});
if ( ! response . ok ) {
throw new Error ( `Validation failed: ${ response . status } ` );
}
const result = await response . json ();
if ( ! result . valid ) {
console . warn ( 'Token validation failed:' , result . error );
return null ;
}
return {
tournamentId: result . tournamentId ,
playerId: result . playerId ,
roomId: result . roomId ,
permissions: result . permissions ,
playerInfo: result . playerInfo ,
expiresAt: new Date ( result . expiresAt )
};
} catch ( error ) {
console . error ( 'Token validation error:' , error );
return null ;
}
}
async authenticatePlayer ( jwtToken ) {
const validation = await this . validateToken ( jwtToken );
if ( ! validation ) {
throw new Error ( 'Authentication failed - invalid token' );
}
// Check if player has required permissions
if ( ! validation . permissions . includes ( 'play' )) {
throw new Error ( 'Authentication failed - insufficient permissions' );
}
// Check token expiration
if ( validation . expiresAt <= new Date ()) {
throw new Error ( 'Authentication failed - token expired' );
}
console . log ( `Player ${ validation . playerInfo . displayName } authenticated for tournament ${ validation . tournamentId } ` );
return validation ;
}
}
// Usage example
const auth = new TournamentAuth ( 'internal-api-key' );
// Authenticate player on connection
const authenticatePlayerConnection = async ( jwtToken ) => {
try {
const playerAuth = await auth . authenticatePlayer ( jwtToken );
// Player authenticated successfully
return {
success: true ,
playerId: playerAuth . playerId ,
tournamentId: playerAuth . tournamentId ,
roomId: playerAuth . roomId ,
playerInfo: playerAuth . playerInfo ,
permissions: playerAuth . permissions
};
} catch ( error ) {
console . error ( 'Player authentication failed:' , error . message );
return {
success: false ,
error: error . message
};
}
};
Node.js Middleware Example
// Express.js middleware for JWT authentication
const authenticatePlayer = async ( req , res , next ) => {
try {
// Extract JWT from Authorization header or query parameter
const token = req . headers . authorization ?. replace ( 'Bearer ' , '' ) || req . query . token ;
if ( ! token ) {
return res . status ( 401 ). json ({
error: 'missing_token' ,
message: 'JWT token is required'
});
}
// Validate token with Tournament System
const validation = await auth . validateToken ( token );
if ( ! validation ) {
return res . status ( 401 ). json ({
error: 'invalid_token' ,
message: 'JWT token validation failed'
});
}
// Add player info to request object
req . player = {
tournamentId: validation . tournamentId ,
playerId: validation . playerId ,
roomId: validation . roomId ,
permissions: validation . permissions ,
info: validation . playerInfo
};
// Check tournament-specific permissions
if ( req . path . includes ( '/rebuy' ) && ! validation . permissions . includes ( 'rebuy' )) {
return res . status ( 403 ). json ({
error: 'insufficient_permissions' ,
message: 'Rebuy permission required'
});
}
next ();
} catch ( error ) {
console . error ( 'Authentication middleware error:' , error );
return res . status ( 500 ). json ({
error: 'authentication_error' ,
message: 'Authentication system error'
});
}
};
// Usage in routes
app . get ( '/game/tournament/:id/join' , authenticatePlayer , ( req , res ) => {
// Player is authenticated, req.player contains player info
const { tournamentId , playerId , roomId } = req . player ;
res . json ({
message: 'Game joined successfully' ,
tournamentId: tournamentId ,
playerId: playerId ,
roomId: roomId
});
});
Token Refresh & Expiration
Token Expiration Handling
JWT tokens have limited validity periods. Handle expiration gracefully:
class TokenManager {
constructor ( auth ) {
this . auth = auth ;
this . tokenCache = new Map ();
this . refreshThreshold = 5 * 60 * 1000 ; // 5 minutes before expiration
}
async getValidToken ( playerId , currentToken ) {
const cached = this . tokenCache . get ( playerId );
// Check if cached token is still valid
if ( cached && cached . expiresAt > new Date ( Date . now () + this . refreshThreshold )) {
return cached . token ;
}
// Validate current token
const validation = await this . auth . validateToken ( currentToken );
if ( validation && validation . expiresAt > new Date ( Date . now () + this . refreshThreshold )) {
// Token is still valid, cache it
this . tokenCache . set ( playerId , {
token: currentToken ,
expiresAt: validation . expiresAt
});
return currentToken ;
}
// Token expired or expiring soon - request refresh
return await this . requestTokenRefresh ( playerId );
}
async requestTokenRefresh ( playerId ) {
try {
// Request new token from Casino System
// This would typically involve redirecting player back to casino
// or making an API call if refresh tokens are supported
console . log ( `Token refresh required for player ${ playerId } ` );
// For now, indicate token refresh needed
throw new Error ( 'TOKEN_REFRESH_REQUIRED' );
} catch ( error ) {
console . error ( 'Token refresh failed:' , error );
throw error ;
}
}
}
Automatic Token Validation
// WebSocket connection with automatic token validation
class TournamentSocket {
constructor ( wsUrl , jwtToken ) {
this . wsUrl = wsUrl ;
this . jwtToken = jwtToken ;
this . auth = new TournamentAuth ( 'internal-api-key' );
this . reconnectAttempts = 0 ;
this . maxReconnectAttempts = 5 ;
}
async connect () {
try {
// Validate token before connecting
const validation = await this . auth . validateToken ( this . jwtToken );
if ( ! validation ) {
throw new Error ( 'Invalid JWT token' );
}
// Connect to WebSocket with validated token
this . ws = new WebSocket ( ` ${ this . wsUrl } ?token= ${ this . jwtToken } ` );
this . ws . onopen = () => {
console . log ( 'Tournament WebSocket connected' );
this . reconnectAttempts = 0 ;
};
this . ws . onmessage = ( event ) => {
this . handleMessage ( JSON . parse ( event . data ));
};
this . ws . onclose = ( event ) => {
console . log ( 'Tournament WebSocket closed:' , event . code , event . reason );
// Handle different close codes
if ( event . code === 1008 ) { // Policy violation (auth failure)
console . error ( 'Authentication failed - token may be invalid' );
this . handleAuthFailure ();
} else {
this . attemptReconnect ();
}
};
this . ws . onerror = ( error ) => {
console . error ( 'Tournament WebSocket error:' , error );
};
} catch ( error ) {
console . error ( 'Tournament connection failed:' , error );
throw error ;
}
}
handleAuthFailure () {
// Redirect back to casino for new token
window . location . href = '/casino/tournament-login?expired=true' ;
}
attemptReconnect () {
if ( this . reconnectAttempts < this . maxReconnectAttempts ) {
this . reconnectAttempts ++ ;
const delay = Math . min ( 1000 * Math . pow ( 2 , this . reconnectAttempts ), 30000 );
console . log ( `Attempting reconnect in ${ delay } ms (attempt ${ this . reconnectAttempts } )` );
setTimeout (() => {
this . connect ();
}, delay );
} else {
console . error ( 'Max reconnection attempts exceeded' );
this . handleAuthFailure ();
}
}
}
Security Considerations
Token Security Best Practices
Signature Verification Always verify JWT signatures using EdDSA algorithm with proper key validation
Expiration Checking Validate token expiration times and reject expired tokens immediately
Permission Validation Check token permissions match required actions before allowing access
Secure Transmission Only transmit tokens over HTTPS/WSS encrypted connections
Common Security Issues
Never trust client-side token validation - always validate tokens server-side via the internal API. Client-side validation should only be used for UX improvements.
Token Tampering Detection
// Always validate tokens server-side
const validateClientToken = async ( clientToken ) => {
// DON'T do client-side validation only
// const payload = JSON.parse(atob(clientToken.split('.')[1])); // INSECURE
// DO validate via internal API
const validation = await auth . validateToken ( clientToken );
if ( ! validation ) {
throw new Error ( 'Token validation failed' );
}
return validation ;
};
Replay Attack Prevention
// Track token usage to prevent replay attacks
class TokenTracker {
constructor () {
this . usedTokens = new Set ();
this . cleanupInterval = setInterval (() => {
this . cleanup ();
}, 60000 ); // Cleanup every minute
}
isTokenUsed ( tokenId ) {
return this . usedTokens . has ( tokenId );
}
markTokenUsed ( tokenId , expiresAt ) {
this . usedTokens . add ({
id: tokenId ,
expiresAt: expiresAt
});
}
cleanup () {
const now = new Date ();
this . usedTokens = new Set ([ ... this . usedTokens ]. filter (
token => token . expiresAt > now
));
}
}
Next Steps