Skip to content
RustPluginGenerator

OnUserPermissionGranted

OnUserPermissionGranted is called by Oxide after a permission has been added directly to a player and only when the player did not already have it. It fires from Permission.GrantUserPermission, the same method behind the `oxide.grant user` command and the permission.GrantUserPermission API.

Signature
void OnUserPermissionGranted(string playerId, string permission)

Parameters

Hook parameters
ParameterTypeDescription
playerIdstringThe Steam ID of the player who received the permission, as a string. Use it with BasePlayer.FindByID or BasePlayer.FindAwakeOrSleeping to reach the player object.
permissionstringThe full permission string that was granted, for example `mytool.use` or `admin`. It is the name as registered with Oxide, not the group it came from.

Return value

The original method returns void and the hook return value is ignored, so returning anything has no effect on the grant. The permission is already stored before the hook is called; you cannot cancel or veto it here.

How it works

The hook fires at the end of Permission.GrantUserPermission once the permission string is in the player's permission set. Oxide returns early when the player already had the permission, so granting a duplicate does not call the hook again. It also is not called for group grants; group changes go through OnUserGroupAdded and OnGroupPermissionGranted instead. Because the call comes from Oxide itself rather than a patched game method, it fires for every source: the oxide.grant user and grantperm console commands, other plugins calling permission.GrantUserPermission, and framework code. Both arguments are plain strings, so a player may be offline or absent from BasePlayer.activePlayerList when the hook runs; resolve the player lazily with BasePlayer.FindByID if you need the object. There is only one signature, both parameters are strings, and declaring both exactly as given is the safe choice since no object argument exists to narrow. Common real uses are syncing external permission systems, auditing who granted what, and reacting with a chat notice or UI refresh.

Example plugin: Grant a starter kit when a player receives a specific permission

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

namespace Oxide.Plugins
{
    [Info("Permission Starter Kit", "RustPluginGenerator", "1.0.0")]
    [Description("Gives a configured kit once to every player who receives a specific permission.")]
    public class PermissionStarterKit : RustPlugin
    {
        private const string PermUse = "permissionstarterkit.use";

        private PluginConfig config;
        private StoredData data;

        private class PluginConfig
        {
            [JsonProperty("Permission that triggers the kit")]
            public string TriggerPermission = "starterkit.use";

            [JsonProperty("Kit items (shortname -> amount)")]
            public Dictionary<string, int> KitItems = new Dictionary<string, int>
            {
                ["wood"] = 500,
                ["stone"] = 500,
                ["bandage"] = 5
            };

            [JsonProperty("Send a chat confirmation")]
            public bool NotifyPlayer = true;
        }

        private class StoredData
        {
            [JsonProperty("Players who already received the kit")]
            public HashSet<string> Received = new HashSet<string>();
        }

        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>
            {
                ["KitGiven"] = "You received your starter kit: {0} items.",
                ["AlreadyReceived"] = "You already received the starter kit."
            }, 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(PermUse, this);
            LoadData();
        }

        private void LoadData()
        {
            data = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name) ?? new StoredData();
            if (data.Received == null) data.Received = new HashSet<string>();
        }

        private void Unload()
        {
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);
        }

        private void OnUserPermissionGranted(string playerId, string permissionName)
        {
            if (string.IsNullOrEmpty(playerId) || string.IsNullOrEmpty(permissionName)) return;
            if (!permissionName.Equals(config.TriggerPermission, StringComparison.OrdinalIgnoreCase)) return;
            if (!permission.UserHasPermission(playerId, PermUse)) return;
            if (data.Received.Contains(playerId)) return;

            var player = BasePlayer.FindByID(ulong.Parse(playerId));
            if (player == null || !player.IsConnected) return;

            data.Received.Add(playerId);
            Interface.Oxide.DataFileSystem.WriteObject(Name, data);

            var given = 0;
            foreach (var entry in config.KitItems)
            {
                if (entry.Value <= 0) continue;
                var item = ItemManager.CreateByName(entry.Key, entry.Value);
                if (item == null) continue;
                player.GiveItem(item);
                given += entry.Value;
            }

            if (config.NotifyPlayer)
                player.ChatMessage(Msg("KitGiven", player.UserIDString, given));
        }
    }
}

The plugin watches for a configurable permission (default starterkit.use) being granted to a player. When the granted permission matches, the plugin checks that the player still holds the plugin's own permission permissionstarterkit.use, resolves the player by Steam ID, and gives them the configured kit through player.GiveItem only if the player is connected. Each player is recorded in the plugin's data file so the kit is only handed out once, even if the permission is revoked and granted again. An optional chat confirmation is localized through the lang file. The config exposes the trigger permission string and the kit items so an admin can retarget it without recompiling.

Common use cases

  • Sync permissions to an external rank system

    Push the granted permission into an Economy or Discord reference plugin's API so the rank shown outside Rust matches Oxide. Check the reference plugin for null before calling.

  • Log privileged grants for auditing

    Record every grant of admin-level permissions with the player ID, permission string and epoch time to a data file so staff changes can be reviewed later.

  • Refresh a permission-driven UI

    When a permission that unlocks a menu or shop tier is granted, rebuild that player's CUI panel so buttons appear without requiring a reconnect.

Pitfalls

  • Group grants do not fire this hook

    Adding a player to a group that already has a permission does not call OnUserPermissionGranted. Handle OnUserGroupAdded and OnGroupPermissionGranted separately if your logic must cover both paths.

  • The player may be offline

    Both arguments are strings and the hook runs without a player object, so BasePlayer.FindByID can return null. Always guard against that before touching inventory or sending chat.

  • Duplicate grants are silent

    Oxide returns before the hook call when the player already holds the permission, so code that assumes one call per command will not see repeat grants. Do not rely on this hook for idempotency checks.

Build a plugin with OnUserPermissionGranted

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

Prompt: Sync permissions to an external rank system using the OnUserPermissionGranted 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 OnUserPermissionGranted

Generate