> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> better-auth based authentication settings

Sonamu provides an authentication system based on [better-auth](https://www.better-auth.com/). It supports various authentication methods including email/password authentication and social login, with authentication APIs automatically registered at `/api/auth/*`.

## Basic Structure

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    // Enable authentication with default settings
    auth: {
      emailAndPassword: {
        enabled: true,
      },
    },

    // Or detailed configuration
    auth: {
      basePath: "/api/auth",
      emailAndPassword: {
        enabled: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
      },
    },
  },
  // ...
});
```

## auth Settings

### Type

**Type**: `BetterAuthOptions` (configuration type from better-auth library)

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      // better-auth configuration options
    },
  },
});
```

### Email/Password Authentication

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
        // Optional: minimum password length
        minPasswordLength: 8,
      },
    },
  },
});
```

### Social Login (Google)

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
      },
    },
  },
});
```

### Social Login (GitHub)

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      socialProviders: {
        github: {
          clientId: process.env.GITHUB_CLIENT_ID!,
          clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        },
      },
    },
  },
});
```

### Customizing basePath

The default path is `/api/auth`. To change it:

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      basePath: "/auth", // Change to /auth/*
      emailAndPassword: {
        enabled: true,
      },
    },
  },
});
```

## Entity Generation

You need to generate the required entities before using better-auth.

### Generate Entities via CLI

```bash theme={null}
pnpm sonamu better-auth
```

This command generates the following entities:

| Entity       | Table         | Description              |
| ------------ | ------------- | ------------------------ |
| User         | users         | User information         |
| Session      | sessions      | Session information      |
| Account      | accounts      | OAuth account linking    |
| Verification | verifications | Email verification, etc. |

<Note>
  If a User entity already exists, only missing fields will be added when running the command.
</Note>

### Generate Entities with Plugins

To use better-auth plugins, specify the required plugins with the `--plugins` option:

```bash theme={null}
# Single plugin
pnpm sonamu better-auth --plugins=2fa

# Multiple plugins (comma-separated)
pnpm sonamu better-auth --plugins=2fa,admin,username
```

Supported plugins:

| Plugin ID      | Description                      | Added Fields/Tables                                                                                                   |
| -------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `2fa`          | Two-Factor Authentication (TOTP) | TwoFactor table, User.two\_factor\_enabled                                                                            |
| `admin`        | Admin features                   | User.role, User.banned, User.ban\_reason, User.ban\_expires, Session.impersonated\_by                                 |
| `username`     | Username login                   | User.username (unique), User.display\_username                                                                        |
| `phone-number` | Phone number verification        | User.phone\_number (unique), User.phone\_number\_verified                                                             |
| `passkey`      | WebAuthn passkey authentication  | Passkey table                                                                                                         |
| `sso`          | SSO login (OIDC/SAML)            | SsoProvider table                                                                                                     |
| `api-key`      | API key authentication           | ApiKey table                                                                                                          |
| `jwt`          | JWT token issuance               | Jwks table                                                                                                            |
| `organization` | Organization/team management     | Organization, Member, Invitation, Team, TeamMember tables, Session.active\_organization\_id, Session.active\_team\_id |
| `anonymous`    | Anonymous users                  | User.is\_anonymous                                                                                                    |

<Tip>
  When using plugins, you also need to enable them in `sonamu.config.ts`. See the [Plugin
  Configuration](#plugin-configuration) section below for details.
</Tip>

### Field Mapping

Since Sonamu uses snake\_case column names, better-auth's camelCase field names are automatically mapped:

```typescript theme={null}
// better-auth → Sonamu
emailVerified → email_verified
createdAt → created_at
updatedAt → updated_at
ipAddress → ip_address
userAgent → user_agent
userId → user_id
// ...
```

## Authentication APIs

Once better-auth is registered, the following APIs become automatically available:

### Sign Up

```
POST /api/auth/sign-up/email
Content-Type: application/json

{
  "name": "John Doe",
  "email": "user@example.com",
  "password": "password123"
}
```

### Sign In

```
POST /api/auth/sign-in/email
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "password123"
}
```

### Sign Out

```
POST /api/auth/sign-out
```

### Current Session

```
GET /api/auth/get-session
```

### Social Login (Google)

```
GET /api/auth/sign-in/social?provider=google
```

<Tip>
  For the complete API list, refer to the [better-auth
  documentation](https://www.better-auth.com/docs/api-reference).
</Tip>

## Accessing User Information from Context

In authenticated requests, you can access user information through Context.

```typescript theme={null}
import { api, Sonamu } from "sonamu";

export class MyModel {
  @api()
  static async myApi() {
    const ctx = Sonamu.getContext();

    // Currently logged-in user
    const user = ctx.user; // User | null

    // Current session information
    const session = ctx.session; // Session | null

    if (!user) {
      throw new UnauthorizedError("Login required");
    }

    return { userId: user.id, userName: user.name };
  }
}
```

### User Type

```typescript theme={null}
type User = {
  id: string;
  name: string;
  email: string;
  emailVerified: boolean;
  image: string | null;
  createdAt: Date;
  updatedAt: Date;
};
```

### Session Type

```typescript theme={null}
type Session = {
  id: string;
  expiresAt: Date;
  token: string;
  createdAt: Date;
  updatedAt: Date;
  ipAddress: string | null;
  userAgent: string | null;
  userId: string;
};
```

## Access Control with Guards

```typescript theme={null}
import { api } from "sonamu";

export class AdminModel {
  @api({ guards: ["admin"] })
  static async adminOnly() {
    // Executes only if admin guard passes
    return { message: "Admin-only API" };
  }
}
```

guardHandler configuration:

```typescript theme={null}
export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
    },

    apiConfig: {
      guardHandler: async (guard, request) => {
        // Get user from Context
        const { user } = Sonamu.getContext();

        if (guard === "auth" && !user) {
          throw new UnauthorizedError("Login required");
        }

        if (guard === "admin") {
          if (!user) {
            throw new UnauthorizedError("Login required");
          }
          // Assuming User entity has a role field
          const fullUser = await UserModel.findById("A", user.id);
          if (fullUser.role !== "admin") {
            throw new UnauthorizedError("Admin access required");
          }
        }
      },
    },
  },
});
```

## Client-Side Integration

### Using with React

```typescript theme={null}
// lib/auth-client.ts
import { createAuthClient } from "better-auth/react";

export const authClient = createAuthClient({
  baseURL: "http://localhost:4000",
});
```

```tsx theme={null}
// components/LoginForm.tsx
import { authClient } from "../lib/auth-client";

export function LoginForm() {
  const handleLogin = async (email: string, password: string) => {
    const result = await authClient.signIn.email({
      email,
      password,
    });

    if (result.error) {
      alert(result.error.message);
      return;
    }

    // Login successful
    window.location.href = "/dashboard";
  };

  // ...
}
```

```tsx theme={null}
// components/GoogleLoginButton.tsx
import { authClient } from "../lib/auth-client";

export function GoogleLoginButton() {
  const handleGoogleLogin = () => {
    authClient.signIn.social({
      provider: "google",
      callbackURL: "/dashboard",
    });
  };

  return <button onClick={handleGoogleLogin}>Sign in with Google</button>;
}
```

## Plugin Configuration

To use better-auth plugins, add them to the `auth.plugins` array in `sonamu.config.ts`.

<Warning>
  Before configuring plugins, you must first generate the required entities and fields using `pnpm
      sonamu better-auth --plugins=...`.
</Warning>

### Two-Factor Authentication (2FA)

Enable TOTP-based two-factor authentication:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { twoFactor, TWO_FACTOR_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        twoFactor({
          issuer: "My App", // Name displayed in OTP apps
          schema: TWO_FACTOR_SCHEMA,
        }),
      ],
    },
  },
});
```

2FA-related APIs:

* `POST /api/auth/two-factor/enable` - Start 2FA setup
* `POST /api/auth/two-factor/verify` - Verify 2FA code
* `POST /api/auth/two-factor/disable` - Disable 2FA

### Admin Plugin

Supports user roles, banning, and impersonation:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { admin, ADMIN_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        admin({
          schema: ADMIN_SCHEMA,
        }),
      ],
    },
  },
});
```

Fields added to User table by Admin plugin:

* `role` - User role (default: "user")
* `banned` - Ban status
* `ban_reason` - Ban reason
* `ban_expires` - Ban expiration time (Unix timestamp)

### Username Plugin

Allows login with username instead of email:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { username, USERNAME_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        username({
          schema: USERNAME_SCHEMA,
        }),
      ],
    },
  },
});
```

Fields added to User table by Username plugin:

* `username` - Normalized username (lowercase, unique index)
* `display_username` - Display username (preserves original case)

### Phone Number Plugin

Supports phone number verification:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { phoneNumber, PHONE_NUMBER_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        phoneNumber({
          schema: PHONE_NUMBER_SCHEMA,
        }),
      ],
    },
  },
});
```

Fields added to User table by Phone Number plugin:

* `phone_number` - Phone number (unique index)
* `phone_number_verified` - Phone number verification status

### Passkey Plugin

Supports WebAuthn/FIDO2 based passkey authentication:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { passkey, PASSKEY_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        passkey({
          schema: PASSKEY_SCHEMA,
        }),
      ],
    },
  },
});
```

Tables created by Passkey plugin:

* `passkeys` - User passkey information (public key, credential ID, etc.)

Passkey-related APIs:

* `POST /api/auth/passkey/generate-register-options` - Generate passkey registration options
* `POST /api/auth/passkey/verify-registration` - Verify passkey registration
* `POST /api/auth/passkey/generate-authentication-options` - Generate passkey authentication options
* `POST /api/auth/passkey/verify-authentication` - Verify passkey authentication

<Note>The `@better-auth/passkey` package is required to use the Passkey plugin.</Note>

### SSO Plugin

Supports SSO login through external IdPs (OIDC, SAML):

```typescript theme={null}
import { defineConfig } from "sonamu";
import { sso, SSO_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        sso({
          ...SSO_SCHEMA,
        }),
      ],
    },
  },
});
```

Tables created by SSO plugin:

* `sso_providers` - SSO provider settings (including OIDC/SAML configuration)

<Note>The `@better-auth/sso` package is required to use the SSO plugin.</Note>

### API Key Plugin

Supports API key based authentication:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { apiKey, API_KEY_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        apiKey({
          schema: API_KEY_SCHEMA,
        }),
      ],
    },
  },
});
```

Tables created by API Key plugin:

* `api_keys` - API key information (hashed key, rate limit settings, etc.)

API Key-related APIs:

* `POST /api/auth/api-key/create` - Create API key
* `POST /api/auth/api-key/revoke` - Revoke API key
* `GET /api/auth/api-key/list` - List API keys

### JWT Plugin

Supports JWT token issuance and JWKS key management:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { jwt, JWT_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        jwt({
          schema: JWT_SCHEMA,
        }),
      ],
    },
  },
});
```

Tables created by JWT plugin:

* `jwks` - JSON Web Key Set information (public key, private key)

JWT-related APIs:

* `GET /api/auth/.well-known/jwks.json` - JWKS endpoint
* `POST /api/auth/jwt/generate` - Generate JWT token

### Organization Plugin

Supports organization, member, invitation, and team management:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { organization, ORGANIZATION_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        organization({
          schema: ORGANIZATION_SCHEMA,
        }),
      ],
    },
  },
});
```

Tables created by Organization plugin:

* `organizations` - Organization information
* `members` - Organization members
* `invitations` - Organization invitations
* `teams` - Teams
* `team_members` - Team members

Fields added to Session table by Organization plugin:

* `active_organization_id` - Current active organization ID
* `active_team_id` - Current active team ID

Organization-related APIs:

* `POST /api/auth/organization/create` - Create organization
* `POST /api/auth/organization/invite` - Invite member
* `POST /api/auth/organization/accept-invitation` - Accept invitation
* `POST /api/auth/organization/set-active` - Set active organization

### Anonymous Plugin

Supports anonymous user authentication. Allows creating temporary users without sign-up:

```typescript theme={null}
import { defineConfig } from "sonamu";
import { anonymous, ANONYMOUS_SCHEMA } from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      plugins: [
        anonymous({
          schema: ANONYMOUS_SCHEMA,
        }),
      ],
    },
  },
});
```

Fields added to User table by Anonymous plugin:

* `is_anonymous` - Whether the user is anonymous

Anonymous-related APIs:

* `POST /api/auth/sign-in/anonymous` - Anonymous login
* `POST /api/auth/anonymous/link` - Link anonymous account to a regular account

### Using Multiple Plugins Together

```typescript theme={null}
import { defineConfig } from "sonamu";
import {
  admin,
  ADMIN_SCHEMA,
  twoFactor,
  TWO_FACTOR_SCHEMA,
  username,
  USERNAME_SCHEMA,
  passkey,
  PASSKEY_SCHEMA,
  organization,
  ORGANIZATION_SCHEMA,
} from "sonamu/auth";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: { enabled: true },
      plugins: [
        admin({ schema: ADMIN_SCHEMA }),
        twoFactor({
          issuer: "My App",
          schema: TWO_FACTOR_SCHEMA,
        }),
        username({ schema: USERNAME_SCHEMA }),
        passkey({ schema: PASSKEY_SCHEMA }),
        organization({ schema: ORGANIZATION_SCHEMA }),
      ],
    },
  },
});
```

<Note>
  Each plugin's schema (`*_SCHEMA`) maps Sonamu's snake\_case column names to better-auth's camelCase
  field names. It must be passed along with the corresponding plugin.
</Note>

## Practical Examples

### Basic Configuration

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
      },
    },

    apiConfig: {
      guardHandler: async (guard) => {
        const { user } = Sonamu.getContext();

        if (guard === "auth" && !user) {
          throw new UnauthorizedError("Login required");
        }
      },
    },
  },
});
```

### Social Login + Email Verification

```typescript theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  server: {
    auth: {
      emailAndPassword: {
        enabled: true,
        requireEmailVerification: true,
      },
      socialProviders: {
        google: {
          clientId: process.env.GOOGLE_CLIENT_ID!,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
        },
        github: {
          clientId: process.env.GITHUB_CLIENT_ID!,
          clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        },
      },
    },
  },
});
```

## Important Notes

### 1. Entity Generation Required

```bash theme={null}
# Generate entities before auth configuration
pnpm sonamu better-auth

# Run migrations
pnpm sonamu migrate run
```

### 2. Environment Variables Setup

```bash theme={null}
# .env
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
```

### 3. CORS Configuration

When the client runs on a different domain:

```typescript theme={null}
export default defineConfig({
  server: {
    plugins: {
      cors: {
        origin: ["http://localhost:3000"],
        credentials: true,
      },
    },

    auth: {
      emailAndPassword: { enabled: true },
    },
  },
});
```

### 4. Compatibility with Existing User Entity

If a User entity already exists, running `pnpm sonamu better-auth` will only add missing fields. Existing data is preserved.

## Next Steps

After completing authentication setup:

* [Context](/en/api-development/context/sonamu-context) - Accessing user information from Context
* [Guards](/en/api-reference/decorators/api#guards) - API access control
