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 withlocal@localhostidentity.ACCESS_TOKEN: Simple shared token for production.- Custom: Bring your own auth via
getSessioncallback.
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 sessionPOST /_agent-native/auth/login— email/password or token loginPOST /_agent-native/auth/register— create accountPOST /_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_idcolumns
The active organization flows automatically through the system: session.orgId → AGENT_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 |