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.
object CanUserLogin(string playerName, string connectionId, string connectionIp)Parameters
| Parameter | Type | Description |
|---|---|---|
playerName | string | The display name the connecting client sent (connection.username). May contain formatting characters and is not sanitized by the game before this hook. |
connectionId | string | The Steam64 ID of the connecting player as a string (connection.userid.ToString()), useful for permission checks and data lookups. |
connectionIp | string | The 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
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
connectionIpagainst 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
connectionIdand 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
playerNamecomes 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.
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