How Kaneo Handles API Authentication: Better Auth Implementation Explained
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. 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 authenticationapiKey({ apiKeyHeaders: "x-api-key", ... })— Enables API key authentication with optional rate limitingdeviceAuthorization— Supports OAuth device flow for CLI and automation clientsmagicLink,emailOTP,genericOAuth— Provide passwordless sign-in optionsanonymous— Creates temporary guest accounts when guest access is enabledadminPlugin— Grants admin role based on database role fieldsopenAPI()— 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. This middleware is registered globally in apps/api/src/index.ts via:
api.use("*", async (c, next) => {
await authenticateApiRequest(c);
await next();
});
Authentication Flow
The middleware executes a prioritized credential check:
- Credential extraction — Parses the
Authorizationheader for Bearer tokens and checksx-api-keyfor API keys - API key verification — Calls
verifyApiKey(fromapps/api/src/utils/verify-api-key.ts) when an API key is present; valid keys injectuserId,apiKey, and clear session data - Session resolution — Attempts
auth.api.getSessionvia Better Auth when no API key is found - 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
- 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:
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:
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:
fetch("https://kaneo.example.com/api/task/123", {
headers: {
"x-api-key": "sk_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
},
});
Bearer Session Token
Use JWT session tokens after login:
fetch("https://kaneo.example.com/api/task/123", {
headers: {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
},
});
OpenAPI Security Scheme
The OpenAPI specification in 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— 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 - Global middleware
authenticateApiRequestcentralizes all API authentication logic - Dual credential support: API keys (
x-api-keyheader) and Bearer session tokens - Context injection provides downstream handlers with
userId,user,session, andapiKeyfields - Automatic route protection via
api.use("*", ...)inapps/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 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, 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 and registered in 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →