# Authentication

## Authentication

### Overview

The SDK uses JWT token-based authentication to keep your API credentials secure. Instead of exposing your Client ID and Secret in browser code, tokens are generated server-side by your backend.

***

### Why Token-Based Authentication?**Security Benefits:**

* API credentials (Client ID/Secret) never leave your server
* Tokens are scoped to specific customers and invoices
* Tokens have limited lifetime, reducing exposure risk
* Token refresh is handled automatically by the SDK\*\*\*

### Authentication Flow

```
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Your           │     │  Your           │     │  Alternative    │
│  Backend        │     │  Frontend       │     │  API            │
└────────┬────────┘     └────────┬────────┘     └────────┬────────┘
         │                       │                       │
         │  1. POST /v1/checkout-auth/init              │
         │  (with OAuth2 client credentials)            │
         │──────────────────────────────────────────────>│
         │                       │                       │
         │  2. Returns JWT token                        │
         │<──────────────────────────────────────────────│
         │                       │                       │
         │  3. Pass token to frontend                   │
         │──────────────────────>│                       │
         │                       │                       │
         │                       │  4. SDK uses token    │
         │                       │  for all API calls    │
         │                       │──────────────────────>│
         │                       │                       │
         │                       │  5. Token expires     │
         │                       │  onAccessTokenExpired │
         │                       │  callback triggered   │
         │                       │<─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│
         │                       │                       │
         │  6. Frontend requests │                       │
         │  new token            │                       │
         │<──────────────────────│                       │
         │                       │                       │
         │  7. New token returned│                       │
         │──────────────────────>│                       │
         │                       │                       │
         │                       │  8. SDK continues     │
         │                       │  with new token       │
         │                       │──────────────────────>│
```

***

### Backend Implementation

#### Step 1: Get OAuth Token

First, exchange your Client ID and Secret for an OAuth access token:

```bash
curl -X POST https://public-api.alternativepayments.io/oauth/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Authorization: Basic $(echo -n 'CLIENT_ID:CLIENT_SECRET' | base64)" \
  -d "grant_type=client_credentials"
```

Response:

```json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}
```

#### Step 2: Generate Checkout Token

Use the OAuth token to generate a checkout token for a specific customer and invoice:

```bash
curl -X POST https://public-api.alternativepayments.io/v1/checkout-auth/init \
  -H "Authorization: Bearer {oauth_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "cus_xxx",
    "invoice_id": "inv_xxx"
  }'
```

Response:

```json
{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_at": 1703980800
}
```

#### Complete Backend Example\`\`\`typescript

import express from 'express';

const router = express.Router();

const CLIENT\_ID = process.env.ALTERNATIVE\_CLIENT\_ID; const CLIENT\_SECRET = process.env.ALTERNATIVE\_CLIENT\_SECRET; const API\_BASE = '<https://public-api.alternativepayments.io>';

// Cache OAuth token let oauthToken: { token: string; expiresAt: number } | null = null;

async function getOAuthToken(): Promise { // Return cached token if valid if (oauthToken && oauthToken.expiresAt > Date.now() + 60000) { return oauthToken.token; }

const response = await fetch(`${API_BASE}/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': `Basic ${Buffer.from(`${CLIENT\_ID}:${CLIENT\_SECRET}`).toString('base64')}`, }, body: 'grant\_type=client\_credentials', });

const data = await response.json(); oauthToken = { token: data.access\_token, expiresAt: Date.now() + data.expires\_in \* 1000, };

return data.access\_token; }

router.post('/api/checkout-token', async (req, res) => { try { const { customerId, invoiceId } = req.body;

```
// Validate inputs
if (!customerId || !invoiceId) {
  return res.status(400).json({ error: 'customerId and invoiceId are required' });
}

// Get OAuth token
const accessToken = await getOAuthToken();

// Generate checkout token
const response = await fetch(`${API_BASE}/v1/checkout-auth/init`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    customer_id: customerId,
    invoice_id: invoiceId,
  }),
});

if (!response.ok) {
  const error = await response.json();
  return res.status(response.status).json(error);
}

const data = await response.json();
res.json({
  token: data.token,
  expiresAt: data.expires_at,
});
```

} catch (error) { console.error('Failed to generate checkout token:', error); res.status(500).json({ error: 'Failed to generate token' }); } });

export default router; `</div><div data-gb-custom-block data-tag="tab" data-title='Python (FastAPI)'>`python from fastapi import FastAPI, HTTPException from pydantic import BaseModel import httpx import os import base64 import time

app = FastAPI()

CLIENT\_ID = os.environ\["ALTERNATIVE\_CLIENT\_ID"] CLIENT\_SECRET = os.environ\["ALTERNATIVE\_CLIENT\_SECRET"] API\_BASE = "<https://public-api.alternativepayments.io>"

## Token cache

oauth\_token = {"token": None, "expires\_at": 0}

async def get\_oauth\_token() -> str: global oauth\_token

```
# Return cached token if valid
if oauth_token["token"] and oauth_token["expires_at"] > time.time() + 60:
    return oauth_token["token"]

credentials = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()

async with httpx.AsyncClient() as client:
    response = await client.post(
        f"{API_BASE}/oauth/token",
        headers={
            "Content-Type": "application/x-www-form-urlencoded",
            "Authorization": f"Basic {credentials}",
        },
        data="grant_type=client_credentials",
    )

data = response.json()
oauth_token = {
    "token": data["access_token"],
    "expires_at": time.time() + data["expires_in"],
}

return data["access_token"]
```

class TokenRequest(BaseModel): customer\_id: str invoice\_id: str

@app.post("/api/checkout-token") async def generate\_checkout\_token(request: TokenRequest): try: access\_token = await get\_oauth\_token()

```
    async with httpx.AsyncClient() as client:
        response = await client.post(
            f"{API_BASE}/v1/checkout-auth/init",
            headers={
                "Authorization": f"Bearer {access_token}",
                "Content-Type": "application/json",
            },
            json={
                "customer_id": request.customer_id,
                "invoice_id": request.invoice_id,
            },
        )

    if response.status_code != 200:
        raise HTTPException(status_code=response.status_code, detail=response.json())

    data = response.json()
    return {"token": data["token"], "expiresAt": data["expires_at"]}

except Exception as e:
    raise HTTPException(status_code=500, detail=str(e))
```

````</div></div>***

## Frontend Implementation

### Initialize with Token

```typescript
import { AlternativeClient } from '@getalternative/partner-sdk';

// Fetch token from your backend
const { token } = await fetch('/api/checkout-token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
}).then(res => res.json());

// Initialize SDK
const client = await AlternativeClient.create({
  accessToken: token,
  environment: 'production',
});
````

#### Handle Token Expiration

Provide an `onAccessTokenExpired` callback to automatically refresh tokens:

````typescript
const client = await AlternativeClient.create({
  accessToken: token,
  environment: 'production',
  onAccessTokenExpired: async () => {
    // This is called when the SDK receives a 401 response
    console.log('Token expired, fetching new one...');

    const response = await fetch('/api/checkout-token', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
    });
    const { token } = await response.json();

    return token; // Return the new token
  },
});
```<div data-gb-custom-block data-tag="hint" data-style='warning'>If `onAccessTokenExpired` is not provided and the token expires, the SDK will throw an `AccessTokenExpiredError`.</div>***

## Error Handling

```typescript
import { AccessTokenExpiredError } from '@getalternative/partner-sdk';

try {
  // SDK operations
} catch (error) {
  if (error instanceof AccessTokenExpiredError) {
    // Token expired and no refresh callback was provided
    // Redirect user to re-authenticate or show error
    console.error('Session expired. Please refresh the page.');
  }
}
````

***

### Security Best Practices

1. **Never expose credentials in frontend code** - Always generate tokens on your backend
2. **Use HTTPS** - Ensure all communication is encrypted
3. **Validate on your backend** - Verify the customer/invoice belongs to the authenticated user
4. **Set appropriate token lifetimes** - Shorter lifetimes reduce exposure risk
5. **Implement the refresh callback** - Provide a smooth experience when tokens expire

***

### Token Contents

The checkout token is a JWT that contains:

| Claim         | Description                          |
| ------------- | ------------------------------------ |
| `partner_id`  | Your partner identifier              |
| `client_id`   | Your API client ID                   |
| `customer_id` | The customer this token is scoped to |
| `invoice_id`  | The invoice this token is scoped to  |
| `exp`         | Token expiration timestamp           |


---

# 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://docs.alternativepayments.io/web-sdk/authentication.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.
