Core components
Networking & multiplayer infra...
User connection and authorization
21 min
overview the user connection and authorization system is a critical component of the ir engine's multiplayer infrastructure that manages how players connect to game instances and verifies their permissions it ensures that only authenticated users with appropriate authorization can access specific game environments by implementing robust security checks and connection management, this system protects game instances from unauthorized access while providing legitimate players with a seamless connection experience this chapter explores the implementation, workflow, and security aspects of user connection and authorization within the ir engine core concepts authentication and authorization the system implements two distinct security processes authentication verifying the identity of a user validates that users are who they claim to be processes authentication tokens or credentials confirms the legitimacy of the connection request establishes the user's identity within the system rejects invalid or expired credentials authorization determining if a user has permission to access a specific resource checks if the authenticated user can join a particular instance enforces access control rules and restrictions verifies instance specific permissions handles capacity limitations and user bans ensures appropriate access levels these processes work together to create a secure multiplayer environment connection management once authenticated and authorized, user connections must be properly managed connection establishment creating network connections between clients and servers channel assignment adding users to appropriate communication channels state synchronization ensuring the client and server have consistent state connection monitoring tracking the status and health of connections disconnection handling managing graceful disconnections and timeouts effective connection management ensures stable and reliable multiplayer experiences implementation connection request handling the system processes incoming connection requests from clients // simplified from src/socketfunctions ts function setupsocketfunctions(app) { const logger = app get('logger'); // create a network server to handle connections const server = new networkserver(); // handle new connection attempts server on('connection', (spark) => { logger info(`new connection attempt from ${spark address ip}`); // set up event handlers for this connection spark on('data', async (message) => { try { // parse the incoming message const data = json parse(message); // handle authentication request if (data type === 'authenticate') { await handleauthenticationrequest(app, spark, data); } // handle other message types // } catch (error) { logger error(`error processing message ${error message}`); spark write(json stringify({ status 'error', message 'invalid message format' })); } }); // handle disconnection spark on('end', () => { handledisconnection(app, spark); }); }); return server; } this function creates a network server to handle client connections sets up event handlers for new connections processes incoming messages from clients routes authentication requests to the appropriate handler manages disconnection events authentication process the authentication process verifies the identity of connecting users // simplified from src/channels ts async function handleauthenticationrequest(app, spark, data) { const logger = app get('logger'); const { token, peerid, instanceid } = data; try { // validate the authentication token const authresult = await app service('authentication') strategies jwt authenticate( { accesstoken token }, {} ); // extract user information from the authentication result const identityproviderpath = app get('authentication') entity; const userid = authresult\[identityproviderpath] userid; logger info(`user ${userid} authenticated successfully`); // store user information with the connection spark userid = userid; spark peerid = peerid; spark instanceid = instanceid; // proceed to authorization await authorizeuserforinstance(app, spark, userid, instanceid); } catch (error) { logger error(`authentication failed ${error message}`); // send authentication failure response spark write(json stringify({ status 'error', message 'authentication failed', code 'invalid token' })); // close the connection spark end(); } } this function extracts the token, peer id, and instance id from the request validates the authentication token using the authentication service retrieves the user id from the authentication result associates the user information with the connection proceeds to the authorization step if authentication succeeds sends an error response and closes the connection if authentication fails authorization process the authorization process checks if authenticated users can access specific instances // simplified from src/networkfunctions ts async function authorizeuserforinstance(app, spark, userid, instanceid) { const logger = app get('logger'); try { // get instance details const instance = await app service('instance') get(instanceid); if (!instance) { throw new error(`instance ${instanceid} not found`); } // get user details const user = await app service('user') get(userid); if (!user) { throw new error(`user ${userid} not found`); } // check if the user is banned const banquery = { query { userid userid, active true } }; const bans = await app service('ban') find(banquery); if (bans total > 0) { throw new error(`user ${userid} is banned`); } // check if the instance is private and if the user is authorized if (instance isprivate) { const authquery = { query { instanceid instanceid, userid userid } }; const authorizations = await app service('instance authorization') find(authquery); if (authorizations total === 0) { throw new error(`user ${userid} not authorized for private instance ${instanceid}`); } } // check if the instance is full const currentusers = await app service('user instance') find({ query { instanceid instanceid } }); if (currentusers total >= instance maxusers) { throw new error(`instance ${instanceid} is full`); } logger info(`user ${userid} authorized for instance ${instanceid}`); // proceed to connection establishment await establishconnection(app, spark, user, instance); } catch (error) { logger error(`authorization failed ${error message}`); // send authorization failure response spark write(json stringify({ status 'error', message error message, code 'authorization failed' })); // close the connection spark end(); } } this function retrieves instance and user details from their respective services checks if the user is banned from the system verifies authorization for private instances ensures the instance has not reached its capacity proceeds to connection establishment if authorization succeeds sends an error response and closes the connection if authorization fails connection establishment after successful authentication and authorization, the connection is established // simplified from src/networkfunctions ts async function establishconnection(app, spark, user, instance) { const logger = app get('logger'); try { // add the connection to the instance channel app channel(`instanceids/${instance id}`) join(spark); // record the user's presence in the instance await app service('user instance') create({ userid user id, instanceid instance id, peerid spark peerid }); // get webrtc router capabilities for this instance const instanceserverstate = getstate(instanceserverstate); const router = instanceserverstate routers\[0]; // simplified const routerrtpcapabilities = router rtpcapabilities; // send success response with necessary connection information spark write(json stringify({ status 'success', message 'connection established', routerrtpcapabilities, user { id user id, name user name }, instance { id instance id, name instance name } })); // notify other users in the instance about the new connection app service('message') create({ type 'user joined', instanceid instance id, userid user id }); logger info(`user ${user id} successfully connected to instance ${instance id}`); } catch (error) { logger error(`connection establishment failed ${error message}`); // send connection failure response spark write(json stringify({ status 'error', message 'failed to establish connection', code 'connection failed' })); // close the connection spark end(); } } this function adds the connection to the appropriate instance channel records the user's presence in the instance retrieves webrtc router capabilities for the instance sends a success response with necessary connection information notifies other users in the instance about the new connection handles any errors that occur during connection establishment disconnection handling the system manages user disconnections from instances // simplified from src/channels ts async function handledisconnection(app, spark) { const logger = app get('logger'); // check if this was an authenticated connection if (!spark userid || !spark instanceid) { logger info(`unauthenticated connection from ${spark address ip} disconnected`); return; } try { const userid = spark userid; const instanceid = spark instanceid; logger info(`user ${userid} disconnected from instance ${instanceid}`); // remove the user from the instance channel app channel(`instanceids/${instanceid}`) leave(spark); // remove the user's presence record await app service('user instance') remove(null, { query { userid userid, instanceid instanceid } }); // notify other users in the instance about the disconnection app service('message') create({ type 'user left', instanceid instanceid, userid userid }); // check if the instance is now empty const remainingusers = await app service('user instance') find({ query { instanceid instanceid } }); // if the instance is empty and configured for auto shutdown, initiate shutdown if (remainingusers total === 0) { const instance = await app service('instance') get(instanceid); if (instance autoshutdownwhenempty) { logger info(`instance ${instanceid} is empty, initiating shutdown`); await app service('instance server') shutdown(instanceid); } } } catch (error) { logger error(`error handling disconnection ${error message}`); } } this function identifies the disconnecting user and instance removes the user from the instance channel updates the user's presence record in the database notifies other users about the disconnection checks if the instance is now empty initiates instance shutdown if appropriate connection workflow the complete user connection workflow follows this sequence sequencediagram participant client as game client participant server as ir engine server participant auth as authentication service participant db as database services client >>server connection request (token, instanceid) server >>auth validate token alt invalid token auth >>server authentication failed server >>client error invalid token server >>server close connection else valid token auth >>server authentication successful (userid) server >>db get user and instance details server >>db check user bans alt user is banned server >>client error user is banned server >>server close connection else user is not banned server >>db check instance authorization alt not authorized for instance server >>client error not authorized server >>server close connection else authorized for instance server >>db check instance capacity alt instance is full server >>client error instance full server >>server close connection else instance has capacity server >>server add to instance channel server >>db record user presence server >>client success connection established server >>db notify other users end end end end note over client,server active connection client >>server disconnection server >>db remove user presence server >>db notify other users server >>db check if instance empty alt instance empty and auto shutdown enabled server >>server initiate instance shutdown end this diagram illustrates the client sends a connection request with authentication token the server validates the token with the authentication service if authentication fails, the connection is rejected if authentication succeeds, the server checks authorization the server verifies the user is not banned the server checks instance specific authorization the server ensures the instance has capacity if all checks pass, the connection is established when the client disconnects, the server updates records if the instance becomes empty, shutdown may be initiated integration with other components the user connection and authorization system integrates with several other components of the multiplayer infrastructure instance lifecycle management the connection system interacts with instance lifecycle management // simplified integration with instance lifecycle async function updateinstanceforuser(app, userid, instanceid) { const instanceserverstate = getstate(instanceserverstate); // check if the instance is already initialized on this server if (!instanceserverstate instance || instanceserverstate instance id !== instanceid) { // initialize the instance if needed await initializeinstance(app, instanceid); } // ensure the instance is ready if (!instanceserverstate ready) { throw new error(`instance ${instanceid} is not ready`); } // update instance user count await app service('instance') patch(instanceid, { currentusers { $inc 1 } }); } this integration ensures the instance is initialized before user connection verifies the instance is in a ready state updates instance statistics when users connect maintains the relationship between users and instances mediasoup webrtc the connection system integrates with the webrtc communication backbone // simplified integration with mediasoup async function setupwebrtcforuser(app, spark, userid, instanceid) { // get the router for this instance const instanceserverstate = getstate(instanceserverstate); const router = instanceserverstate routers\[0]; // simplified // send router capabilities to the client spark write(json stringify({ type 'webrtc router capabilities', routerrtpcapabilities router rtpcapabilities })); // set up event handlers for webrtc transport creation spark on('data', (message) => { const data = json parse(message); if (data type === 'webrtc transport create') { handlewebrtctransportcreate(app, spark, data); } else if (data type === 'webrtc transport connect') { handlewebrtctransportconnect(app, spark, data); } else if (data type === 'webrtc producer create') { handlewebrtcproducercreate(app, spark, data); } else if (data type === 'webrtc consumer create') { handlewebrtcconsumercreate(app, spark, data); } }); } this integration provides webrtc router capabilities to connected clients sets up event handlers for webrtc related requests enables real time communication for authenticated users manages the webrtc components for each connection feathersjs services the connection system uses feathersjs services for data operations // simplified integration with feathersjs services async function validateuseraccess(app, userid, instanceid) { // check if the user exists const user = await app service('user') get(userid); // check if the instance exists const instance = await app service('instance') get(instanceid); // check if the user is banned const bans = await app service('ban') find({ query { userid userid, active true } }); if (bans total > 0) { throw new error('user is banned'); } // check instance specific authorization if (instance isprivate) { const authorizations = await app service('instance authorization') find({ query { instanceid instanceid, userid userid } }); if (authorizations total === 0) { throw new error('not authorized for this instance'); } } return { user, instance }; } this integration uses feathersjs services to access user and instance data queries ban records to enforce system wide restrictions checks instance specific authorization records leverages the service architecture for consistent data access benefits of user connection and authorization the user connection and authorization system provides several key advantages security ensures only legitimate users can access game instances access control enforces instance specific permissions and restrictions identity verification confirms the identity of connecting users resource protection prevents unauthorized access to game resources capacity management controls the number of users in each instance moderation support enforces bans and other administrative actions connection tracking maintains records of user connections and disconnections these benefits make the user connection and authorization system an essential component for creating secure and controlled multiplayer experiences next steps with an understanding of how users connect to game instances, the next chapter explores the underlying application framework that powers these services next feathersjs application & services docid\ d73ap4fr2 gpncbxjp36u