Soroban Contracts 101 : Auth
Tags: soroban, sorobanathon, stellar, smartcontract
Published: 2023-03-17 22:37:46

Hi there! Welcome to my seventh post of my series called "Soroban Contracts 101", where I'll be explaining the basics of Soroban contracts, such as data storage, authentication, custom types, and more. All the code that we're gonna explain throughout this series will mostly come from [soroban-contracts-101](https://github.com/yuzurush/soroban-contracts-101) github repository.

In this seventh post of the series, I'll be covering soroban contract auth funcionalities. Authentication (auth) in smart contracts refers to restricting access to contract methods (functions) to only authorized callers. In Soroban, contract methods can require authentication (auth) to only allow authorized callers to execute them. This is done using the `require_auth` and `require_auth_for_args` methods. For example `user.require_auth()` would require authentication by a user address to continue execution. Only a caller that has been authorized as that user would be able to proceed past this point.`require_auth_for_args` allows also restricting based on function arguments, for more fine-grained control.

##The Contract Code

```rust
#[contracttype]
pub enum DataKey {
    Counter(Address),
}

pub struct IncrementContract;

#[contractimpl]
impl IncrementContract {
    /// Increment increments a counter for the user, and returns the value.
    pub fn increment(env: Env, user: Address, value: u32) -> u32 {

        user.require_auth();

        let key = DataKey::Counter(user.clone());

        let mut count: u32 = env
            .storage()
            .get(&key)
            .unwrap_or(Ok(0)) // If no value set, assume 0.
            .unwrap(); // Panic if the value of COUNTER is not u32.

        // Increment the count.
        count += value;

        // Save the count.
        env.storage().set(&key, &count);

        // Return the count to the caller.
        count
    }
}
```
This contract code implements an increment contract that requires authentication to use. The key points of the contract :

- It uses the `require_auth` and `require_auth_for_args` methods to enforce that only authorized callers can execute the `increment` function
- It stores `counter` values under a key derived from the user's address `(DataKey::Counter(user.clone()))`, so each user has their own `counter`
- It gets, increments, and stores the counter for a user
- It returns the new counter value

So this contract demonstrates using Soroban's authentication functionality to restrict access to contract function. Only authorized users (in this case, a single predefined user) can call the increment method and manage their own counter.

##The Test Code

```rust
#[test]
fn test() {
    let env = Env::default();
    let contract_id = env.register_contract(None, IncrementContract);
    let client = IncrementContractClient::new(&env, &contract_id);

    let user_1 = Address::random(&env);
    let user_2 = Address::random(&env);

    assert_eq!(client.increment(&user_1, &5), 5);
    // Verify that the user indeed had to authorize a call of `increment` with
    // the expected arguments:
    assert_eq!(
        env.recorded_top_authorizations(),
        std::vec![(
            // Address for which auth is performed
            user_1.clone(),
            // Identifier of the called contract
            contract_id.clone(),
            // Name of the called function
            symbol!("increment"),
            // Arguments used to call `increment` (converted to the env-managed vector via `into_val`)
            (user_1.clone(), 5_u32).into_val(&env)
        )]
    );

    // Do more `increment` calls. It's not necessary to verify authorizations
    // for every one of them as we don't expect the auth logic to change from
    // call to call.
    assert_eq!(client.increment(&user_1, &2), 7);
    assert_eq!(client.increment(&user_2, &1), 1);
    assert_eq!(client.increment(&user_1, &3), 10);
    assert_eq!(client.increment(&user_2, &4), 5);
}

```
This test code is a unit test that thoroughly test the authentication logic of our `increment` contract. It:

- Creates an `Env` and registers the contract
- Creates a client to call the contract
- Calls `increment` with two different users (randomly generated addresses)
- Verifies the authorization record after the first call, checking that the expected user/function/args were authenticated
- Makes additional calls without verifying the auth record each time (since the logic won't change)

So this test ensures that the contract's authentication works as expected - only allowing a given user to increment their own `counter`, and recording the correct authorization information.
Testing a contract's authentication (and other security) logic is important to ensure it will behave correctly and securely when deployed to a real blockchain.

##Running Contract Tests
To ensure that the contract functions as intended, you can run the contract tests using the following command:
```
cargo test 
```
If the tests are successful, you should see an output similar to:
```
running 1 test
test test::test ... ok
```

##Building The Contract
To build the contract, use the following command:
```
cargo build --target wasm32-unknown-unknown --release
```
This should output a .wasm file in the ../target directory:
```
../target/wasm32-unknown-unknown/release/soroban_auth_contract.wasm
```

##Invoking The Contract
We will do invocation using 2 different account. First Account :
```
$ soroban contract invoke \
>     --source GAAX4DJPNZJNWN3QOSBAJIFTT4XXPQ4B274MBEYODWW3ZMHLQK6NKKG4 \
>     --wasm ../target/wasm32-unknown-unknown/release/soroban_auth_contract.wasm \
>     --id 1 \
>     -- \ 
>     increment \
>     --user GAAX4DJPNZJNWN3QOSBAJIFTT4XXPQ4B274MBEYODWW3ZMHLQK6NKKG4 \
>     --value 5
5
```
Second account :

```
$ soroban contract invoke \
>     --source GC4E5NTKIV7PDFDNYFOLUHB32BXYURJOVNDFZ5X67XKQXVHXDLGZBOCU \
>     --wasm ../target/wasm32-unknown-unknown/release/soroban_auth_contract.wasm \
>     --id 1 \
>     -- \ 
>     increment \
>     --user GC4E5NTKIV7PDFDNYFOLUHB32BXYURJOVNDFZ5X67XKQXVHXDLGZBOCU \
>     --value 10
10
```
Then we will use `soroban read` to check the our contract current  contract-data ledger entry value :

```
$ soroban contract read --id 1
"[""Counter"",""GAAX4DJPNZJNWN3QOSBAJIFTT4XXPQ4B274MBEYODWW3ZMHLQK6NKKG4""]",5
"[""Counter"",""GC4E5NTKIV7PDFDNYFOLUHB32BXYURJOVNDFZ5X67XKQXVHXDLGZBOCU""]",10
```

##Conclusion
We explored how to implement authentication (auth) in Soroban smart contracts. Auth is important to restrict sensitive contract methods to only authorized callers. Soroban provides the `require_auth` and `require_auth_for_args` methods to easily enforce that only designated addresses can execute certain methods. Stay tuned for more post in this "Soroban Contracts 101" Series where we will dive deeper into Soroban Contracts and their functionalities.