# How Kaneo Handles Authentication: A Complete Technical Guide

> Discover how Kaneo handles authentication using JWT tokens, session cookies, API keys, and OAuth. Secure your API and web interface with this comprehensive technical guide.

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

---

**Kaneo uses Better Auth as its authentication foundation, combining JWT tokens, session cookies, API keys, and OAuth providers to secure both its API and React-based web interface.**

Kaneo is an open-source project management platform built by usekaneo. Understanding how Kaneo handles authentication is essential for contributors, self-hosters, and security auditors. This article breaks down the complete authentication architecture based on the actual source code implementation.

## Core Authentication Components

Kaneo's authentication stack relies on five primary mechanisms working together:

| Component | Purpose | Implementation |
|-----------|---------|----------------|
| **JWT tokens** | Stateless session proof | Signed with `AUTH_SECRET` (minimum 32 characters) from `.env` |
| **Session cookies** | Persistent browser sessions | HTTP-only, SameSite=Lax cookies managed by Better Auth |
| **API keys** | Programmatic access | Validated by [`authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/authenticate-api-request.ts) via `Authorization: Bearer <key>` header |
| **OAuth providers** | SSO login | GitHub, Google, Discord, and custom OIDC through environment variables |
| **Turnstile (CAPTCHA)** | Bot protection | Verified by [`verify-turnstile.ts`](https://github.com/usekaneo/kaneo/blob/main/verify-turnstile.ts) on sign-up and sensitive actions |

## Authentication Request Flow

Understanding the sequence of authentication checks helps debug issues and extend the system:

1. **Request enters the API** through [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts) where Better Auth middleware extracts the JWT cookie or Bearer token.

2. **Better Auth validates** the token and injects context—`c.get("user")` and `c.get("userId")` become available to downstream handlers.

3. **API-key validation** occurs in parallel for programmatic requests via [`authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/authenticate-api-request.ts), which may enforce additional scopes.

4. **Authorization checks** apply fine-grained permissions through utilities like [`authorize-asset-access.ts`](https://github.com/usekaneo/kaneo/blob/main/authorize-asset-access.ts) and [`require-workspace-permission.ts`](https://github.com/usekaneo/kaneo/blob/main/require-workspace-permission.ts).

5. **Frontend synchronization** happens through [`auth-client.ts`](https://github.com/usekaneo/kaneo/blob/main/auth-client.ts), which supplies session tokens to TanStack Query hooks and handles automatic refresh.

## Key Implementation Files

These source files define Kaneo's authentication behavior:

| File Path | Responsibility |
|-----------|----------------|
| [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts) | Central Better Auth configuration and middleware registration |
| [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts) | API key validation logic |
| [`apps/api/src/utils/authorize-asset-access.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authorize-asset-access.ts) | Asset permission enforcement |
| [`apps/web/src/lib/auth-client.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/lib/auth-client.ts) | React client wrapper for Better Auth |
| [`apps/web/src/routes/auth.tsx`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/routes/auth.tsx) | UI routes for login, registration, and SSO |
| [`apps/api/src/utils/verify-turnstile.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/verify-turnstile.ts) | CAPTCHA verification utility |

## Code Examples: Authentication in Practice

### Protecting an API Route

The Better Auth middleware automatically populates the Hono context with user information:

```typescript
// apps/api/src/some-feature/controllers/example.ts
import { Hono } from "hono";

export const example = new Hono()
  .get(
    "/protected",
    async (c) => {
      const userId = c.get("userId");
      if (!userId) {
        return c.json({ error: "Unauthenticated" }, 401);
      }
      // Business logic executes only for authenticated users
      return c.json({ message: `Hello user ${userId}` });
    },
  );

```

### Configuring the Frontend Auth Client

The shared client ensures consistent authentication behavior across the React application:

```typescript
// apps/web/src/lib/auth-client.ts
import { createAuthClient } from "@better-auth/react";
import { VITE_API_URL } from "@/config";

export const authClient = createAuthClient({
  apiUrl: VITE_API_URL,
  // Automatic token refresh is handled internally
});

```

### Protecting a React Component

Conditional rendering based on authentication state:

```tsx
// apps/web/src/routes/dashboard.tsx
import { useAuth } from "@/lib/auth-client";

export default function Dashboard() {
  const { user, loading } = useAuth();

  if (loading) return <Spinner />;
  if (!user) return <Redirect to="/login" />;

  return <div>Welcome, {user.email}!</div>;
}

```

## Security Mechanisms

Kaneo implements multiple defense layers to protect authentication data:

- **HTTP-only cookies** prevent JavaScript access to session tokens, mitigating XSS attacks
- **SameSite=Lax** cookie policy restricts cross-origin request contexts
- **JWT signatures** guarantee token integrity without database lookups
- **Hashed API keys** stored in the database support revocation at any time
- **Turnstile integration** blocks automated account creation attempts
- **Role-based access control** enforced through workspace membership checks

## Environment Configuration

Authentication behavior is controlled through environment variables:

```bash

# Required: Minimum 32-character secret for JWT signing

AUTH_SECRET=your-super-secret-32-char-minimum-key

# OAuth provider credentials (optional)

GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx

# Turnstile for bot protection

TURNSTILE_SECRET_KEY=xxx

```

## Summary

- **Better Auth** provides the foundational authentication library for Kaneo's entire stack
- **Dual token system** separates browser sessions (JWT cookies) from API access (Bearer keys)
- **Middleware-based validation** in [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts) injects user context into all API routes
- **Fine-grained authorization** utilities enforce workspace and asset-level permissions
- **React integration** through [`auth-client.ts`](https://github.com/usekaneo/kaneo/blob/main/auth-client.ts) provides automatic session management
- **Security-hardened defaults** include HTTP-only cookies, JWT signing, and CAPTCHA protection

## Frequently Asked Questions

### What authentication library does Kaneo use?

Kaneo uses **Better Auth**, a flexible TypeScript authentication library that supports multiple login methods including credentials, OAuth providers, and API keys. The integration is configured in [`apps/api/src/auth.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth.ts) and consumed by the React frontend through `@better-auth/react`.

### How does Kaneo validate API requests from external services?

External services authenticate using **API keys** passed in the `Authorization: Bearer <key>` header. The [`apps/api/src/utils/authenticate-api-request.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/authenticate-api-request.ts) utility validates these keys against hashed database records and can enforce additional permission scopes beyond standard user sessions.

### Where are authentication routes defined in the Kaneo codebase?

All user-facing authentication UI—login, registration, password reset, and OAuth callbacks—lives in [`apps/web/src/routes/auth.tsx`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/routes/auth.tsx). These routes use the shared [`auth-client.ts`](https://github.com/usekaneo/kaneo/blob/main/auth-client.ts) to communicate with the Better Auth backend, ensuring consistent behavior between server and client authentication state.

### How does Kaneo prevent automated account creation?

Kaneo integrates **Cloudflare Turnstile** for invisible CAPTCHA protection. The [`apps/api/src/utils/verify-turnstile.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/utils/verify-turnstile.ts) utility validates Turnstile tokens on registration and other sensitive actions, blocking bots without degrading legitimate user experience.