# How Kaneo Handles API Authentication: Better Auth Implementation Explained

> Discover how Kaneo handles API authentication using Better Auth. Learn about global middleware for Bearer tokens and API keys with the x-api-key header.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: deep-dive
- Published: 2026-08-29

---

**Kaneo uses Better Auth with a global `authenticateApiRequest` middleware that supports both Bearer session tokens and API keys via the `x-api-key` header.**

Kaneo is an open-source project management platform built by `usekaneo/kaneo`. Understanding how Kaneo handles API authentication is essential for developers integrating with its REST API or deploying self-hosted instances. This guide breaks down the authentication flow, middleware implementation, and configuration based on the actual source code.

---

## Better Auth Core Configuration

Kaneo's authentication foundation lives in [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts). This file exports a configured `betterAuth` instance that powers the entire API security layer.

The configuration enables multiple authentication methods through a plugin architecture:

- **`bearer()`** — Accepts standard Bearer tokens for session-based authentication
- **`apiKey({ apiKeyHeaders: "x-api-key", ... })`** — Enables API key authentication with optional rate limiting
- **`deviceAuthorization`** — Supports OAuth device flow for CLI and automation clients
- **`magicLink`, `emailOTP`, `genericOAuth`** — Provide passwordless sign-in options
- **`anonymous`** — Creates temporary guest accounts when guest access is enabled
- **`adminPlugin`** — Grants admin role based on database role fields
- **`openAPI()`** — Exposes authentication metadata for API documentation

---

## Global Authentication Middleware

All protected API routes flow through the **`authenticateApiRequest`** middleware defined in [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts). This middleware is registered globally in [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts) via:

```typescript
api.use("*", async (c, next) => {
  await authenticateApiRequest(c);
  await next();
});

```

### Authentication Flow

The middleware executes a prioritized credential check:

1. **Credential extraction** — Parses the `Authorization` header for Bearer tokens and checks `x-api-key` for API keys
2. **API key verification** — Calls `verifyApiKey` (from [`apps/api/src/utils/verify-api-key.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/verify-api-key.ts)) when an API key is present; valid keys inject `userId`, `apiKey`, and clear session data
3. **Session resolution** — Attempts `auth.api.getSession` via Better Auth when no API key is found
4. **Token type handling** — Bearer tokens first try API key lookup (allowing tokens to serve dual purpose), then fall back to session retrieval with cookie stripping
5. **Cookie fallback** — Uses `getSession(c.req.raw.headers)` when no Bearer token is supplied

Invalid or missing credentials trigger a `401 Unauthorized` HTTPException.

### Context Population

Successful authentication sets these Hono context fields for downstream handlers:

| Field | Description |
|-------|-------------|
| `userId` | Authenticated user's UUID (from session or API key) |
| `user` | Full `User` object (session-based auth only) |
| `session` | Better Auth session record |
| `apiKey` | API key metadata when key-based auth is used |
| `userEmail` | Convenience copy of user's email (empty for API key auth) |

---

## Using Authentication in API Routes

### Manual Middleware Application

For fine-grained control, call `authenticateApiRequest` directly within handlers:

```typescript
import { authenticateApiRequest } from "./utils/authenticate-api-request";

const handler = async (c: Context) => {
  await authenticateApiRequest(c);
  const userId = c.get("userId");
  const apiKey = c.get("apiKey");
  // Proceed with authorized operations
};

```

### Automatic Protection

Routes inherit authentication automatically through the global middleware:

```typescript
api.get("/task/:id", async (c) => {
  const taskId = c.req.param("id");
  const userId = c.get("userId"); // Already validated
  // Fetch task with authorized user context
});

```

---

## Client-Side Authentication Examples

### API Key Authentication

Include your API key in the `x-api-key` header:

```typescript
fetch("https://kaneo.example.com/api/task/123", {
  headers: {
    "x-api-key": "sk_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  },
});

```

### Bearer Session Token

Use JWT session tokens after login:

```typescript
fetch("https://kaneo.example.com/api/task/123", {
  headers: {
    "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
  },
});

```

---

## OpenAPI Security Scheme

The OpenAPI specification in [`apps/api/src/openapi.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/openapi.ts) declares a global **Bearer** security scheme that maps to this authentication system. This ensures consistent documentation and client generation across all protected endpoints.

Additional security-related logic includes:

- **[`apps/api/src/utils/check-registration-allowed.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/check-registration-allowed.ts)** — Sign-up gating with invitation IDs and disposable email checks
- **OAuth device flow** — For secure CLI authentication without browser redirects

---

## Summary

- Kaneo builds on **Better Auth** with a plugin-extensible architecture in [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts)
- **Global middleware** `authenticateApiRequest` centralizes all API authentication logic
- **Dual credential support**: API keys (`x-api-key` header) and Bearer session tokens
- **Context injection** provides downstream handlers with `userId`, `user`, `session`, and `apiKey` fields
- **Automatic route protection** via `api.use("*", ...)` in [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts)
- **OpenAPI integration** ensures consistent security documentation

---

## Frequently Asked Questions

### What authentication methods does Kaneo support?

Kaneo supports session-based authentication via Bearer tokens, API key authentication via the `x-api-key` header, OAuth device flow, magic links, email OTP, and anonymous guest access. The `genericOAuth` plugin enables integration with external identity providers.

### How do I authenticate API requests to Kaneo?

Pass either an `x-api-key` header with a valid API key or an `Authorization: Bearer <token>` header with a session token. The global middleware in [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts) automatically validates credentials and populates request context.

### Where is the API authentication configured in Kaneo?

The main configuration is in [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts), which instantiates Better Auth with plugins for bearer tokens, API keys, and various sign-in methods. The global middleware is defined in [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts) and registered in [`apps/api/src/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/index.ts).

### What happens when authentication fails?

The `authenticateApiRequest` middleware throws a `401 Unauthorized` HTTPException for invalid, missing, or malformed credentials. This ensures consistent error handling across all protected endpoints without requiring manual checks in individual route handlers.