# 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 %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dev.ada-anvil.io/developer-tools/cip8-auth-guide.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
