Using Authentication Witnesses
This guide shows you how to create and use authentication witnesses (authwits) to authorize other accounts to perform actions on your behalf.
Using AuthWitnesses is always a two-part process. This guide shows how to generate and use them, but you still need to set up your contract to accept and authenticate them.
Therefore it is recommended to read the aztec-nr
guide on authwitnesses before this one.
Prerequisites
- Deployed account wallets
- Contract with authwit validation (see smart contract authwits)
- Understanding of authwit concepts
AuthWits
Let's also assume we have a contract with functions some_public_function
and some_private_function
with the macro #[authorize_once("from", "authwit_nonce")]
, meaning it will check if:
from
ismsg_sender
, or- there's an authwitness allowing
from
to call this function
Regardless of its type, you'll want to define what is being delegated (let's call it "action") and the intent ("who intends to act"). For example:
const nonce = Fr.random()
// bob creates an authwit that authorizes alice to call the function on his behalf
const action = contract.methods.some_private_function(bob, 10n, nonce)
const intent = {
caller: alice.address, // alice "intends" to call the function on bob's behalf
action
};
The nonce is necessary to avoid replay attacks. However, the contract is smart enough to allow bob to call the function himself by setting the nonce to 0
.
Create private authwits
Private AuthWits mean that some action is authorized in private. No specific transaction is made, the authorization is just sent as part of the actual transaction:
const authWit = await wallet.createAuthWit(bob.address, intent);
Now alice can call the function by providing the authwit:
await action.send({ from: alice.address, authWitnesses: [authWit] }).wait();
Create public authwits
Public authwits mean the authorization is public, so it requires a transaction. You create the authwit just as above, but the wallet needs to authorize it in the canonical AuthRegistry
contract:
// "true" is specific here... because you may want to revoke it later!
const authwit = await wallet.setPublicAuthWit(bob.address, intent, true);
await authwit.send({ from: bob.address }).wait()
Now that everyone knows about the public authorization, alice can call the function normally:
await action.send({ from: alice.address }).wait()
Create arbitrary message authwits
This is useful when you need to authorize arbitrary data rather than a specific contract function call. For example, authorizing a signature over a message for offchain verification.
Step 1: Create inner hash
You can use computeInnerAuthWitHash
to get yourself a hash of arbitrary hash you can use in an authwit:
import { computeInnerAuthWitHash, computeAuthWitMessageHash } from "@aztec/aztec.js";
// Create hash of arbitrary data
const innerHash = computeInnerAuthWitHash([
field1,
field2,
field3
]);
// Create full authwit message hash
const messageHash = computeAuthWitMessageHash(
executorAddress,
chainId,
version,
innerHash
);
Revoke public authwits
Because public authwits are... well, public, that means you should be able to revoke them. Just set the last parameter to false
and send the transaction:
// Set authorized to false to revoke
const revoked = await authorizerWallet.setPublicAuthWit({
caller: executorAddress,
action: action
}, false).send({ from: account.address });
Next steps
- Learn about authwits in smart contracts
- Understand authwit concepts
- Explore account abstraction
- Implement cross-chain messaging