Host functions for invoker-less authorization
Specification
CAP: 0089
Title: Host functions for invoker-less authorization
Working Group:
Owner: Dmytro Kozhevin <@dmkozh>
Authors: Dmytro Kozhevin <@dmkozh>
Consulted: Boyan <@brozorec>
Status: Draft
Created: 2026-10-16
Discussion: https://github.com/orgs/stellar/discussions/2043
Protocol version: 31
Simple Summary
Introduce new host functions that are analogous to the existing require_auth[_for_args] functions, but require external authorization.
Working Group
As specified in the Preamble.
Motivation
The built-in authorization mechanism in Soroban supports invoker authorization out of the box: if authorization is required from an address that has invoked a given function, then the authorization is automatically considered a success. In overwhelming majority of the cases, this is the desired behavior: if a contract calls another contract, it implicitly authorizes the callee. A typical example of this is transferring tokens from the current contract: the contract can just call token.transfer without any additional signatures, and the call itself is considered to be authorized.
Nevertheless, there are scenarios where the default invoker authorization is not desired. The main motivational example for this is custom account implementation: if an account contract calls require_auth for an address, it might have an intention to explicitly delegate authorization to that address (see CAP-0071-01 for more details on delegation implementation). In the case of delegation, if the delegate tries to require authorization from its caller via require_auth (e.g. due to mis-configuration), then the authorization would incorrectly succeed because of the default invoker authorization behavior. This scenario allows performing custom account authorization without any externally provided signatures involved, so anyone could authorize actions on behalf on the account and thus bypass the intended logic entirely.
Identifying the delegation loop scenarios like the one described above is currently not always possible, e.g. in case of combining CAP-0071 built-in delegation with delegation based on require_auth. In order to address this particular issue, as well as any other potential scenarios where explicit authorization is desired, we introduce new host functions that allow developers to explicitly opt-out of invoker authorization, and thus solving the problem for any delegation loop scenarios at once.
Note, that implementing delegation chains only via CAP-0071 delegate_auth mechanism is another way to avoid the issues described above (as delegate_auth chains do not allow invoker authorization). However, this CAP still has its own role in supporting the legacy delegation implementations, as well as potentially addressing the new use cases that do not exist yet.
Goals Alignment
This CAP is aligned with the following Stellar Network Goals:
- The Stellar Network should make it easy for developers of Stellar projects to create highly usable products
Abstract
Two new host functions are introduced: require_external_auth_for_args and require_external_auth. They behave in exactly the same ways as the respective existing functions (require_auth_for_args and require_auth), except that they can only be satisfied by external authorization. The authorization payload must be attached to transaction, and a contract up in the stack can't satisfy this via invoker auth. The functions do not execute any logic related to invoker authorization.
Specification
New host functions
The diff is based on commit 0c99950e96128beba3319aa96b116646ce974e69 of rs-soroban-env.
diff --git a/soroban-env-common/env.json b/soroban-env-common/env.json
index cde9ef90..0951d589 100644
--- a/soroban-env-common/env.json
+++ b/soroban-env-common/env.json
@@ -3014,7 +3014,35 @@
"return": "Void",
"docs": "Delegates the custom account authentication to the provided address. This is only available when called within `__check_auth` contract function inside a custom account. This call will require the `address` to have authorized exactly the same call tree as the one being authorized by the current `__check_auth` call. Specifically, the same signature payload and the same context will be passed into `address`s authorization check. Panics if the call has not been authorized, or if called not from within `__check_auth`.",
"min_supported_protocol": 27
- }
+ },
+ {
+ "export": "b",
+ "name": "require_external_auth_for_args",
+ "args": [
+ {
+ "name": "address",
+ "type": "AddressObject"
+ },
+ {
+ "name": "args",
+ "type": "VecObject"
+ }
+ ],
+ "return": "Void",
+ "docs": "Works exactly like `require_auth_for_args`, but enforces external authorization (i.e. authorization payload must be attached to transaction, and a contract up in the stack can't satisfy this)."
+ },
+ {
+ "export": "c",
+ "name": "require_external_auth",
+ "args": [
+ {
+ "name": "address",
+ "type": "AddressObject"
+ }
+ ],
+ "return": "Void",
+ "docs": "Works exactly like `require_auth`, but enforces external authorization (i.e. authorization payload must be attached to transaction, and a contract up in the stack can't satisfy this)."
+ }
]
},
{
Semantics
New host functions
require_external_auth_for_args requires authorization from address for a call of the current contract function with the provided args. It works exactly like require_auth_for_args, but disallows invoker authorization entirely, including both direct invokers and invokers higher up in the stack. Thus the only way to satisfy the authorization is to have it explicitly provided in a SorobanAuthorizationEntry attached to the transaction.
require_external_auth requires authorization from address for a call of the current contract function with its call arguments. It works exactly like require_auth, but disallows invoker authorization entirely, including both direct invokers and invokers higher up in the stack. Thus the only way to satisfy the authorization is to have it explicitly provided in a SorobanAuthorizationEntry attached to the transaction.
Design Rationale
New host functions vs disabling invoker authorization in __check_auth
An alternative solution to the specific problem described in the Motivation section would be to modify the existing require_auth_for_args and require_auth host functions to optionally disallow invoker authorization when they are called from the __check_auth contract function.
The downside of this approach is that it would modify the existing protocol semantics, so there is no guarantee that an existing contract where this behavior is intentional would not be broken by the change. It is also a solution to a very specific problem, whereas introducing new host functions allows developers to opt-out of invoker authorization in arbitrary scenarios, which may not involve __check_auth at all.
Thus the proposed solution is both more general, and also does not risk breaking existing contracts. The main downside of it is that the developers would need to update their contracts. The trade-off is considered acceptable here given the increased flexibility and safety it provides.
Protocol Upgrade Transition
The proposed functionality will be available starting from protocol version 31.
Backwards Incompatibilities
There are no backwards incompatibilities introduced by this CAP.
Resource Utilization
The new host functions are going to be metered using the existing metering primitives. No other resource utilization changes are expected.
Security Concerns
The new host functions are more restrictive than the existing ones, so using them may only make the authorization requirements stricter and should not introduce new security vulnerabilities.
Test Cases
TBD
Implementation
TBD
Preamble
Discussion
1 linked threads
- Design discussion
- Add CAP-0089: VRF-Based Protocol Randomness and Fair Leader Selection#2005 · closed · 14 comments · 2026-11-08