Skip to content

Commit d2dbf6f

Browse files
committed
release 0.7.0 updates
1 parent e957cfb commit d2dbf6f

15 files changed

Lines changed: 882 additions & 247 deletions

File tree

content/stellar-contracts/accounts/authorization-flow.mdx

Lines changed: 67 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,30 @@
22
title: Authorization Flow
33
---
44

5-
Authorization in smart accounts is determined by matching the current context against the account's context rules. Rules are gathered, ordered by recency, and evaluated until one satisfies the requirements. If a matching rule is found, its policies (if any) are enforced. Otherwise, authorization fails.
5+
Authorization in smart accounts is determined by matching the current context against explicitly selected context rules. The caller supplies `context_rule_ids` in the `AuthPayload`, specifying exactly one rule per auth context. If the selected rule passes all checks, its policies (if any) are enforced. Otherwise, authorization fails.
6+
7+
## AuthPayload
8+
9+
The `AuthPayload` structure is passed as the signature data in `__check_auth`:
10+
11+
```rust
12+
#[contracttype]
13+
pub struct AuthPayload {
14+
/// Signature data mapped to each signer.
15+
pub signers: Map<Signer, Bytes>,
16+
/// Per-context rule IDs, aligned by index with `auth_contexts`.
17+
pub context_rule_ids: Vec<u32>,
18+
}
19+
```
20+
21+
Each entry in `context_rule_ids` specifies the rule ID to validate against for the corresponding auth context (by index). Its length must equal `auth_contexts.len()`.
22+
23+
<Callout type="warning">
24+
The `context_rule_ids` are bound into the signed digest: `sha256(signature_payload || context_rule_ids.to_xdr())`. This prevents rule-selection downgrade attacks where an attacker could redirect a signature to a less restrictive rule.
25+
</Callout>
626

727
## Detailed Flow
28+
829
```mermaid
930
sequenceDiagram
1031
participant User
@@ -14,105 +35,67 @@ sequenceDiagram
1435
participant Verifier
1536
participant Policy
1637
17-
User->>SmartAccount: Signatures
18-
SmartAccount->>ContextRule: Match context<br/>(CallContract, Default, ...)
19-
ContextRule->>ContextRule: Filter expired rules<br/>Sort newest first
20-
21-
loop Each rule until match
22-
Note over ContextRule,DelegatedSigner: Built-in authorization <br/>for delegated signers
23-
ContextRule->>DelegatedSigner: require_auth_for_args()
24-
DelegatedSigner-->>ContextRule: Authorized
38+
User->>SmartAccount: AuthPayload (signers + context_rule_ids)
39+
SmartAccount->>SmartAccount: Authenticate all provided signers
2540
26-
Note over ContextRule,Verifier: Signature verification for external signers
27-
ContextRule->>Verifier: verify()
28-
Verifier-->>ContextRule: Valid
41+
loop Each (context, rule_id) pair
42+
SmartAccount->>ContextRule: Look up rule by ID
43+
ContextRule->>ContextRule: Reject if expired or<br/>context type mismatch
2944
30-
Note over ContextRule,Policy: Policy pre-checks
31-
ContextRule->>Policy: can_enforce()
32-
Policy-->>ContextRule: True/False
45+
Note over ContextRule,DelegatedSigner: Identify authenticated signers<br/>from rule's signer list
46+
Note over ContextRule,Verifier: External signers verified<br/>during initial authentication
3347
34-
alt All checks pass
48+
alt Rule has policies
3549
ContextRule->>Policy: enforce()
36-
Policy->>Policy: Update state
37-
ContextRule-->>SmartAccount: ✓ Authorized
38-
else Any check fails
39-
ContextRule->>ContextRule: Try next rule
50+
Policy->>Policy: Validate + update state
51+
Policy-->>ContextRule: Success (or panic)
52+
else No policies
53+
ContextRule->>ContextRule: All rule signers<br/>must be authenticated
4054
end
4155
end
4256
43-
SmartAccount-->>User: Success
57+
SmartAccount-->>User: Success or Denied
4458
```
4559

46-
### 1. Rule Collection
60+
### 1. Signer Authentication
4761

48-
The smart account gathers all relevant context rules for evaluation:
62+
All signers in the `AuthPayload` are authenticated upfront:
4963

50-
- Retrieve all non-expired rules for the specific context type
51-
- Include default rules that apply to any context
52-
- Sort specific and default rules by creation time (newest first)
53-
54-
**Context Type Matching:**
55-
- For a `CallContract(address)` context, both specific `CallContract(address)` rules and `Default` rules are collected
56-
- For a `CreateContract(wasm_hash)` context, both specific `CreateContract(wasm_hash)` rules and `Default` rules are collected
57-
- For any other context, only `Default` rules are collected
64+
- **Delegated Signer**: The address has authorized the operation via `require_auth_for_args(payload)`
65+
- **External Signer**: The verifier contract confirms the signature is valid for the public key
5866

59-
**Expiration Filtering:**
60-
Rules with `valid_until` set to a ledger sequence that has passed are automatically filtered out during collection.
67+
Any signer in the `AuthPayload` that is not part of any selected context rule is rejected.
6168

6269
### 2. Rule Evaluation
6370

64-
For each rule in order (newest and most specific first):
65-
66-
#### Step 2.1: Signer Filtering
71+
For each (context, rule_id) pair:
6772

68-
Extract authenticated signers from the rule's signer list. A signer is considered authenticated if:
73+
#### Step 2.1: Rule Lookup and Validation
6974

70-
- **Delegated Signer**: The address has authorized the operation via `require_auth_for_args(payload)`
71-
- **External Signer**: The verifier contract confirms the signature is valid for the public key
75+
The rule is looked up by its explicit ID. The rule is rejected if:
76+
- It does not exist
77+
- It is expired (`valid_until` has passed)
78+
- Its context type does not match the actual context (`Default` rules match any context)
7279

73-
Only authenticated signers proceed to the next step.
80+
#### Step 2.2: Signer Matching
7481

75-
#### Step 2.2: Policy Validation
76-
77-
If the rule has attached policies, verify that all can be enforced:
78-
79-
```rust
80-
for policy in rule.policies {
81-
if !policy.can_enforce(e, account, rule_id, signers, auth_context) {
82-
// This rule fails, try the next rule
83-
}
84-
}
85-
```
86-
87-
If any policy's `can_enforce()` returns false, the rule fails and evaluation moves to the next rule.
82+
Authenticated signers are identified from the rule's signer list.
8883

8984
#### Step 2.3: Authorization Check
9085

9186
The authorization check depends on whether policies are present:
9287

9388
**With Policies:**
94-
- Success if all policies passed `can_enforce()`
95-
- The presence of authenticated signers is verified during policy evaluation
89+
- `enforce()` is called on each policy. If any `enforce()` panics, the authorization fails.
90+
- Signer validation is deferred to the policies (e.g., threshold checks).
9691

9792
**Without Policies:**
98-
- Success if all signers in the rule are authenticated
99-
- At least one signer must be authenticated for the rule to match
100-
101-
#### Step 2.4: Rule Precedence
102-
103-
The first matching rule wins. Newer rules take precedence over older rules for the same context type. This allows overwriting old rules.
93+
- All signers in the rule must be authenticated.
94+
- At least one signer must be present.
10495

10596
### 3. Policy Enforcement
10697

107-
If authorization succeeds, the smart account calls `enforce()` on all matched policies in order:
108-
109-
```rust
110-
for policy in matched_rule.policies {
111-
policy.enforce(e, account, rule_id, signers, auth_context);
112-
}
113-
```
114-
115-
This triggers any necessary state changes such as updating spending counters, recording timestamps, emitting audit events, or modifying allowances.
98+
Policy enforcement happens during rule evaluation. When `enforce()` is called, policies both validate conditions and perform state changes (updating spending counters, recording timestamps, emitting audit events, etc.).
11699

117100
Policy enforcement requires the smart account's authorization, ensuring that policies can only be enforced by the account itself.
118101

@@ -148,19 +131,14 @@ ContextRule {
148131

149132
**Call Context:** `CallContract(dex_address)`
150133

151-
**Authorization Entries:** `[passkey_signature]`
134+
**AuthPayload:** `{ signers: [passkey_signature], context_rule_ids: [2] }`
152135

153136
**Flow:**
154-
1. Collect: Rules 2 (specific) and 1 (default)
155-
2. Evaluate Rule 2:
156-
- Signer filtering: Passkey authenticated
157-
- Policy validation: Spending limit check passes
158-
- Authorization check: All policies enforceable → Success
159-
3. Enforce: Update spending counters, emit events
137+
1. Authenticate: Passkey signature verified
138+
2. Look up Rule 2: Not expired, context type matches
139+
3. Enforce: Spending limit policy validates and updates counters
160140
4. Result: Authorized
161141

162-
If the spending limit had been exceeded, Rule 2 would fail and evaluation would continue to Rule 1 (which would also fail since the passkey doesn't match Alice or Bob).
163-
164142
### Fallback to Default
165143

166144
**Configuration:**
@@ -185,16 +163,14 @@ ContextRule {
185163

186164
**Call Context:** `CallContract(dex_address)`
187165

188-
**Authorization Entries:** `[ed25519_alice_signature, ed25519_bob_signature]`
166+
**AuthPayload:** `{ signers: [alice_sig, bob_sig], context_rule_ids: [1] }`
189167

190168
**Flow:**
191-
1. Collect: Rule 2 filtered out (expired), only Rule 1 collected
192-
2. Evaluate Rule 1: Both Alice and Bob authenticated → Success
193-
3. Enforce: No policies to enforce
169+
1. Authenticate: Alice and Bob signatures verified
170+
2. Look up Rule 1: Default rule matches any context, not expired
171+
3. No policies: Both Alice and Bob authenticated → Success
194172
4. Result: Authorized
195173

196-
The expired session rule is automatically filtered out, and authorization falls back to the default admin rule.
197-
198174
### Authorization Failure
199175

200176
**Configuration:**
@@ -210,24 +186,21 @@ ContextRule {
210186

211187
**Call Context:** `CallContract(any_address)`
212188

213-
**Authorization Entries:** `[alice_signature]`
189+
**AuthPayload:** `{ signers: [alice_signature], context_rule_ids: [1] }`
214190

215191
**Flow:**
216-
1. Collect: Default rule retrieved
217-
2. Evaluate:
218-
- Signer filtering: Only Alice authenticated
219-
- Policy validation: Threshold policy requires 2 signers, only 1 present → Fail
220-
3. No more rules to evaluate
192+
1. Authenticate: Alice signature verified
193+
2. Look up Rule 1: Default rule, not expired
194+
3. Enforce: Threshold policy requires 2 signers, only 1 present → panics
221195
4. Result: Denied (transaction reverts)
222196

223197
## Performance Considerations
224198

225199
Protocol 23 optimizations make the authorization flow efficient:
226-
- **Marginal storage read costs**: Reading multiple context rules has negligible cost
200+
- **Marginal storage read costs**: Reading context rules has negligible cost
227201
- **Cheaper cross-contract calls**: Calling verifiers and policies is substantially cheaper
228202

229-
The framework enforces limits to maintain predictability:
230-
- Maximum context rules per smart account: 15
203+
The framework enforces per-rule limits to maintain predictability:
231204
- Maximum signers per context rule: 15
232205
- Maximum policies per context rule: 5
233206

content/stellar-contracts/accounts/context-rules.mdx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ List of authorized signers (maximum 15 per rule). Signers can be either delegate
3232
For detailed documentation on signers, see [Signers](/stellar-contracts/accounts/signers-and-verifiers).
3333

3434
#### Policies
35-
List of policy contracts (maximum 5 per rule). Policies act as enforcement modules that perform read-only prechecks and state-changing enforcement logic.
35+
List of policy contracts (maximum 5 per rule). Policies act as enforcement modules that validate and enforce authorization constraints.
3636

3737
For detailed documentation on policies, see [Policies](/stellar-contracts/accounts/policies).
3838

@@ -44,28 +44,29 @@ Each rule must contain at least one signer OR one policy. This enables pure poli
4444
### Multiple Rules Per Context
4545
Multiple rules can exist for the same context type with different signer sets and policies. This allows progressive authorization models where different combinations of credentials grant access to the same operations.
4646

47-
### Rule Precedence
48-
Rules are evaluated in reverse chronological order (newest first). The first matching rule wins. This enables seamless permission updates: adding a new rule with different requirements immediately takes precedence over older rules for the same context.
47+
### Explicit Rule Selection
48+
The caller explicitly selects which rule to validate against for each auth context via `AuthPayload::context_rule_ids`. No automatic iteration or rule precedence is applied — the caller chooses the exact rule to use.
4949

5050
### Automatic Expiration
51-
Expired rules are automatically filtered out during authorization evaluation.
51+
Expired rules are rejected during authorization evaluation.
5252

5353
## Context Rule Limits
5454

55-
The framework enforces limits to keep costs predictable and encourage proactive context rule management (remove expired or non-valid rules):
55+
The framework enforces per-rule limits to maintain predictability:
5656

57-
- Maximum context rules per smart account: 15
5857
- Maximum signers per context rule: 15
5958
- Maximum policies per context rule: 5
6059

60+
There is no upper limit on the total number of context rules per smart account.
61+
6162
## Authorization Matching
6263

63-
During authorization, the framework:
64+
During authorization, the caller supplies `context_rule_ids` in the `AuthPayload`, one per auth context. For each (context, rule_id) pair:
6465

65-
1. Gathers all non-expired rules matching the context type plus default rules
66-
2. Sorts rules by creation time (newest first)
67-
3. Evaluates rules in order until one matches
68-
4. Returns the first matching rule or fails if none match
66+
1. The rule is looked up by its explicit ID
67+
2. The rule is validated: must not be expired, context type must match
68+
3. Signers are authenticated and policies enforced
69+
4. Authorization succeeds if all checks pass, otherwise fails
6970

7071
For detailed documentation on the authorization flow, see [Authorization Flow](/stellar-contracts/accounts/authorization-flow).
7172

0 commit comments

Comments
 (0)