Authentication

Agent-native apps use Better Auth for authentication with an account-first design. Users create an account on first visit and get real identity from day one.

Overview

Auth is configured automatically via autoMountAuth(app) in the auth server plugin. The behavior depends on your environment:

  • Default: Better Auth with email/password + social providers. Onboarding page shown on first visit.
  • AUTH_MODE=local: No auth. Solo local development with local@localhost identity.
  • ACCESS_TOKEN: Simple shared token for production.
  • Custom: Bring your own auth via getSession callback.

Better Auth (Default)

When no ACCESS_TOKEN or AUTH_MODE=local is set, Better Auth powers authentication. It provides:

  • Email/password registration and login
  • Social providers (Google, GitHub, and 35+ others)
  • Organizations with roles and invitations
  • JWT tokens for API and A2A access
  • Bearer token support for programmatic clients

Better Auth routes are mounted at /_agent-native/auth/ba/*. The framework also provides backward-compatible endpoints:

  • GET /_agent-native/auth/session — get current session
  • POST /_agent-native/auth/login — email/password or token login
  • POST /_agent-native/auth/register — create account
  • POST /_agent-native/auth/logout — sign out

Local Mode

For solo local development without auth, set AUTH_MODE=local in your .env file. This returns { email: "local@localhost" } for all requests.

You can also enable local mode from the onboarding page by clicking "Use locally without an account". This writes AUTH_MODE=local to your .env automatically.

# .env
AUTH_MODE=local

Local mode works in any environment (dev or production). To switch back to real auth, remove the line from .env.

Social Providers

Set environment variables to enable social login. Better Auth auto-detects them:

# Google OAuth
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_CLIENT_SECRET=your-client-secret

# GitHub OAuth
GITHUB_CLIENT_ID=your-client-id
GITHUB_CLIENT_SECRET=your-client-secret

Templates that use createGoogleAuthPlugin() show a "Sign in with Google" page. The Google OAuth callback handles mobile deep linking for native apps automatically.

Organizations

Better Auth's organization plugin is built into the framework. Every app supports:

  • Creating organizations
  • Inviting members with roles (owner, admin, member)
  • Switching active organization
  • Per-org data scoping via org_id columns

The active organization flows automatically through the system: session.orgIdAGENT_ORG_ID → SQL scoping. See the Security & Data Scoping docs for details.

Access Tokens

For simple deployments, set ACCESS_TOKEN (single) or ACCESS_TOKENS (comma-separated) as environment variables:

# Single token
ACCESS_TOKEN=my-secret-token

# Multiple tokens
ACCESS_TOKENS=token1,token2,token3

When access tokens are configured, users see a token login page. Sessions are cookie-based with 30-day expiry.

Bring Your Own Auth

Pass a custom getSession callback to use any auth provider (Clerk, Auth0, Firebase, etc.):

// server/plugins/auth.ts
import { createAuthPlugin } from "@agent-native/core/server";

export default createAuthPlugin({
  getSession: async (event) => {
    // Your custom auth logic here
    const session = await myAuthProvider.verify(event);
    if (!session) return null;
    return { email: session.email };
  },
  publicPaths: ["/api/webhooks"],
});

Session API

The session object returned by getSession(event) has this shape:

interface AuthSession {
  email: string; // User's email (primary identifier)
  userId?: string; // Better Auth user ID
  token?: string; // Session token
  orgId?: string; // Active organization ID
  orgRole?: string; // Role in active org (owner/admin/member)
}

On the client, use the useSession() hook:

import { useSession } from "@agent-native/core/client";

function MyComponent() {
  const { session, isLoading } = useSession();
  if (isLoading) return <p>Loading...</p>;
  if (!session) return <p>Not signed in</p>;
  return <p>Hello, {session.email}</p>;
}

Environment Variables

Variable Purpose
AUTH_MODE Set to local to disable auth
BETTER_AUTH_SECRET Signing key for Better Auth (auto-generated if not set)
GOOGLE_CLIENT_ID Enable Google OAuth
GOOGLE_CLIENT_SECRET Google OAuth secret
GITHUB_CLIENT_ID Enable GitHub OAuth
GITHUB_CLIENT_SECRET GitHub OAuth secret
ACCESS_TOKEN Simple shared token auth
ACCESS_TOKENS Comma-separated shared tokens
AUTH_DISABLED Set to true to skip auth (infrastructure-level auth)
A2A_SECRET Shared secret for JWT-signed A2A cross-app identity verification