Authentication and JWT Tokens

Our platform uses JWT (JSON Web Token) authentication to manage user sessions and secure server-to-server interactions. JWTs are digitally signed tokens containing encoded information, allowing systems to verify their authenticity without needing to store session data. Here’s a brief overview and guide for implementing JWT-based authentication.

What is a JWT?

A JWT is a self-contained token that includes encoded information (claims) about the user or system. Each JWT is digitally signed with a secret key using the HS256 algorithm, allowing the server to verify the token’s integrity. In our system, the signature ensures that the token has not been tampered with and was generated by an authorized source.

Token Setup and Security

The JWT secret must always be kept on the server side and should never be exposed to the front-end or any public domain, as leaking this key would allow anyone to forge valid tokens, compromising the security of your application.

  1. Secrets for Signing:

    • For secure production use, we will provide a secret key to generate the JWT. This key will be shared securely.
    • We also provide a staging secret for testing purposes, which is safer to share for development.
  2. Token Payload Requirements:

    • The payload is the core of the JWT, containing essential information about the user. For a valid session token, your payload must include:

      
          
      { "id": "user's unique ID in your system", "username": "public username or handle", "exp": "expiry timestamp as a number (Unix time)" }
    • Example Payload:

      
          
      { "id": "12345", "username": "john_doe", "exp": 1700000000 }
      • id: Unique identifier for the user in your system (non-sensitive).
      • username: Publicly viewable username or handle for the user (non-sensitive).
      • exp: Expiry timestamp, here set to 1700000000 (Unix time), which represents an expiration date.
    • Token Expiry (exp): This field should be a Unix timestamp (type number, not string). It sets the token’s validity period and helps prevent unauthorized access.

  3. Publicly Viewable Data:

    • The id and username fields will be viewable in the front-end. Ensure these fields contain non-sensitive, displayable data. If you lack a username field in your system, contact your Scout PAM or Project Lead to discuss possible solutions Scout can provide and host.

Authentication Flow and Token Use

  1. Token Generation and Use:

    • You will generate the JWT on your server and pass it to our front-end script when the game loads. Once the token is available, our front-end will treat the state as a logged-in user.
    • Our servers will validate the token’s signature, and if valid, it establishes the user’s session.
  2. User Session Verification:

    • If additional session verification is desired, you may include relevant session data in the token. This token can then be validated again in the create-bet callback, ensuring the user session is secure.

Server-to-Server Authentication

For server-to-server callbacks, such as refunds and settling bets, we use a system-signed admin token. This token is generated by our system and should be treated as an administrator-level token:

Implementation Examples

Below are simple implementations for generating a JWT token in different programming languages. These examples use the HS256 algorithm and require a library that supports JWT generation.

Java (using java-jwt)


    
import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; public class JwtExample { public static String createToken(String userId, String username, String secret) { Algorithm algorithm = Algorithm.HMAC256(secret); return JWT.create() .withClaim("id", userId) .withClaim("username", username) .withClaim("exp", 1700000000) // Example expiry timestamp .sign(algorithm); } }

Node.js (using jsonwebtoken)


    
const jwt = require('jsonwebtoken'); function createToken(userId, username, secret) { return jwt.sign( { id: userId, username: username, exp: Math.floor(Date.now() / 1000) + (60 * 60) }, // 1 hour expiry secret ); }

C# (.NET using System.IdentityModel.Tokens.Jwt)


    
using System; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.IdentityModel.Tokens; public class JwtExample { public static string CreateToken(string userId, string username, string secret) { var key = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes(secret)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( claims: new[] { new Claim("id", userId), new Claim("username", username), new Claim("exp", ((DateTimeOffset)DateTime.UtcNow.AddHours(1)).ToUnixTimeSeconds().ToString()) // 1 hour expiry }, signingCredentials: creds ); return new JwtSecurityTokenHandler().WriteToken(token); } }

Python (using PyJWT)


    
import jwt import time def create_token(user_id, username, secret): payload = { 'id': user_id, 'username': username, 'exp': int(time.time()) + 3600 # 1 hour expiry } return jwt.encode(payload, secret, algorithm='HS256')

Security Best Practices

  1. Token Expiry: Always set an expiry date on tokens (exp claim) to ensure sessions are time-limited.
  2. Signature Verification: Our platform will verify the token’s signature on each request. Verifying the signature on your end ensures that each token is valid and untampered.
  3. Separate Tokens for Staging and Production: Only use the staging secret for development. Production environments should always use the production secret shared securely by Scout.
  4. Store the secret securely: The JWT secret must always be kept on the server side and should never be exposed to the front-end or any public domain, as leaking this key would allow anyone to forge valid tokens, compromising the security of your application.

By following these steps, JWT tokens provide a secure, efficient means of user authentication and session management on our platform.

Here’s the section on Refreshing the Token on User Activity:


Updating the Token to Prevent Expiry (Front end)

To ensure that user sessions remain active, it’s important to refresh the JWT token when user activity is detected near the token’s expiration. By updating the token periodically, you prevent it from going stale and avoid unnecessary logouts or session interruptions.

Exposing a Refresh Function

If you expose a refresh function to our front-end script, we can trigger it whenever the token is near expiry. This function should handle all necessary steps to obtain a fresh token, then return the new token back to our script.

Example Refresh Function in JavaScript

Here’s a simple example of how you might implement this function on the client side, using JavaScript:


    
// Expose a function named refreshToken that fetches a new token window.refreshToken = async function () { try { // Example call to your server's endpoint to get a fresh token const response = await fetch('https://your-server.com/api/refresh-token', { method: 'POST', credentials: 'include', // Include cookies if needed for authentication headers: { 'Content-Type': 'application/json' } }); if (!response.ok) throw new Error('Token refresh failed'); const data = await response.json(); return data.token; // Assuming the new token is in data.token } catch (error) { console.error('Error refreshing token:', error); return null; // Handle failure, possibly triggering a logout } };

How It Works

  1. Token Expiry Check: Our front-end will check if the token is close to expiring. If so, it will call the refreshToken function.
  2. Server Call: This function makes a POST request to your server to obtain a new token.
  3. Return Token: The function returns the new token to the front-end, which can continue using it without requiring a full logout.

Implementing a refresh flow like this ensures user sessions remain active and reduces friction, enhancing the overall user experience.