> For the complete documentation index, see [llms.txt](https://kinematicsoup.gitbook.io/reactor/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kinematicsoup.gitbook.io/reactor/tutorials/authentication.md).

# Authentication

## Summary

This tutorial demonstrates how to send authentication arguments to the server when you connect, how an OnAuthenticate handler can be registered on the server to authenticate connecting players, and how to send arbitrary data back to the client in the server's authentication response. In this tutorial a random number is sent to players when they pass authentication, and an error message is sent when they fail. Clients will pass or fail authentication depending on which button they press to connect.

### Requirements

* [Reactor Requirements](/reactor/overview.md#requirements)
* Completion of [Tutorial 1](/reactor/tutorials/basics.md)

## Setup

### Create a Room

1. Right click in the hierarachy window and create a **'Reactor/Room'** game object.
2. Uncheck the 'Connect On Start' checkbox in the *ksConnect* inspector. This will prevent the script from connecting automatically when the scene is loaded. We will instead create a script to connect when a button is pressed.

## Scripting

First we will create a client script to connect when a button is pressed, and handle authentication respones. Then we will create a\* server room script to handle player authentication.

### Create a ConnectHandler Monobehaviour

This script has event handlers for the *ksConnect* component's `OnGetRooms` and `OnConnect` events. It will attempt to connect to the server when the users presses one of the '0'-'2' keys, and disconnect when 'd' is pressed. The `OnGetRooms` event allows developers to control which room the client connects to and pass optional authentication arguments to the server. This version of the script connects to the first room available and passes a different argument depending on which number key was pressed. The `OnConnect` event is fired when a connection attempt completes, even if the connection was not successful, allowing you to handle connection errors. This script logs value of the first optional *ksMultiType* the server authentication handler returned in `ksAuthenticationResult.Data` if the connection was succesful, and otherwise logs a connection failure warning depending on the `ksAuthenticationResult.Code` returned by the authentication handler.

> *Just like RPCs, the `Connect` method can take any number of optional* ksMultiType *arguments. These arguments are passed to any* *`Room.OnAuthenticate` handler functions in server room scripts.*

1. Create a new *MonoBehaviour* named 'ConnectHandler'.
2. Attach it to the 'Room' game object.
3. Edit the file to match the code below.
4. Select the *ksConnect* component on the room and add the 'ConnectHandler' *'OnGetRooms'* method to the *ksConnect* *'OnGetRooms'* event list.
   * Click the '+' button in *ksConnect* *'OnGetRooms'* event list.
   * Drag the Room instance into the new object field.
   * Select *'ConnectHandler->OnGetRooms'* in the function selector.
5. Add the 'ConnectHandler' *'OnConnect'* method to the *ksCOnnect* *'OnConnect'* event list.

*ConnectHandler.cs*

```c#
using UnityEngine;
using KS.Reactor.Client.Unity;
using KS.Reactor.Client;
using KS.Reactor;

public class ConnectHandler : MonoBehaviour
{
    private int m_connectArg;

    // Connect with a different authentication argument when 0-2 are pressed, and disconnect when D is pressed.
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Alpha0))
        {
            Connect(0);
        }
        else if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            Connect(1);
        }
        else if (Input.GetKeyDown(KeyCode.Alpha2))
        {
            Connect(2);
        }
        else if (Input.GetKeyDown(KeyCode.D))
        {
            // This will disconnect from all connected rooms.
            ksReactor.Service.Disconnect();
        }
    }

    private void Connect(int connectArg)
    {
        // Store the connect arg so we can connect with it from OnGetRooms.
        m_connectArg = connectArg;

        // Begin the connection process the ksConnect compone is configured for (local, online, or remote).
        GetComponent<ksConnect>().BeginConnect();
    }

    // Handler for ksConnect.OnGetRooms events.
    public void OnGetRooms(ksConnect.GetRoomsEvent e)
    {
        if (e.Status != ksBaseRoom.ConnectStatus.SUCCESS)
        {
            ksLog.Error(this, "Error fetching rooms: " + e.Error);
        }
        else if (e.Rooms == null || e.Rooms.Count == 0)
        {
            ksLog.Warning(this, "No rooms found.");
        }
        else
        {
            // The caller is the ksConnect script instance which invoked this event handler.
            // Connect with the connect arg, unless it is 0, then connect with no arguments.
            if (m_connectArg == 0)
            {
                e.Caller.Connect(e.Rooms[0]);
            }
            else
            {
                e.Caller.Connect(e.Rooms[0], m_connectArg);
            }
        }
    }

    // Handler for ksConnect.OnConnect events.
    public void OnConnect(ksConnect.ConnectEvent e)
    {
        if (e.Status == ksBaseRoom.ConnectStatus.SUCCESS)
        {
            // We connected successfully. Log the first value in Result.Data the server sent, if any.
            if (e.Result.Data.Length > 0)
            {
                ksLog.Info("Connected. Server sent result value " + e.Result.Data[0]);
            }
            else
            {
                ksLog.Info("Connected. Server did not send any result data.");
            }
        }
        // The first constructor parameter of ksAuthenticationResult can be accessed from e.Result.Code. If it is 1,
        // we log a warning that the number of authentication arguments was wrong.
        else if (e.Result.Code == 1)
        {
            ksLog.Warning(this, "Authentication failed: wrong number of authentication arguments.");
        }
        // We don't log other connection errors because the ksConnect component already logs connection errors.
    }
}
```

### Create a Server Authentication Room Script

The server room script registers a `Room.OnAuhthenticate` handler that passes authentication if it was called with a single argument with the value 1. It sends a random number in the `ksAuthenticationResult.Data` to players who pass authentication. If the wrong number of arugments were sent, it fails authentication with `ksAuthenticationResult.Code` 1, and if any argument value other than 1 was sent, it fails with `ksAuthenticationResult.Code` 2 and sends an error message in the `ksAuthenticationResult.Data`.

> *Authentication handlers are async methods that return a* Task, *so you can await external asynchronous authentication calls. For more information, see* [*here*](https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/)*. Authentication handlers cannot be added or removed after the room is loaded and* Initialize *is called on the initial scripts.*

> *The* ksAsyncResult *constructor takes a uint code parameter and an optional number of* ksMultiTypes. *Authentication fails if the uint code is non-zero. The* ksAsyncResult *is sent to the client and can be accessed in the* Room.OnConnect *event. The code and* ksMultiTypes *are logged by the* ksConnect *script when authentication fails.* *If there are multiple authentication handler functions and authentication fails,* *the* ksAsyncResult *sent to the client will be from the first authentication function that failed. If all authentication functions pass, the* ksMultiType *arguments from all* *authentication functions are combined into one array and sent to the client.*

> *It is possible that `OnPlayerJoin` and `OnPlayerLeave` events may not fire for a player who passes authentication if they disconnect during the* *authentication process. You should not set any persistent state in an authentication handler such as user-login state tracking and rely on* *'OnPlayerLeave' to clear that state.*

1. Select the 'Room' object.
2. In the **'Add Component'** menu, select **'Reactor->New Server Room Script'**.
3. Name the script 'ServerAuthenticationRoom'.

*ServerAuthenticationRoom.cs*

```c#
using System.Threading;
using System.Threading.Tasks;
using KS.Reactor.Server;
using KS.Reactor;

public class ServerAuthenticatorRoom : ksServerRoomScript
{
    private ksRandom m_rand = new ksRandom();

    // Called after all other scripts on all entities are attached.
    public override void Initialize()
    {
        Room.OnAuthenticate += Authenticate;
    }
    
    // Called when the script is detached.
    public override void Detached()
    {
        Room.OnAuthenticate -= Authenticate;
    }
    
    
    // Authenticates a player. Return a non-zero result to fail authentication.
    private async Task<ksAuthenticationResult> Authenticate(ksIServerPlayer player, ksMultiType[] args, CancellationToken cancellationToken)
    {
        // args contains the arguments we passed to ksRoom.Connect().
        if (args.Length != 1)
        {
            return new ksAuthenticationResult(1);
        }
        if (args[0] == 1)
        {
            // Pass authentication immediately if the argument is 1, and return a random integer in the result data.
            return new ksAuthenticationResult(0, m_rand.Next());
        }
        // Fail authentication with an error message in the result data for all other argument values.
        // We await a completed Task to prevent a compiler warning that the async method never awaits anything and
        // will be completed synchronously.
        return await Task.FromResult(new ksAuthenticationResult(2, "Unknown auth arg: " + args[0]));
    }
}
```

## Testing

1. Build your scene config (**CTRL + F2**).
2. Start a local server.
3. Enter play mode.
4. Press **1**. You should see a message in the Unity logs `"Connected. Server sent result value X"` where X is a random number.
5. Press **2**. You should get a `ROOM_INITIALIZE_ERROR` connection failure warning. This happens when you try to connect to the same room twice.
6. Press **D** to disconnect.
7. Press **2**. You should get a connection failure warning with `"AUTH_ERR_USER_DEFINED` (code: 2, data: \[Unknown auth arg: 2])"\`.
8. Press **0**. You should get a warning `"Authentication failed: wrong number of authentication arguments."`
