Check Authorization¶
Learn how to verify caller permissions and implement access control in your smart contracts.
Overview¶
Authorization is crucial for smart contract security. Koinos provides built-in mechanisms to check if a caller has the required permissions to execute specific functions.
Basic Authorization¶
Require Authority¶
import { System } from "@koinos/sdk-as";
export class MyContract {
transfer(args: token.transfer_arguments): token.transfer_result {
// Require authorization from the 'from' address
System.requireAuthority(args.from);
// Proceed with transfer logic
// ...
return new token.transfer_result();
}
mint(args: token.mint_arguments): token.mint_result {
// Only contract owner can mint
const owner = this.owner.get();
System.requireAuthority(owner);
// Mint tokens
// ...
return new token.mint_result();
}
}
Get Caller Information¶
function restrictedFunction(): void {
const caller = System.getCaller();
// Check if called by another contract
if (caller.caller_privilege == authority.authorization_type.contract_call) {
System.require(
Arrays.equal(caller.caller, TRUSTED_CONTRACT_ADDRESS),
"unauthorized contract caller"
);
}
// Function logic
}
Authorization Patterns¶
Owner-Only Functions¶
export class OwnableContract {
owner: Storage.Obj<Uint8Array>;
constructor() {
this.owner = new Storage.Obj(
System.getContractId(),
0,
Storage.Serializer.bytes
);
}
set_owner(args: contract.set_owner_arguments): contract.set_owner_result {
// Only current owner can change owner
System.requireAuthority(this.owner.get());
this.owner.put(args.new_owner);
return new contract.set_owner_result();
}
admin_function(args: contract.admin_arguments): contract.admin_result {
System.requireAuthority(this.owner.get());
// Admin logic
return new contract.admin_result();
}
}
Multi-Signature Authorization¶
export class MultiSigContract {
signers: Storage.List<Uint8Array>;
required_signatures: Storage.Obj<u32>;
execute_transaction(args: multisig.execute_arguments): multisig.execute_result {
const signers = args.signers;
const requiredSigs = this.required_signatures.get() || 2;
System.require(signers.length >= requiredSigs, "insufficient signatures");
// Verify each signer is authorized
for (let i = 0; i < signers.length; i++) {
System.requireAuthority(signers[i]);
}
// Execute transaction
// ...
return new multisig.execute_result();
}
}
Role-Based Access Control¶
export class RoleBasedContract {
roles: Storage.Map<Uint8Array, Storage.List<string>>;
has_role(address: Uint8Array, role: string): bool {
const userRoles = this.roles.get(address);
if (!userRoles) return false;
for (let i = 0; i < userRoles.length(); i++) {
if (userRoles.get(i) == role) return true;
}
return false;
}
require_role(address: Uint8Array, role: string): void {
System.require(this.has_role(address, role), `missing role: ${role}`);
}
admin_function(args: contract.admin_arguments): contract.admin_result {
const caller = System.getCaller().caller;
this.require_role(caller, "admin");
System.requireAuthority(caller);
// Admin function logic
return new contract.admin_result();
}
}
Best Practices¶
- Always validate authorization before state changes
- Use specific error messages for different authorization failures
- Implement role-based access for complex permission systems
- Check both authorization and authentication where needed
- Be consistent with authorization patterns across your contract