using FishNet.Authenticating;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Transporting;
using System;
using UnityEngine;
namespace FishNet.Example.Authenticating
{
///
/// This is an example of a password authenticator.
/// Never send passwords without encryption.
///
public class PasswordAuthenticator : HostAuthenticator
{
#region Public.
///
/// Called when authenticator has concluded a result for a connection. Boolean is true if authentication passed, false if failed.
/// Server listens for this event automatically.
///
public override event Action OnAuthenticationResult;
#endregion
#region Serialized.
///
/// Password to authenticate.
///
[Tooltip("Password to authenticate.")]
[SerializeField]
private string _password = "HelloWorld";
#endregion
public override void InitializeOnce(NetworkManager networkManager)
{
base.InitializeOnce(networkManager);
//Listen for connection state change as client.
base.NetworkManager.ClientManager.OnClientConnectionState += ClientManager_OnClientConnectionState;
//Listen for broadcast from client. Be sure to set requireAuthentication to false.
base.NetworkManager.ServerManager.RegisterBroadcast(OnPasswordBroadcast, false);
//Listen to response from server.
base.NetworkManager.ClientManager.RegisterBroadcast(OnResponseBroadcast);
}
///
/// Called when a connection state changes for the local client.
///
private void ClientManager_OnClientConnectionState(ClientConnectionStateArgs args)
{
/* If anything but the started state then exit early.
* Only try to authenticate on started state. The server
* doesn't have to send an authentication request before client
* can authenticate, that is entirely optional and up to you. In this
* example the client tries to authenticate soon as they connect. */
if (args.ConnectionState != LocalConnectionState.Started)
return;
//Authentication was sent as host, no need to authenticate normally.
if (AuthenticateAsHost())
return;
PasswordBroadcast pb = new()
{
Password = _password
};
base.NetworkManager.ClientManager.Broadcast(pb);
}
///
/// Received on server when a client sends the password broadcast message.
///
/// Connection sending broadcast.
///
private void OnPasswordBroadcast(NetworkConnection conn, PasswordBroadcast pb, Channel channel)
{
/* If client is already authenticated this could be an attack. Connections
* are removed when a client disconnects so there is no reason they should
* already be considered authenticated. */
if (conn.IsAuthenticated)
{
conn.Disconnect(true);
return;
}
bool correctPassword = (pb.Password == _password);
SendAuthenticationResponse(conn, correctPassword);
/* Invoke result. This is handled internally to complete the connection or kick client.
* It's important to call this after sending the broadcast so that the broadcast
* makes it out to the client before the kick. */
OnAuthenticationResult?.Invoke(conn, correctPassword);
}
///
/// Received on client after server sends an authentication response.
///
///
private void OnResponseBroadcast(ResponseBroadcast rb, Channel channel)
{
string result = (rb.Passed) ? "Authentication complete." : "Authenitcation failed.";
NetworkManager.Log(result);
}
///
/// Sends an authentication result to a connection.
///
private void SendAuthenticationResponse(NetworkConnection conn, bool authenticated)
{
/* Tell client if they authenticated or not. This is
* entirely optional but does demonstrate that you can send
* broadcasts to client on pass or fail. */
ResponseBroadcast rb = new()
{
Passed = authenticated
};
base.NetworkManager.ServerManager.Broadcast(conn, rb, false);
}
///
/// Called after handling a host authentication result.
///
/// Connection authenticating.
/// True if authentication passed.
protected override void OnHostAuthenticationResult(NetworkConnection conn, bool authenticated)
{
SendAuthenticationResponse(conn, authenticated);
OnAuthenticationResult?.Invoke(conn, authenticated);
}
}
}