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

# Authentication

> HMAC-based authentication for ogis

export const SecretGenerator = () => {
  const [secret, setSecret] = useState('');
  const [copied, setCopied] = useState(false);
  const generate = () => {
    const bytes = new Uint8Array(32);
    crypto.getRandomValues(bytes);
    setSecret(Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join(''));
    setCopied(false);
  };
  const copy = () => {
    navigator.clipboard.writeText(secret).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }).catch(err => console.error("Failed to copy:", err));
  };
  useEffect(() => {
    generate();
  }, []);
  const RefreshIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
      <path d="M3 3v5h5" />
      <path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" />
      <path d="M16 16h5v5" />
    </svg>;
  const CopyIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
      <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
    </svg>;
  const CheckIcon = () => <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M20 6 9 17l-5-5" />
    </svg>;
  return <div className="not-prose">
      <div className="flex items-center space-x-2">
        <code className="flex-1 text-sm font-mono text-zinc-950/70 dark:text-white/70 bg-zinc-950/5 dark:bg-white/5 px-3 py-2 rounded-lg overflow-x-auto">
          {secret}
        </code>
        <button onClick={generate} title="Regenerate" className="p-2 rounded-lg bg-zinc-950/10 dark:bg-white/10 text-zinc-950/70 dark:text-white/70 hover:bg-zinc-950/20 dark:hover:bg-white/20 transition-colors cursor-pointer">
          <RefreshIcon />
        </button>
        <button onClick={copy} title={copied ? "Copied!" : "Copy"} className="p-2 rounded-lg bg-zinc-950 dark:bg-white text-white dark:text-zinc-950 hover:bg-zinc-950/80 dark:hover:bg-white/80 transition-colors cursor-pointer">
          {copied ? <CheckIcon /> : <CopyIcon />}
        </button>
      </div>
    </div>;
};

Secure your ogis instance with HMAC-SHA256 signature validation. When enabled, all requests must include a valid signature parameter.

## When to Use Authentication

* **Private instances** — Prevent unauthorized usage of your self-hosted ogis
* **Rate limiting** — Control who can generate images
* **Usage tracking** — Identify requests by signature

<Info>
  The public hosted service at `img.ogis.dev` does not require authentication.
</Info>

## Enabling Authentication

Set the `OGIS_HMAC_SECRET` environment variable on your server:

<Tabs>
  <Tab title="Docker">
    ```bash theme={null}
    docker run -d \
      -p 3000:3000 \
      -e OGIS_HMAC_SECRET=your-secret-key-here \
      twango/ogis:latest
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    ogis --hmac-secret your-secret-key-here
    ```
  </Tab>
</Tabs>

Choose a strong, random secret (32+ characters recommended):

<Tabs>
  <Tab title="Generator">
    <SecretGenerator />
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    openssl rand -hex 32
    ```
  </Tab>
</Tabs>

## How It Works

1. Client constructs query parameters (e.g., `title=Hello&template=twilight`)
2. Parameters are sorted alphabetically and concatenated
3. HMAC-SHA256 signature is computed using the secret
4. Signature is appended to the URL as `&signature=...`
5. Server verifies the signature before generating the image

## Using the SDK

The SDK handles signing automatically when you provide `hmacSecret`:

```typescript theme={null}
import { OgisClient } from 'ogis';

const ogis = new OgisClient({
  baseUrl: 'https://ogis.example.com',
  hmacSecret: process.env.OGIS_SECRET  // Keep this secret!
});

// URLs are automatically signed
const url = ogis.generateUrl({
  title: 'My Secure Image',
  template: 'twilight'
});

// => https://ogis.example.com/?template=twilight&title=My+Secure+Image&signature=a1b2c3...
```

## Manual Signing

If you're not using the SDK, compute the signature manually.

### Algorithm

1. Collect all query parameters except `signature`
2. Sort parameters alphabetically by key
3. URL-encode and concatenate as `key=value&key=value`
4. Compute HMAC-SHA256 of the string using your secret
5. Hex-encode the result

### Implementation Examples

<Tabs>
  <Tab title="Node.js">
    ```typescript theme={null}
    import crypto from 'crypto';

    function signOgisUrl(params: Record<string, string>, secret: string): string {
      // Sort parameters alphabetically
      const sortedKeys = Object.keys(params).sort();

      // Build canonical query string
      const canonical = sortedKeys
        .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
        .join('&');

      // Compute HMAC-SHA256
      const signature = crypto
        .createHmac('sha256', secret)
        .update(canonical)
        .digest('hex');

      return signature;
    }

    // Usage
    const params = { title: 'Hello', template: 'twilight' };
    const signature = signOgisUrl(params, 'your-secret');
    const url = `https://ogis.example.com/?title=Hello&template=twilight&signature=${signature}`;
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import hmac
    import hashlib
    from urllib.parse import urlencode

    def sign_ogis_url(params: dict, secret: str) -> str:
        # Sort and encode parameters
        sorted_params = sorted(params.items())
        canonical = urlencode(sorted_params)

        # Compute HMAC-SHA256
        signature = hmac.new(
            secret.encode(),
            canonical.encode(),
            hashlib.sha256
        ).hexdigest()

        return signature

    # Usage
    params = {'title': 'Hello', 'template': 'twilight'}
    signature = sign_ogis_url(params, 'your-secret')
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    package main

    import (
        "crypto/hmac"
        "crypto/sha256"
        "encoding/hex"
        "net/url"
        "sort"
    )

    func signOgisURL(params map[string]string, secret string) string {
        // Sort keys
        keys := make([]string, 0, len(params))
        for k := range params {
            keys = append(keys, k)
        }
        sort.Strings(keys)

        // Build canonical string
        values := url.Values{}
        for _, k := range keys {
            values.Set(k, params[k])
        }
        canonical := values.Encode()

        // Compute HMAC-SHA256
        h := hmac.New(sha256.New, []byte(secret))
        h.Write([]byte(canonical))
        return hex.EncodeToString(h.Sum(nil))
    }
    ```
  </Tab>
</Tabs>

## Error Responses

When authentication is enabled, invalid requests return:

| Status             | Reason                        |
| ------------------ | ----------------------------- |
| `401 Unauthorized` | Missing `signature` parameter |
| `401 Unauthorized` | Invalid signature             |

## Security Best Practices

<Warning>
  **Never expose your secret in client-side code or version control.**
</Warning>

1. **Use environment variables** — Store secrets in `OGIS_SECRET` or similar
2. **Rotate secrets periodically** — Update your secret and redeploy
3. **Use HTTPS** — Always serve your ogis instance over HTTPS to prevent signature interception

## Server-Side Only

Generate signed URLs on your server, not in the browser:

<Tabs>
  <Tab title="Next.js API Route">
    ```typescript theme={null}
    // pages/api/og-image.ts
    import { OgisClient } from 'ogis';

    const ogis = new OgisClient({
      baseUrl: process.env.OGIS_URL!,
      hmacSecret: process.env.OGIS_SECRET!
    });

    export default function handler(req, res) {
      const { title } = req.query;
      const url = ogis.generateUrl({ title, template: 'twilight' });
      res.json({ url });
    }
    ```
  </Tab>

  <Tab title="SvelteKit">
    ```typescript theme={null}
    // +page.server.ts
    import { OgisClient } from 'ogis';
    import { OGIS_SECRET } from '$env/static/private';

    const ogis = new OgisClient({
      baseUrl: 'https://ogis.example.com',
      hmacSecret: OGIS_SECRET
    });

    export function load({ params }) {
      return {
        ogImage: ogis.generateUrl({ title: params.title })
      };
    }
    ```
  </Tab>
</Tabs>
