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); } } }