Skip to content

Authorization and Access

In this guide, you will:

  • Understand the importance of authorization and access control in smart contract security.
  • Learn how to define and apply AccessRules for components and their methods.
  • Discover how to set permissions for resources, including minting, burning, and metadata access.
  • Explore identity management and the different types of callers in the Tari Ootle environment.
  • Follow best practices for securing templates and managing digital assets.

Authorization and access control are critical aspects of smart contract security. The Tari Ootle Template Library provides flexible mechanisms to define who can execute methods on your Components and interact with your Resources.

Access rules determine which identities (Components, users, or other templates) are allowed to perform certain actions. They are typically defined when a Component is created or a Resource is minted.

The AccessRules struct is used to configure these permissions.

When you create a Component, you can specify AccessRules for its methods.

use tari_template_lib::prelude::*;
#[template]
mod template {
pub struct MyComponent {
// Component state fields
}
pub fn create_with_access_rules(
admin_badge: ResourceAddress,
) -> ComponentAddress {
let component = MyComponent { /* ... */ }; // Your component instance
let component_address = Component::new(component)
.with_access_rules(ComponentAccessRules::new()
// Allow anyone to call the 'public_method'
.method("public_method", rule!(allow_all))
// Only allow the 'admin_component' to call the 'admin_method'
.method("admin_method", rule!(require(admin_badge)])
// Default rule: anyone can call methods not
// explicitly listed. Without a default rule it
// defaults to `DenyAll` — only the owner can call.
.default(rule!(allow_all))
)
.create_component("my_secured_component", component);
component_address
}
}
  • ComponentAccessRules::new(): Creates a new set of component access rules.
  • .method("method_name", AccessRule): Defines an access rule for a specific method.
  • AccessRule::AllowAll: Allows any caller to execute the method.
  • AccessRule::Restricted(vec![identity1, identity2, ...]): Only allows callers whose identity matches one of the provided identities. Identities can be ComponentAddress, ResourceAddress, TemplateAddress, etc.
  • AccessRule::DenyAll: Denies all callers from executing the method.
  • AccessRule::Owner: Only allows the owner of the component to execute the method. (Note: “owner” is a specific concept often tied to the initial deployer or a designated resource holder).
  • default_access_rule(AccessRule): Sets the default rule for any method not explicitly defined.

Resources can also have access rules, typically governing who can mint, burn, recall, freeze, or update metadata. Each builder method takes two arguments:

  1. The [AccessRule] that gates the action right now.
  2. An [UpdateRule] that gates who, if anyone, can later change that access rule. The updater is fixed at creation time and cannot itself be changed afterwards — this lets holders trust a Locked rule will stay locked forever.
use tari_template_lib::prelude::*;
#[template]
mod template {
pub struct MyComponent {
resource: ResourceManager,
}
impl MyComponent {
pub fn create_with_restricted_resource(
minter_badge: NonFungibleAddress,
) -> ResourceAddress {
let resource_address = ResourceBuilder::public_fungible()
.with_token_symbol("RTK")
.with_divisibility(10)
.initial_supply(amount!["1000000000000000000000"])
// Only the minter badge can mint, and only the owner may
// change who can mint later.
.mintable(rule!(non_fungible(minter_badge)), OWNER)
// Nobody can burn, and the rule is locked forever.
.burnable(rule!(deny_all), LOCKED)
.build();
MyComponent { resource: resource_address.into() }.create()
}
}
}

Each ResourceBuilder exposes one method per action. All accept (rule, updater):

  • .mintable(rule, updater) — who can mint new units of this resource.
  • .burnable(rule, updater) — who can burn units.
  • .recallable(rule, updater) — who can recall units from any external vault.
  • .freezable(rule, updater) — who can freeze a vault holding this resource.
  • .withdrawable(rule, updater) — who can withdraw this resource from vaults.
  • .depositable(rule, updater) — who can deposit this resource into vaults.
  • .update_metadata(rule, updater) — who can change the metadata (the token symbol always stays immutable).
  • .update_non_fungible_data(rule, updater) — who can change the mutable data on individual NFTs (non-fungible only).

UpdateRule controls who can later change an access rule, and has three variants:

UpdateRuleConstantBehaviour
UpdateRule::LockedLOCKEDNobody — not even the resource owner — can change the rule.
UpdateRule::OwnerOWNEROnly the resource owner (per the OwnerRule) can change the rule.
UpdateRule::AccessRulen/aAnyone satisfying the inner AccessRule can change the rule. No implicit owner override.

Why Locked matters — it is the only way to make a trust promise to holders that cannot be quietly retracted later. If recall is DenyAll but the updater is Owner, the owner can flip recall back on after users have already accepted the token; the same goes for freeze, mint (inflation), withdraw, and so on. Issuing a token with the recall/freeze/mint rules Locked is what lets a user verify, by reading the substate once, that those powers can never be granted to anyone — not even the issuer. Use LOCKED for any property you want holders to be able to rely on indefinitely, and reserve OWNER (or a more permissive updater) for rules you genuinely expect to govern over time.

Pass an AccessRule directly where an UpdateRule is expected and it is converted to UpdateRule::AccessRule(rule):

let admin = rule!(non_fungible(admin_badge));
let resource = ResourceBuilder::public_fungible()
// Recall is denied today, but the admin badge holder can re-enable it later.
.recallable(rule!(deny_all), admin.clone())
// Withdrawals are open, but only the owner can ever change that.
.withdrawable(rule!(allow_all), OWNER)
.build();

Calling ResourceAccessRules::new() (the default) yields safe baseline rules:

  • mint, burn, recall, freeze, update_metadata: DenyAll, updater Locked.
  • withdraw, deposit: AllowAll, updater Locked.
  • update_non_fungible_data: AllowAll, updater Owner.

Locked-by-default means a resource issued with ResourceAccessRules::new() and no overrides can never have its recall, mint, etc. silently re-enabled later. To leave room for future tightening or relaxation, pass OWNER (or an explicit access rule) as the updater on the actions you want to keep editable.

Once a resource exists, use ResourceManager::update_access_rule to change a single field. The engine consults the field’s UpdateRule to decide whether the caller is allowed:

ResourceManager::get(my_resource)
.update_access_rule(ResourceAuthAction::Withdraw, rule!(non_fungible(user_badge)));

The action is one of ResourceAuthAction::{Mint, Burn, Recall, Withdraw, Deposit, UpdateNonFungibleData, UpdateMetadata, Freeze}. If the field’s updater is Locked, this call fails even for the owner. If the updater is Owner, only the resource owner can succeed. If the updater is AccessRule(rule), only a caller satisfying rule can succeed — the owner has no implicit override.

Callers are identified by various types, depending on the context:

  • ComponentAddress: When a component calls another component.
  • ResourceAddress: When a resource grants specific permissions (e.g., holding a specific NFT grants voting rights).
  • TemplateAddress: When a template has specific rights.
  • User identities: Through the transaction’s signers or proofs provided.

The auth module in tari_template_lib provides utilities for working with identities and proofs within your templates.

  • Principle of Least Privilege: Grant only the necessary permissions. Avoid AllowAll unless it’s genuinely intended for public methods.
  • Clear Ownership: Define and manage component/resource ownership clearly.
  • Test Thoroughly: Always write comprehensive tests to ensure your access rules behave as expected.

By effectively utilizing access rules, you can ensure the security and integrity of your Tari Ootle Templates and the assets they manage.