Skip to content
RustPluginGenerator

OnUserApprove

OnUserApprove is called by Oxide during the client approval step, after the player has authenticated with Steam but before the game finishes approving the connection and before the BasePlayer entity is spawned.

Signature
object OnUserApprove(Network.Connection connection)

Parameters

Hook parameters
ParameterTypeDescription
connectionNetwork.ConnectionThe connecting player's Network.Connection object. It carries the client's Steam id (connection.userid), the auth level bitfield (connection.authLevel) and the network channel (connection.connection) used for messaging before the player entity exists.

Return value

Return null to let the connection proceed to OnUserApproved and the game's normal auth flow. Return a string to kick the player with that string as the reason; return false to reject with Oxide's default "ConnectionRejected" message; return true (or any other non-null, non-string value) to make Oxide accept the connection immediately and skip further approval checks.

How it works

The hook fires inside Oxide's own approval wrapper, not inside a patched game method. It runs once per connection attempt, right after Steam auth and the ban/permission checks, and just before Oxide calls OnUserApproved. Because it runs this early, there is no BasePlayer, no team, no inventory and usually no BasePlayer.activePlayerList entry yet - use connection (and connection.userid) exclusively.

Returning a non-null value stops the wrapper: a return of true lets the connection through, a return of false causes Oxide to reject the connection with its localized "ConnectionRejected" message, and any string return rejects the player and is shown to them as the kick reason. Returning null lets the normal path continue, in which case the outcome is decided by OnUserApproved and the built-in checks.

The hook method must be named OnUserApprove and take exactly one Connection parameter. Oxide dispatches based on the parameter type, so declaring a narrower type is not possible here - Connection is the only valid signature. Network access is available on connection.connection if you need to send the client a message, but be careful: sending to a connection that was just rejected can throw.

Example plugin: Reject players below a Steam auth hint and check your whitelist file

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

namespace Oxide.Plugins
{
    [Info("ConnectionWhitelist", "RustPluginGenerator", "1.0.0")]
    [Description("Rejects connections whose Steam id is not in the allow list.")]
    public class ConnectionWhitelist : RustPlugin
    {
        private const string PermAdmin = "connectionwhitelist.admin";
        private const string DataFile = "ConnectionWhitelist";

        private class PluginConfig
        {
            [JsonProperty("Whitelist enabled")]
            public bool Enabled = false;

            [JsonProperty("Reject message")]
            public string RejectMessage = "This server is whitelisted. Contact an admin.";

            [JsonProperty("Allowed Steam IDs")]
            public HashSet<string> Allowed = new HashSet<string>();
        }

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

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

        private object OnUserApprove(Network.Connection connection)
        {
            if (connection == null) return null;
            if (!config.Enabled) return null;

            string id = connection.userid.ToString();
            if (string.IsNullOrEmpty(id) || id == "0") return null;

            if (config.Allowed.Contains(id)) return null;

            return config.RejectMessage ?? "Not whitelisted.";
        }

        [ConsoleCommand("wl.toggle")]
        private void CmdToggle(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin) { arg.ReplyWith("Admins only."); return; }
            config.Enabled = !config.Enabled;
            SaveConfig();
            arg.ReplyWith($"Whitelist enabled = {config.Enabled}");
        }

        [ConsoleCommand("wl.allow")]
        private void CmdAllow(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin) { arg.ReplyWith("Admins only."); return; }
            string id = arg.GetString(0, "").Trim();
            if (id.Length == 0) { arg.ReplyWith("Usage: wl.allow <steamid>"); return; }
            if (config.Allowed.Add(id)) SaveConfig();
            arg.ReplyWith($"Allowed {id}, count = {config.Allowed.Count}");
        }

        [ConsoleCommand("wl.deny")]
        private void CmdDeny(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin) { arg.ReplyWith("Admins only."); return; }
            string id = arg.GetString(0, "").Trim();
            if (id.Length == 0) { arg.ReplyWith("Usage: wl.deny <steamid>"); return; }
            if (config.Allowed.Remove(id)) SaveConfig();
            arg.ReplyWith($"Removed {id}, count = {config.Allowed.Count}");
        }

        [ChatCommand("wl.list")]
        private void CmdList(BasePlayer player, string command, string[] args)
        {
            if (player == null) return;
            if (!permission.UserHasPermission(player.UserIDString, PermAdmin))
            {
                player.ChatMessage("You do not have permission.");
                return;
            }
            player.ChatMessage($"Whitelist enabled: {config.Enabled}, entries: {config.Allowed.Count}");
        }
    }
}

The plugin keeps a JSON file under oxide/data/ConnectionWhitelist with a master switch, a per-player allow list and an optional reject message. Inside OnUserApprove it checks whether the master switch is off, in which case it returns null and does nothing. Otherwise it looks up connection.userid (stored as a string) in the allow list. A hit returns null so the normal game approval path runs; a miss returns the configured message, which Oxide passes straight to ConnectionAuth.Reject and the client sees as the disconnect reason.

Admins use wl.allow <steamid> and wl.deny <steamid> from the server console (or in-game with wl.allow), and wl.toggle to flip the master switch. The allow list is saved immediately on every change so a crash cannot lose whitelist entries.

Common use cases

  • Private-server whitelist

    Maintain a list of Steam ids that are allowed in and reject everyone else with a readable message. This is the classic use case for OnUserApprove and it works without any player entity being spawned, so there is no cost when a rejected client hammers connect.

  • Maintenance window kick

    Flip a config flag or admin command during a wipe to reject new connections while existing players keep playing. Because the hook fires before the entity spawns, existing players are not disturbed and rejected clients get a clear message.

  • Pre-login abuse checks

    Reject known duplicate or proxy Steam ids before they consume a server slot or trigger OnPlayerConnected, using an allow list you sync from another system.

Pitfalls

  • No BasePlayer yet

    You only have a Connection. Do not call methods on BasePlayer.FindByID or expect the player to be in BasePlayer.activePlayerList; those are not populated until after approval and entity spawn.

  • Returning the wrong truthy value

    Any non-null return short-circuits Oxide's approval wrapper. Returning true accepts the player even if a ban check or permission check would have rejected them, so only return true if you really mean to override the built-in flow.

  • Hook method has to be exactly named

    This hook is called directly by Oxide's approval wrapper, not by a patched game method. If you rename your method or add extra parameters, the callback will simply never fire and rejections will silently fall through.

Build a plugin with OnUserApprove

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

Prompt: Private-server whitelist using the OnUserApprove 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 OnUserApprove

Generate