Skip to content
RustPluginGenerator

CanClientLogin

CanClientLogin is called when a client connection reaches the approval stage, before the player is fully connected and before OnUserApprove runs. Returning a string rejects the connection with that message; returning a value of any other non-null type lets it through.

Signature
object CanClientLogin(Network.Connection connection)

Parameters

Hook parameters
ParameterTypeDescription
connectionNetwork.ConnectionThe network connection of the client trying to join. Use connection.userid for the Steam ID, connection.username for the display name, connection.ipaddress for the raw IP and connection.authLevel for the Steam auth level (0, 1 or 2).

Return value

Return null to let the normal check continue into CanUserLogin and OnUserApproved. Return a string to reject the connection with that message as the kick reason. Return false to reject with the generic connection-rejected message; returning true or any other non-string non-null value skips this hook's rejection path and the client is approved.

How it works

Oxide fires this hook from its own RustCore.IOnUserApprove handler, which executes during the connection approval handshake after the permission system has already updated the user's nickname and default groups. It runs once per connection attempt, so it is a cheap place to make a join decision. The connection argument is the only source of information: connection.userid (Steam ID) and connection.username identify the player, connection.ipaddress is the raw address (strip the port yourself if you need the bare IP), and connection.authLevel tells you whether Steam authenticated the client as an admin. Returning a string rejects the client and shows that exact text to them. Returning false rejects with the default connection-rejected message; returning anything else non-null, including true, counts as approval. Returning null defers to CanUserLogin/OnUserApproved. Because the game has not created a BasePlayer yet, you cannot use player objects here, and the hook fires for every attempt, including reconnects after a timeout.

Example plugin: Ban known IPs and required-name check at login

LoginGate.cs
using Network;
using System.Linq;
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Oxide.Core;

namespace Oxide.Plugins
{
    [Info("LoginGate", "RustPluginGenerator", "1.0.0")]
    [Description("Rejects connections based on IP address, Steam auth level or username rules.")]
    public class LoginGate : RustPlugin
    {
        private class PluginConfig
        {
            [JsonProperty("Blocked IP addresses")]
            public List<string> BlockedIps = new List<string>();

            [JsonProperty("Require Steam auth (authLevel >= 1)")]
            public bool RequireSteamAuth = true;

            [JsonProperty("Minimum username length")]
            public int MinNameLength = 3;

            [JsonProperty("Maximum username length")]
            public int MaxNameLength = 32;
        }

        private PluginConfig config;

        protected override void LoadDefaultConfig() => config = new PluginConfig();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<PluginConfig>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Configuration file is invalid, using defaults");
                LoadDefaultConfig();
            }
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["BlockedIp"] = "Your IP address is not permitted on this server.",
                ["SteamRequired"] = "You must be signed in to Steam to join this server.",
                ["BadName"] = "Your name does not meet this server's name policy."
            }, this);
        }

        private string Msg(string key, string userId = null, params object[] args) => string.Format(lang.GetMessage(key, this, userId), args);

        private object CanClientLogin(Connection connection)
        {
            if (connection == null) return null;

            string rawIp = connection.ipaddress ?? string.Empty;
            int colon = rawIp.IndexOf(':');
            string ip = colon >= 0 ? rawIp.Substring(0, colon) : rawIp;
            string userId = connection.userid.ToString();

            if (config.BlockedIps != null && config.BlockedIps.Contains(ip))
            {
                return Msg("BlockedIp", userId);
            }

            if (config.RequireSteamAuth && connection.authLevel == 0)
            {
                return Msg("SteamRequired", userId);
            }

            string name = connection.username ?? string.Empty;
            if (name.Length < config.MinNameLength || name.Length > config.MaxNameLength)
            {
                return Msg("BadName", userId);
            }

            return null;
        }
    }
}

The plugin stores a config with a list of blocked IP addresses and an optional required name fragment. CanClientLogin runs while the connection is still pending, so it reads connection.ipaddress (with the port removed) and connection.username and returns a rejection string when a rule matches. Because the rejection string is shown directly to the client, it is kept short and generic. The hook is cheap and does not touch any player entity, so there is no risk of null references. Config load validates the object and rewrites defaults if the file is corrupted. No data files or timers are needed, so Unload is empty.

Common use cases

  • IP-based access list

    Reject connections from addresses on a block list, or restrict the server to a whitelist of trusted IPs during a lockdown. Use connection.ipaddress with the port removed before comparing.

  • Steam auth level requirement

    Force connection.authLevel >= 1 so clients without a valid Steam session cannot join. Return a string explaining that Steam login is required before the player can connect.

  • Custom join name policy

    Block connections whose username contains disallowed characters, is empty, or exceeds a maximum length. The check happens before any player object exists, so it is fast and side-effect free.

Pitfalls

  • Returning true silently approves

    If you return true from this hook, the approval path is considered handled and the client is approved. That can bypass your own downstream checks if you also use CanUserLogin — return null when you want to allow normal processing instead.

  • No BasePlayer is available

    This hook runs before the player entity is created. connection.userid and connection.username are the only identifiers; calling BasePlayer.FindByID will return null, and permission lookups should use the Steam ID string from the connection.

  • Port is part of the IP string

    connection.ipaddress typically includes the port (for example 1.2.3.4:56789). Split on : before comparing against an IP list, otherwise every entry will fail to match.

Build a plugin with CanClientLogin

Describe what should happen and get a compiled plugin that uses this hook.

Prompt: IP-based access list using the CanClientLogin hook, with a config file and a permission
Generate this plugin

Edit the prompt on the next screen. 2 free plugins a month, no card.

Last updated Oct 26, 2026. Found a mistake? Email support@rustplugingenerator.com.

Build a plugin with CanClientLogin

Generate