Skip to content
RustPluginGenerator

CanUserLogin

CanUserLogin is called by Oxide during a player's connection approval, after permission groups are updated and before OnUserApprove, and lets a plugin reject the connection by returning a string or false.

Signature
object CanUserLogin(string playerName, string connectionId, string connectionIp)

Parameters

Hook parameters
ParameterTypeDescription
playerNamestringThe display name the connecting client sent (connection.username). May contain formatting characters and is not sanitized by the game before this hook.
connectionIdstringThe Steam64 ID of the connecting player as a string (connection.userid.ToString()), useful for permission checks and data lookups.
connectionIpstringThe client IP address with the port portion stripped by Oxide's regex. Use it for bans, allowlists, or logging.

Return value

Returning null or true lets the login proceed. Returning a string causes ConnectionAuth.Reject with that text as the reason, and returning false rejects with Oxide's default connection-rejected message. Any non-null return short-circuits the approval path, so OnUserApprove and OnUserApproved no longer run for that connection.

How it works

Oxide invokes CanUserLogin from its RustCore.IOnUserApprove path, which runs while a client is still being approved. By the time it fires, Oxide has already updated the player's nickname and group membership (default players, plus administrators for auth level 2 and above) and registered the player with Covalence, so permission queries against connectionId are valid inside the hook. It fires before OnUserApprove and before OnPlayerConnected.

playerName is the raw username string, connectionId is the Steam ID as text, and connectionIp is the port-stripped address. Use them for name blacklists, IP/range bans, region or VPN blocking, whitelists, and pre-login account checks.

Returning a non-null string rejects the connection with that string as the kick reason; returning false rejects with the framework's default "ConnectionRejected" message. Returning null or true lets the connection continue.

The same connection also triggers CanClientLogin, and if that hook returns a value it takes priority over your result, so a rejection here can be overridden by another plugin. Declaring all three parameters as string is the only supported signature.

Example plugin: Reject connections whose name contains blocked text

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

namespace Oxide.Plugins
{
    [Info("Login Name Filter", "RustPluginGenerator", "1.0.0")]
    [Description("Rejects connections whose display name contains blocked fragments, unless the IP is allowed")]
    public class LoginNameFilter : RustPlugin
    {
        private const string PermBypass = "loginnamefilter.bypass";

        private PluginConfig config;

        private class PluginConfig
        {
            [JsonProperty("Blocked name fragments (lowercase)")]
            public List<string> BlockedFragments = new List<string> { "discord.gg", "server name" };

            [JsonProperty("Allowed IP addresses")]
            public List<string> AllowedIps = new List<string>();

            [JsonProperty("Rejection message")]
            public string Rejection = "Your name contains blocked text: {0}";
        }

        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>
            {
                ["Rejected"] = "You cannot join with this name."
            }, this);
        }

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

        private void Init()
        {
            permission.RegisterPermission(PermBypass, this);
        }

        private object CanUserLogin(string playerName, string connectionId, string connectionIp)
        {
            if (string.IsNullOrEmpty(playerName)) return null;
            if (config.AllowedIps != null && connectionIp != null && config.AllowedIps.Contains(connectionIp)) return null;
            if (permission.UserHasPermission(connectionId, PermBypass)) return null;

            var name = playerName.ToLowerInvariant();
            foreach (var fragment in config.BlockedFragments)
            {
                if (string.IsNullOrEmpty(fragment)) continue;
                if (name.Contains(fragment.ToLowerInvariant()))
                {
                    return Msg("Rejected", connectionId) + " (" + fragment + ")";
                }
            }
            return null;
        }
    }
}

The plugin keeps two config lists: a set of name fragments that cause a rejection, and IP addresses that are always allowed to bypass the filter. In CanUserLogin the name is lowercased and compared against each fragment; if one matches and the IP is not allowed, the hook returns a message with the matched fragment so the player knows why they were refused. Messages are stored in the language file with placeholders for the name and fragment. Config is loaded with a safe fallback to defaults, so a corrupt file never stops the plugin from loading and enforcing the list.

Common use cases

  • Block impersonation and advertising names

    Reject players whose display name contains slurs, server names, or Discord invite fragments before they ever reach the world.

  • IP allowlist and ban enforcement

    Compare connectionIp against stored ranges or an external ban list and return a reason string so rejected players understand the block.

  • VIP or membership gate

    Check a permission or a data file using connectionId and reject non-members with a message pointing to the server store.

Pitfalls

  • CanClientLogin can override your result

    Oxide uses the value from CanClientLogin if it is non-null, so a second plugin can let a player through even when CanUserLogin rejects them. Keep login logic in one place where possible.

  • The name is client-supplied

    playerName comes from the connecting client and may include rich text tags or unusual characters. Sanitize or normalize it before matching, and never echo it back raw into chat or UI.

  • A non-null return skips later hooks

    Returning a rejection string prevents OnUserApprove and OnUserApproved from running, so any setup those hooks perform will not happen for rejected players.

Build a plugin with CanUserLogin

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

Prompt: Block impersonation and advertising names using the CanUserLogin 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 CanUserLogin

Generate