Skip to content
RELKIT
Esc
↑↓navigate↵open⌘Jpreview
On this page

Authentication overview

Understand authentication, sessions, and authorization, and decide where each responsibility belongs in a RELKIT application.

Authentication tells your application who is making a request. A session lets that caller make subsequent requests without entering credentials again. Authorization decides what that identity may do: read an order, join an organization, or change another user’s settings. Signing in does not grant those permissions.

Keep these concepts separate from the authentication library you choose. An auth service owns identity and session integration; business domains own permission checks and product behavior. Your routes and clients connect those responsibilities without becoming a second authentication system.

Pre-setup

For a new app, first complete PostgreSQL in local Docker. Authentication shares that database. If your app already uses another supported dialect, keep it and pass the matching --database-dialect below.

Scaffold the authentication service

Run once in the backend root when it has no auth service:

bun run relkit add auth --adapter better-auth --database-dialect postgresql

The command installs the selected packages, adds auth tables to the existing Drizzle schema, creates the auth service and /api/auth mount, and merges environment declarations. It does not generate a secret, apply migrations, or protect your business endpoints.

Configure environment values

Keep these fields in src/platform/env.ts and its existing import in relkit.config.ts. This minimal inline configuration shows the required contract; merge rather than replacing your application’s other fields and settings:

/** Auth and Drizzle share one database; credentials remain in the backend environment. */
import { defineApp, defineEnv, env } from "@relkit/app/config";

export default defineApp({
  env: defineEnv({
    DATABASE_URL: env.secret(),
    BETTER_AUTH_URL: env.url(),
    BETTER_AUTH_SECRET: env.secret(),
  }),
});

Retain DATABASE_URL from the Docker setup. Generate a secret once:

openssl rand -base64 32

Add BETTER_AUTH_URL=http://127.0.0.1:3000 and BETTER_AUTH_SECRET=<generated value> to your untracked .env.local, using your actual backend origin and generated secret. Keep the same secret across restarts. Better Auth reads it from the backend process environment; defineEnv validates it but does not inject parsed values into native auth options. Never expose it to the frontend.

Apply auth tables and verify

bun run relkit env check
bun --bun drizzle-kit generate --name=add-auth

Review the generated changes, preserving existing tables and applied migrations. Then run:

bun run relkit db migrate
bun run check
bun run typecheck
bun run dev

Continue with Better Auth options and protecting application routes. Verify sign-up and cookies using the sign-in guide; its standalone SQLite sample is an alternative, so keep your PostgreSQL service and migrations when adapting its requests.

Update application HTTP fixtures to use @relkit/testing/better-auth, including fixtures for public routes. Supply explicit test environment values and an isolated, migrated database; the harness does not inherit .env.local. See testing authentication.

Organize authentication by responsibility

What you need to add Where it belongs Continue with
Sign-in methods, session policy, auth plugins src/services/auth/service.ts, using the chosen integration Better Auth service
Auth tables, connection, schema changes The existing database domain and migration tooling Database integration
Public authentication endpoints A filesystem route under src/routes/ Protect routes
The current caller in a business operation The owning domain’s function Read sessions
Ownership, roles, organization access The business operation that reads or changes the resource Authorization boundaries
Sign-up forms and sign-in state Your frontend and auth client’s public configuration Clients
Background work on behalf of a user The owning job/function, with an explicit trusted identity and policy Sessions outside HTTP

This separation keeps identity plumbing out of every domain. Changing an auth provider’s settings does not require moving Orders functions; changing an Orders permission rule does not require rewriting the login route. See Service organization for the wider application layout.

Understand the request boundary

Browser → auth endpoint → authentication service → persisted identity/session
Browser → application endpoint → session check → business function → permission check → data

The auth endpoint owns credential validation and session cookies. Application endpoints either allow anonymous callers or require a session. A business function uses the verified identity to enforce its own rules. Never treat an arbitrary input field, header, or client-side “logged in” flag as verified identity.

Public service functions can also be called outside HTTP. Protecting a route does not secure every job, tool, or direct invocation of the same function. Put the permission decision at the operation boundary and explicitly define which trusted non-HTTP callers may invoke it.

Choose the current integration

Better Auth is the supported auth service integration today. It uses the application’s Drizzle service, mounts a native HTTP handler, and supplies context.auth.getSession() to application functions. Other auth libraries do not gain this wiring just because their file is named service.ts; these guides do not promise a universal provider API.

The getting-started concepts apply generally. Better Auth options, cookie behavior, plugins, and table requirements belong to the integration guides. For a complete local flow, follow Your first auth flow.

After changing service or route registration, run from your app root:

bun run check
bun run typecheck

These check discovery and types, not real credentials, schema migrations, or browser cookie behavior.

Find the guide for your task

Configure the integration and secrets:

Read a nullable, request-bound identity:

Require authentication and enforce permissions:

Build sign-up, sign-in, and sign-out flows:

Add providers, plugins, fields, and production policy:

Verify sessions and failure cases:

Add authentication to an existing app:

Reference and source