> For the complete documentation index, see [llms.txt](https://dev.ada-anvil.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dev.ada-anvil.io/developer-tools/cip8-auth-guide.md).

# Web3 Authentication

To "Sign in with Cardano," users cryptographically prove control of their stake address by signing a unique challenge with their wallet’s private key via CIP-8. This guide shows how to integrate Weld for wallet interactions, craft and sign challenges, verify signatures on your server, and extend authentication with on-chain business logic. (i.e. NFT gating, checking delegation status, wallet whitelisting, etc...)

***

## Core Components

* **FrontEnd UI (Browser):** Initiates the request. We recommend a library like [Weld](https://github.com/cardano-forge/weld) to handle different wallet APIs.
* **Wallet UI (e.g., Lace, Vespr, Eternl):** Holds the user's private key and creates the signature.
* **Backend Server:** Verifies the signature to authenticate the user.

***

{% @mermaid/diagram content="sequenceDiagram
actor U as User
participant D as FrontEnd UI
participant W as Wallet UI
participant S as Backend Server
U->>D: Click "Connect Wallet"
D->>W: weld.connectAsync('walletName')
W-->>D: Approve & return stakeAddress
D->>D: Build payload { address, action, uri, timestamp }
D->>W: weld.handler.signData(payload)
W->>U: Ask User to Sign payload
U->>W: Click "Approve" and Sign
W-->>D: Return signatureEnvelope + publicKey
D->>S: POST /auth/verify { signatureEnvelope, publicKey }
S->>S: Parse COSE\_Sign1 & verify signature
S->>S: Match publicKey hash → address
S->>S: Business logic hooks - NFT gating, Wallet Whitelisting, etc...
S-->>D: 200 OK + Set-Cookie (session)
D-->>U: Render authenticated UI state" %}

***

## Authentication Flow

### 1. FrontEnd UI: Create and Sign Payload

1. **Get Address:** Retrieve the user's stake address from the connected wallet.
2. **Create Payload:** Construct a unique message including the stake address to prevent replay attacks (e.g., `"Auth for MyDApp: stake1u..."`).
3. **Sign Payload:** Use the wallet's `signData` function to sign the payload. The wallet returns a `COSE_Sign1` structure (the proof).
4. **Send Proof:** Send the `COSE_Sign1` proof and the user's public key to the backend server.

### 2. Backend Server: Verify Proof & Apply Business Logic

The server receives the `COSE_Sign1` proof, which contains a **protected header** (with the user's address), the original **payload**, and the **signature**. It must perform these checks:

* **Check 1: Key Matches Address**
  * Hash the public key received from the client.
  * Extract the public key hash from the `address` in the proof's protected header.
  * **Verify they match.** This confirms the public key belongs to the address in the proof.
* **Check 2: Signature is Valid**
  * Using a crypto library, verify the `signature` against the `protected header` and `payload`.
  * This confirms the proof was signed by the user's private key and hasn't been tampered with.
* **Check 3: Business Logic Hooks (Optional)** With the address cryptographically verified, you can now implement custom business logic. Examples include:
  * **Token/NFT Gating:** Query a Cardano API (e.g., Blockfrost, Koios) to confirm the wallet's UTXOs contain a required asset policy ID or fingerprint.
  * **Stake & Delegation Checks:** Use the stake address to check delegation status, stake pool, or total ADA balance against your requirements.
  * **Wallet Whitelists/Blacklists:** For higher security applications, you might maintain a list of approved stake addresses.
  * **Session Management:** If you're using a session-based authentication system, set a secure cookie on the client to grant access.
  * **Replay Protection:** The payload should include a `timestamp`. On the server, enforce a time-to-live (TTL) to reject signatures that are too old.
  * **Domain Separation:** The payload should include the `uri` of the request origin and a specific `action` description. The server must validate these fields to prevent a signature from one domain being reused on another (phishing) and to ensure the user approved the specific action intended.

<details>

<summary>Implementation Details: Full Backend Verification</summary>

While working directly with Cardano's serialization libraries (CSL) can have a steep learning curve, you don't need to be an expert to implement secure authentication. The function below encapsulates the entire CIP-8 verification process. We'll use this similar functions in the full examples provided later, giving you a practical, production-ready tool for validating signature packages.

```typescript
import {
  COSESign1,
  COSEKey,
  Label, 
  Int,
  BigNum
} from '@emurgo/cardano-message-signing-asmjs';
import {
  Address,
  Ed25519Signature,
  PublicKey,
  RewardAddress,
} from '@emurgo/cardano-serialization-lib-asmjs';

export async function verifyCIP8Signature(
  sigData: { signature: string; key?: string },
  options: { expectedDomain: string; expectedAction: string }
): Promise<{ isValid: boolean; stakeAddress: string | null }> {
  try {
    if (typeof sigData !== 'object' || !sigData.signature || !sigData.key) {
      console.error('Invalid signature data format');
      return { isValid: false, stakeAddress: null };
    }

    // 1. Decode the signature and key
    const decoded = COSESign1.from_bytes(hexToBytes(sigData.signature));
    const key = COSEKey.from_bytes(hexToBytes(sigData.key));
    const headermap = decoded.headers().protected().deserialized_headers();
    const address = Address.from_bytes(headermap.header(Label.new_text('address')).to_bytes());
    const pubKeyBytes = key.header(Label.new_int(Int.new_negative(BigNum.from_str('2')))).as_bytes();
    const publicKey = PublicKey.from_bytes(pubKeyBytes);

    // 2. Verify the cryptographic signature
    const signatureBytes = Ed25519Signature.from_bytes(decoded.signature());
    const signedData = decoded.signed_data().to_bytes();
    const isSignatureValid = publicKey.verify(signedData, signatureBytes);
    if (!isSignatureValid) throw new Error('Cryptographic signature is invalid.');

    // 3. Parse and validate the payload for security
    const payloadBytes = decoded.payload();
    if (!payloadBytes) throw new Error('No payload in signature');
    const payload = JSON.parse(new TextDecoder().decode(payloadBytes));

    // 4. Replay Protection: Check if the timestamp is recent
    const TTL = 5 * 60 * 1000; // 5 minutes
    const isTimestampValid = Date.now() - payload.timestamp < TTL;
    if (!isTimestampValid) throw new Error('Timestamp is too old, possible replay attack.');

    // 5. Domain Separation: Check URI and action to prevent phishing
    const isDomainValid = payload.uri === options.expectedDomain;
    const isActionValid = payload.action === options.expectedAction;
    if (!isDomainValid || !isActionValid) throw new Error('Domain or action mismatch.');

    // 6. Final verification: Extract stake address
    const signerStakeAddrBech32 = RewardAddress.from_address(address)?.to_address()?.to_bech32();
    if (!signerStakeAddrBech32) throw new Error('Could not extract stake address.');

    console.log('CIP-8 verification successful for:', signerStakeAddrBech32);
    return { isValid: true, stakeAddress: signerStakeAddrBech32 };

  } catch (error) {
    console.error('CIP-8 verification error:', error);
    return { isValid: false, stakeAddress: null };
  }
}

export function hexToBytes(hex: string): Uint8Array {
  const bytes = new Uint8Array(Math.floor(hex.length / 2));
  for (let i = 0; i < bytes.length; i++) {
    const hexByte = hex.substring(i * 2, i * 2 + 2);
    bytes[i] = parseInt(hexByte, 16);
  }
  return bytes;
}
```

***

</details>

### 3. FrontEnd UI: Render Authenticated UI State

1. **Render Authenticated UI State:** Once the server has verified the signature, it can grant access to the user's authenticated UI state.

***

## Full Examples

{% content-ref url="<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/developer-tools/guides/transaction/README.md>" %}
<https://github.com/Cardano-Forge/anvil-api/blob/main/docs/developer-tools/guides/transaction/README.md>
{% endcontent-ref %}
