> ## Documentation Index
> Fetch the complete documentation index at: https://docs.handcash.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Authenticate users and manage permissions with Handcash Connect

# HandCash Authentication Integration Guide

## Prerequisites

1. **Create an application** in the [Developer Dashboard](https://dashboard.handcash.io)
2. **Configure callback URLs** in your application settings
3. **Get your credentials** (`AppId` and `AppSecret`)

## Configure Callback URLs

Configure callback URLs in [dashboard.handcash.io](https://dashboard.handcash.io):

1. Select your application
2. Go to **Settings** or **Configuration**
3. Set:
   * **Authorization Success URL**: Where users redirect after authorization (e.g., `https://yourapp.com/auth/success`)
   * **Authorization Failed URL**: Where users redirect if they decline (e.g., `https://yourapp.com/auth/failed`)

<Note>
  **Important:** Use HTTPS in production. For local development, `http://localhost` is allowed.
</Note>

## Key Generation

### Required Dependency

```json theme={null}
"@noble/secp256k1": "2.2.3"
```

### Generate Key Pair

```typescript theme={null}
import * as secp256k1 from "@noble/secp256k1"

export function generateAuthenticationKeyPair() {
  const privateKey = secp256k1.utils.randomSecretKey()
  const publicKey = secp256k1.getPublicKey(privateKey, true)
  
  return {
    privateKey: Buffer.from(privateKey).toString("hex"),
    publicKey: Buffer.from(publicKey).toString("hex"),
  }
}
```

## Authentication Flow

### Step 1: Generate Keys

```typescript theme={null}
const { privateKey, publicKey } = generateAuthenticationKeyPair()
```

### Step 2: Store Private Key

Store the private key securely:

**Client:** `localStorage`, `sessionStorage`, or in-memory\
**Server:** Database, Redis, session, or JWT

```typescript theme={null}
// Client example
sessionStorage.setItem('handcash_private_key', privateKey)

// Server example
await db.users.update(userId, { handcashPrivateKey: privateKey })
```

### Step 3: Redirect to HandCash

```typescript theme={null}
const authUrl = `https://handcash.io/connect?appId=YOUR_APP_ID&publicKey=${publicKey}`
window.location.href = authUrl
```

The user will log in and authorize your app on HandCash.

<Note>
  **Recommended: Use State Parameter for CSRF Protection**

  For enhanced security, include a `state` parameter to prevent CSRF attacks. See the [State Parameter Security](#state-parameter-security) section below.
</Note>

### Step 4: Handle Callback

After authorization, HandCash redirects to your **Authorization Success URL**. Retrieve the stored private key and validate:

```typescript theme={null}
import { getInstance, Connect } from '@handcash/sdk'

const privateKey = sessionStorage.getItem('handcash_private_key') // Retrieve stored key

const sdk = getInstance({ appId: 'YOUR_APP_ID', appSecret: 'YOUR_APP_SECRET' })
const client = sdk.getAccountClient(privateKey)

// Validate by fetching user profile
const { data: profile } = await Connect.getCurrentUserProfile({ client })
console.log('User authenticated:', profile.publicProfile.handle)
```

### Step 5: Store Private Key for Session

Associate the private key with the user's session:

```typescript theme={null}
req.session.handcashPrivateKey = privateKey
// or
await db.users.update(userId, { handcashPrivateKey: privateKey })
```

## How It Works

1. Generate a private/public key pair
2. Send the public key to HandCash during authorization
3. User authorizes your app on HandCash
4. Use the private key for all API calls - it serves as your authentication credential
5. HandCash validates the private key matches the authorized public key

## Check User Permissions

```typescript theme={null}
const { data } = await Connect.getPermissions({ client })
console.log('User granted permissions:', data?.items)
```

## Error Handling

```typescript theme={null}
try {
  const client = sdk.getAccountClient(privateKey)
  const profile = await Connect.getCurrentUserProfile({ client })
} catch (error) {
  if (error.message.includes('Invalid token')) {
    // Invalid private key, redirect to re-authenticate
    const { publicKey } = generateAuthenticationKeyPair()
    window.location.href = `https://handcash.io/connect?appId=${appId}&publicKey=${publicKey}`
  }
}
```

## State Parameter Security

<Warning>
  **Recommended:** Use the `state` parameter to prevent CSRF attacks and ensure the callback originated from your app session.
</Warning>

### What This Means in Practice

#### 1️⃣ App Generates State When Starting Connect

Generate a random, unguessable string and store it in your app session:

```typescript theme={null}
import crypto from 'crypto'

// Generate random state
const state = crypto.randomBytes(32).toString('hex')

// Store in session (server-side example)
req.session.handcashState = state

// Or client-side (sessionStorage)
sessionStorage.setItem('handcash_state', state)
```

Include the state in the redirect URL:

```typescript theme={null}
const authUrl = `https://handcash.io/connect?appId=YOUR_APP_ID&publicKey=${publicKey}&state=${state}`
window.location.href = authUrl
```

#### 2️⃣ Wallet Requires State

The HandCash wallet will:

* Require `state` to be present in the authorization request
* Echo the same `state` back in the callback URL
* Treat `state` as proof that the key came from the same app session that initiated connect

#### 3️⃣ Wallet Redirects Back with Same State

After authorization, HandCash redirects to your callback URL with the state included:

```
https://yourapp.com/auth/success?userId=...&publicKey=...&state=STATE
```

#### 4️⃣ App Validates State

Validate that the state matches the one stored in your session:

```typescript theme={null}
// Server-side example
app.get('/auth/success', async (req, res) => {
  const callbackState = req.query.state
  const sessionState = req.session.handcashState
  
  // Validate state matches
  if (callbackState !== sessionState) {
    return res.status(403).send('Invalid state parameter - possible CSRF attack')
  }
  
  // Clear state from session
  delete req.session.handcashState
  
  // Continue with authentication...
  const privateKey = req.session.handcashPrivateKey
  // ... rest of authentication flow
})
```

```typescript theme={null}
// Client-side example
const urlParams = new URLSearchParams(window.location.search)
const callbackState = urlParams.get('state')
const sessionState = sessionStorage.getItem('handcash_state')

if (callbackState !== sessionState) {
  console.error('Invalid state parameter - possible CSRF attack')
  return
}

// Clear state
sessionStorage.removeItem('handcash_state')

// Continue with authentication...
```

**If state mismatch → reject the request** - This indicates a potential CSRF attack.

## Security Best Practices

* **Use state parameter** - Always include and validate `state` to prevent CSRF attacks
* **Store private keys securely** - Use encrypted storage or secure sessions
* **Use HTTPS** - Always use HTTPS for redirect URLs
* **Validate private keys** - Check validity before making API calls
* **Protect the private key** - Treat it as sensitive authentication data
