Signing Data
ARC-60 Authentication with Lute Wallet
The useWallet
hook/composable/primitive provides a signData
method for implementing ARC-60 authentication with the Lute wallet provider. This guide demonstrates how to implement Sign-In with Algorand (SIWA) using Lute wallet.
Implementation
Here's how to implement ARC-60 authentication:
import { useWallet } from '@txnlab/use-wallet-react'
import algosdk from 'algosdk'
import { ed } from '@noble/ed25519'
import { canonify } from "canonify"
function Authenticate() {
const { activeAddress, activeWallet, signData } = useWallet()
const handleAuth = async () => {
try {
if (!activeAddress) {
throw new Error('No active account')
}
if (!activeWallet?.canSignData) {
throw new Error('Current wallet does not support data signing')
}
// Create SIWA request
const siwaRequest = {
domain: location.host,
chain_id: '283',
account_address: activeAddress,
type: 'ed25519',
uri: location.origin,
version: '1',
'issued-at': new Date().toISOString()
}
// Convert request to base64
const dataString = canonify(siwaRequest)
if (!dataString) throw Error('Invalid JSON')
const data = btoa(dataString)
// Sign data with authentication scope
const metadata = { scope: 'auth', encoding: 'base64' }
const resp = await signData(data, metadata)
// Verify signature
const enc = new TextEncoder()
const clientDataJsonHash = await crypto.subtle.digest('SHA-256', enc.encode(dataString))
const authenticatorDataHash = await crypto.subtle.digest('SHA-256', resp.authenticatorData)
const toSign = new Uint8Array(64)
toSign.set(new Uint8Array(clientDataJsonHash), 0)
toSign.set(new Uint8Array(authenticatorDataHash), 32)
const pubKey = algosdk.Address.fromString(activeAddress).publicKey
const isValid = await ed.verifyAsync(resp.signature, toSign, pubKey)
if (!isValid) {
throw new Error('Verification Failed')
}
console.info('Successfully authenticated!')
} catch (error) {
console.error('Error signing data:', error)
}
}
return (
<button onClick={handleAuth}>Sign In with Algorand</button>
)
}
How It Works
Create SIWA Request: The code creates a Sign-In with Algorand (SIWA) request object containing:
Required Properties
domain
- The current hostchain_id
- The Algorand network ID (283 for MainNet)account_address
- The user's wallet addresstype
- The signature type (ed25519)uri
- The origin URLversion
- The SIWA version
Optional Properties
statement
- A human-readable statement about the purpose of the sign-innonce
- A unique value to prevent replay attacksissued-at
- The timestamp when the request was createdexpiration-time
- When the request should expirenot-before
- The earliest time the request should be considered validrequest-id
- A unique identifier for the requestresources
- An array of URIs the user is requesting access to
The SIWA request format follows the CAIP-122 specification for chain-agnostic sign-in.
Sign Data: The request is converted to base64 and signed using the
signData
method with:scope
- Set to 'auth' for authenticationencoding
- Set to 'base64' for the data format
Verify Signature: The signature is verified by:
Computing SHA-256 hashes of the client data and authenticator data
Combining the hashes into a single buffer
Using the user's public key to verify the signature
Error Handling
The implementation includes error handling for:
Missing active account
Unsupported wallet provider
Failed signature verification
General signing errors
Best Practices
Always verify the signature before accepting the authentication
Store the authentication result securely
Implement proper error handling and user feedback
Use HTTPS for secure communication
Consider implementing session management
Resources
CAIP-122 specification - Chain-agnostic sign-in standard
ARC-60 specification - Algorand implementation of CAIP-122 (Draft)
Last updated