> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Auth system overview, user management, sessions, OAuth providers, and JWT tokens

# Authentication

TrailBase provides a complete authentication system with JWT tokens, OAuth integration, email verification, and flexible access control.

## Overview

The authentication system supports multiple flows:

* **Email/Password Authentication**: Traditional username and password login
* **OAuth Providers**: Google, GitHub, Discord, Microsoft, and more
* **JWT Tokens**: Stateless authentication with EdDSA signatures
* **Refresh Tokens**: Long-lived tokens stored in secure cookies
* **Email Verification**: Account verification workflow
* **Password Reset**: Secure password recovery

<Card title="Auth Module" icon="lock" href="/home/daytona/workspace/source/crates/core/src/auth/mod.rs">
  All authentication logic is implemented in `crates/core/src/auth/`.
</Card>

## User Model

### Database Schema

Users are stored in the `_user` table:

```rust theme={null}
// From auth/user.rs:14-42
pub struct DbUser {
  pub id: [u8; 16],                          // UUIDv7 as bytes
  pub email: String,
  pub password_hash: String,                 // Argon2 hash
  pub verified: bool,
  pub admin: bool,
  
  pub created: i64,
  pub updated: i64,
  
  // Email verification
  pub email_verification_code: Option<String>,
  pub email_verification_code_sent_at: Option<i64>,
  pub pending_email: Option<String>,
  
  // Password reset
  pub password_reset_code: Option<String>,
  pub password_reset_code_sent_at: Option<i64>,
  
  // OAuth authorization
  pub authorization_code: Option<String>,
  pub authorization_code_sent_at: Option<i64>,
  pub pkce_code_challenge: Option<String>,
  
  // OAuth provider data
  pub provider_id: i64,
  pub provider_user_id: Option<String>,
  pub provider_avatar_url: Option<String>,
}
```

### Authenticated User

Once authenticated, users are represented by the `User` struct:

```rust theme={null}
// From auth/user.rs:86-96
pub struct User {
  pub id: String,           // Base64-encoded UUID
  pub email: String,
  pub uuid: Uuid,           // Convenience UUID representation
  pub csrf_token: String,   // CSRF protection token
}
```

<Info>
  The `User` type can be extracted from Axum request handlers using `FromRequestParts`.
</Info>

## JWT Tokens

<Card title="JWT Helper" icon="key" href="/home/daytona/workspace/source/crates/core/src/auth/jwt.rs">
  JWT token generation and validation using EdDSA (Ed25519) signatures.
</Card>

### Token Structure

```rust theme={null}
// From auth/jwt.rs:29-49
pub struct TokenClaims {
  pub sub: String,         // Base64 encoded user ID
  pub iat: i64,           // Issued at (Unix timestamp)
  pub exp: i64,           // Expiration (Unix timestamp)
  pub email: String,       // User's email address
  pub csrf_token: String,  // Random CSRF token
  pub admin: bool,        // Is admin user
}
```

### Key Management

JWT keys are automatically generated on first startup:

* **Private Key**: `traildepot/secrets/key/private_key.pem` (EdDSA)
* **Public Key**: `traildepot/secrets/key/public_key.pem`

```rust theme={null}
// From auth/jwt.rs:89-98
pub struct JwtHelper {
  header: Header,
  validation: Validation,
  encoding_key: EncodingKey,  // For creating tokens
  decoding_key: DecodingKey,  // For validating tokens
  public_key: String,
}
```

<Warning>
  Never commit your private key to version control. It's automatically excluded via `.gitignore`.
</Warning>

### Token Lifetimes

From `constants.rs`:

```rust theme={null}
pub const DEFAULT_AUTH_TOKEN_TTL: Duration = Duration::minutes(60);
pub const DEFAULT_REFRESH_TOKEN_TTL: Duration = Duration::days(30);
```

* **Auth Token**: Short-lived (60 minutes), sent in headers
* **Refresh Token**: Long-lived (30 days), stored in secure HTTP-only cookie

## Authentication Flows

### Email/Password Registration

```
1. POST /api/auth/v1/register
   Body: { "email": "user@example.com", "password": "secure123" }
   
2. Server creates unverified user
3. Email verification code sent
4. Response: { auth_token, refresh_token, user_id }
```

<Info>
  Users can log in even without email verification, but some features may require `verified=true`.
</Info>

### Email/Password Login

```
1. POST /api/auth/v1/login
   Body: { "email": "user@example.com", "password": "secure123" }
   
2. Server validates credentials with Argon2
3. Lookup user sessions
4. Generate JWT tokens
5. Response: { auth_token, refresh_token, user_id }
```

Implemented in `auth/api/login.rs` using `login_with_password()` from `auth/util.rs`.

### OAuth Flow

<Card title="OAuth Integration" icon="users" href="/home/daytona/workspace/source/crates/core/src/auth/oauth/">
  Supports multiple OAuth providers with PKCE for security.
</Card>

Supported providers:

* Google
* GitHub
* GitLab
* Discord
* Microsoft
* Facebook
* Twitch
* Yandex
* Generic OIDC (OpenID Connect)

**OAuth Flow**:

```
1. GET /api/auth/v1/oauth/login/{provider}
   Query: ?redirect_uri=<app_callback>&code_challenge=<pkce>
   
2. Server redirects to provider's authorization page

3. User authorizes on provider site

4. Provider redirects to: /api/auth/v1/oauth/callback/{provider}
   Query: ?code=<auth_code>&state=<state>
   
5. Server exchanges code for tokens
6. Fetch user profile from provider
7. Create or update user in _user table
8. Redirect to app with authorization code

9. Client: POST /api/auth/v1/token
   Body: { "code": "<auth_code>", "code_verifier": "<pkce_verifier>" }
   
10. Response: { auth_token, refresh_token, user_id }
```

### Token Refresh

```
1. POST /api/auth/v1/refresh
   Cookie: refresh_token=<token>
   
2. Server validates refresh token
3. Check session validity
4. Generate new auth token
5. Response: { auth_token, refresh_token }
```

Implemented in `auth/api/refresh.rs`.

## Password Security

<Card title="Password Hashing" icon="shield-halved" href="/home/daytona/workspace/source/crates/core/src/auth/password.rs">
  Passwords are hashed using Argon2id with secure parameters.
</Card>

```rust theme={null}
// Password hashing uses Argon2id
pub fn hash_password(password: &str) -> Result<String, AuthError> {
  let salt = SaltString::generate(&mut OsRng);
  let argon2 = Argon2::default();
  let hash = argon2
    .hash_password(password.as_bytes(), &salt)
    .map_err(|_| AuthError::Internal("password hashing"))?;
  Ok(hash.to_string())
}
```

**Password Requirements** (configurable):

* Minimum length
* Complexity rules
* Common password prevention

## Email Verification

### Verification Flow

```
1. User registers account
2. Server generates random verification code
3. Email sent with verification link
4. GET /api/auth/v1/verify_email/confirm/{code}
5. Server marks user as verified
6. Redirect to application
```

### Re-send Verification

```
GET /api/auth/v1/verify_email/trigger
Authorization: Bearer <auth_token>
```

## Password Reset

### Reset Flow

```
1. POST /api/auth/v1/reset_password/request
   Body: { "email": "user@example.com" }
   
2. Server generates reset code
3. Email sent with reset link

4. POST /api/auth/v1/reset_password/update
   Body: { 
     "email": "user@example.com",
     "code": "<reset_code>",
     "new_password": "new_secure123"
   }
   
5. Password updated, user can log in
```

<Warning>
  Reset codes expire after a configured time period (default: 1 hour).
</Warning>

## Sessions

Sessions are stored in the `_session` table:

```sql theme={null}
CREATE TABLE _session (
  id              BLOB PRIMARY KEY NOT NULL,
  user_id         BLOB NOT NULL REFERENCES _user(id),
  refresh_token   TEXT NOT NULL,
  csrf_token      TEXT NOT NULL,
  created         INTEGER NOT NULL,
  expires         INTEGER NOT NULL
) STRICT;
```

### Session Management

* **Creation**: New session created on login
* **Validation**: Checked on token refresh
* **Logout**: Deletes session and invalidates refresh token
* **Expiration**: Sessions auto-expire based on `expires` timestamp

## Access Control

### Table-Level ACLs

<Info>
  Access Control Lists define base permissions for unauthenticated and authenticated users.
</Info>

```protobuf theme={null}
record_apis {
  name: "posts"
  table_name: "post"
  
  # Unauthenticated users can READ
  acl_world: [READ]
  
  # Authenticated users can do everything
  acl_authenticated: [CREATE, READ, UPDATE, DELETE, SCHEMA]
}
```

Permissions from `records/mod.rs:103-110`:

```rust theme={null}
pub enum Permission {
  Create = 1,   // Create new records
  Read = 2,     // Read records
  Update = 4,   // Update records
  Delete = 8,   // Delete records
  Schema = 16,  // View JSON schema
}
```

### Row-Level Access Rules

<Card title="Access Rules" icon="gavel" href="/home/daytona/workspace/source/crates/core/src/records/record_api.rs:741-833">
  SQL expressions that control access to individual records.
</Card>

Access rules are SQL expressions with special variables:

* `_USER_`: Current authenticated user (or NULL)
* `_ROW_`: The record being accessed
* `_REQ_`: The request payload (for CREATE/UPDATE)

**Example Rules**:

```protobuf theme={null}
# Anyone can read all posts
read_access_rule: "TRUE"

# Only authenticated users can create
create_access_rule: "_USER_.id IS NOT NULL"

# Only post owner can update
update_access_rule: "_ROW_.user_id = _USER_.id"

# Only owner or admin can delete
delete_access_rule: "_ROW_.user_id = _USER_.id OR _USER_.admin = 1"
```

**Complex Example**:

```protobuf theme={null}
# Can only publish own posts, admins can publish any
update_access_rule: |
  _ROW_.user_id = _USER_.id AND (
    _REQ_.published IS NULL OR 
    _REQ_.published = _ROW_.published OR
    _USER_.admin = 1
  )
```

## CSRF Protection

<Warning>
  Admin API endpoints require CSRF token validation to prevent cross-site request forgery.
</Warning>

CSRF protection flow:

1. Auth token includes `csrf_token` in claims
2. Client extracts token from auth token
3. Client sends token in `x-csrf-token` header
4. Server validates token matches user's token

```rust theme={null}
// From server/mod.rs:572-597
async fn assert_admin_api_access(
  State(state): State<AppState>,
  mut req: Request,
  next: Next,
) -> Result<Response, AuthError> {
  let user = req.extract_parts_with_state::<User, _>(&state).await?;
  
  if !is_admin(&state, &user.uuid).await {
    return Err(AuthError::Forbidden);
  }
  
  // Validate CSRF token
  let Some(received_csrf_token) = req
    .headers()
    .get(HEADER_CSRF_TOKEN)
    .and_then(|header| header.to_str().ok())
  else {
    return Err(AuthError::BadRequest("admin APIs require csrf header"));
  };
  
  if user.csrf_token != received_csrf_token {
    return Err(AuthError::BadRequest("invalid CSRF token"));
  }
  
  Ok(next.run(req).await)
}
```

## Rate Limiting

<Card title="Rate Limiter" icon="gauge" href="/home/daytona/workspace/source/crates/core/src/server/mod.rs:176">
  IP-based rate limiting prevents authentication abuse.
</Card>

Auth endpoints have rate limiting:

* **Burst**: 5 requests (50 in debug mode)
* **Replenish**: 1 request every 2 seconds
* **Scope**: POST methods only (login, register, etc.)
* **Key**: Client IP address from headers or connection

```rust theme={null}
// Rate limiter configuration
let governor_conf = GovernorConfigBuilder::default()
  .burst_size(5)
  .per_second(2)
  .key_extractor(RealIpKeyExtractor)
  .use_headers()
  .methods(vec![Method::POST])
  .finish()?;
```

## Configuration

### Auth Options

```protobuf theme={null}
auth {
  # Disable password authentication (OAuth only)
  disable_password_auth: false
  
  # Custom URI schemes for mobile deep links
  custom_uri_schemes: ["myapp"]
  
  # OAuth providers
  oauth_providers {
    key: "google"
    value {
      provider_id: GOOGLE
      client_id: "<google_client_id>"
      client_secret: "<google_client_secret>"
    }
  }
}
```

### OAuth Provider Setup

<CardGroup cols={2}>
  <Card title="Google OAuth" icon="google">
    1. Create project in Google Cloud Console
    2. Enable Google+ API
    3. Create OAuth 2.0 credentials
    4. Set redirect URI: `<site_url>/api/auth/v1/oauth/callback/google`
  </Card>

  <Card title="GitHub OAuth" icon="github">
    1. Go to Settings > Developer settings > OAuth Apps
    2. Register new application
    3. Set callback URL: `<site_url>/api/auth/v1/oauth/callback/github`
    4. Copy client ID and secret
  </Card>
</CardGroup>

## Best Practices

<CardGroup cols={2}>
  <Card title="Use HTTPS" icon="lock">
    Always use TLS in production to protect tokens in transit
  </Card>

  <Card title="Short Auth Tokens" icon="clock">
    Keep auth token TTL short (minutes), rely on refresh tokens
  </Card>

  <Card title="Validate Email" icon="envelope">
    Require email verification for sensitive operations
  </Card>

  <Card title="Implement Logout" icon="right-from-bracket">
    Always provide logout to invalidate sessions
  </Card>
</CardGroup>

## Security Considerations

* **Token Storage**: Store refresh tokens in HTTP-only, secure cookies
* **Token Rotation**: Refresh tokens are rotated on each use
* **Session Tracking**: All sessions are tracked in database
* **IP Validation**: Consider validating IP for sensitive operations
* **Admin Separation**: Admin endpoints require both auth + CSRF

## Next Steps

<CardGroup cols={2}>
  <Card title="APIs" icon="code" href="./apis">
    Learn how to make authenticated API requests
  </Card>

  <Card title="Data Model" icon="database" href="./data-model">
    Understand how access control integrates with data
  </Card>
</CardGroup>
