Skip to content

prod release #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,8 @@ coverage/
._*.tsx
._*.css
._*.cjs
._*.mjs
._*.js
._*.html
._*.scss
out/
1 change: 0 additions & 1 deletion app/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { getFinalPageTitle } from '@/lib/h1-extractor';
import { readFile } from 'fs/promises';
import { getMDXComponents } from '@/mdx-components';
import { homeOptions, docsOptions } from '../layout.config';
import { docs } from '@/.source/index';

export default async function Page({
params,
Expand Down
29 changes: 9 additions & 20 deletions app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,17 @@ import { source } from '@/lib/source';

export const dynamic = 'force-static';

function getPages(tree: any): any[] {
const pages: any[] = [];

function traverse(nodes: any[]) {
for (const node of nodes) {
if (node.type === 'page') {
pages.push(node);
} else if ('children' in node) {
traverse(node.children);
}
}
}

traverse(tree.children);
return pages;
}

export default function sitemap(): MetadataRoute.Sitemap {
const pages = getPages(source.pageTree).map((page) => ({
// Get all pages from source.getPages() which combines all sources
const allPages = source.getPages();

// Map pages to sitemap entries
const sitemapEntries = allPages.map((page) => ({
url: new URL(page.url, 'https://docs.deploystack.io').toString(),
lastModified: page.lastModified,
lastModified: page.data.lastModified ? new Date(page.data.lastModified) : undefined,
changeFrequency: 'weekly' as const,
priority: page.url === '/' ? 1.0 : page.url.startsWith('/development') || page.url.startsWith('/self-hosted') ? 0.7 : 0.8,
}));

return pages;
return sitemapEntries;
}
2 changes: 1 addition & 1 deletion docs/development/backend/api-security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ fastify.get('/dual-auth-endpoint', {
});
```

For detailed OAuth2 implementation, see the [Backend OAuth Implementation Guide](/development/backend/oauth) and [Backend Security Policy](/development/backend/security#oauth2-server-security).
For detailed OAuth2 implementation, see the [Backend OAuth Implementation Guide](/development/backend/oauth-providers) and [Backend Security Policy](/development/backend/security#oauth2-server-security).

### Team-Aware Permission System

Expand Down
274 changes: 271 additions & 3 deletions docs/development/backend/auth.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,274 @@
---
title: Authentication System
description: Complete guide to implementing user authentication in DeployStack Backend.
title: Backend Authentication System
description: Technical documentation for the DeployStack backend authentication implementation, including session management, password hashing, and authentication flows.
---

# DeployStack Authentication System
# Backend Authentication System

This document provides technical details about the DeployStack backend authentication system implementation. For user-facing authentication configuration, see [Authentication Methods](/auth). For OAuth provider implementation details, see [OAuth Provider Implementation](/development/backend/oauth-providers) and [OAuth2 Server Implementation](/development/backend/oauth2-server).

## Architecture Overview

The backend authentication system is built on several key components:

- **[Lucia v3](https://lucia-auth.com/)** - Core session management and authentication library
- **[Argon2](https://github.com/napi-rs/node-rs)** - Industry-standard password hashing
- **[Arctic](https://arctic.js.org/)** - OAuth 2.0 client library for provider integration
- **Database-backed sessions** - SQLite/Turso storage for session persistence
- **Dual authentication** - Support for both cookie sessions and OAuth2 Bearer tokens

## Authentication Flow Types

The backend supports multiple authentication flows to accommodate different client types and use cases:

### 1. Email/Password Authentication

Traditional username/password authentication with email verification:

- **Registration** (`/api/auth/email/register`) - Creates new user accounts
- **Login** (`/api/auth/email/login`) - Authenticates existing users
- **Email Verification** - Optional verification requirement based on global settings
- **Password Reset** - Secure token-based password recovery

### 2. OAuth Provider Authentication

Third-party authentication via OAuth providers (currently GitHub):

- **Provider Login** (`/api/auth/github/login`) - Initiates OAuth flow
- **OAuth Callback** (`/api/auth/github/callback`) - Handles provider response
- **Account Linking** - Automatically links OAuth accounts to existing users by email
- **User Provisioning** - Creates new users from OAuth profile data

### 3. OAuth2 Server Authentication

Bearer token authentication for programmatic API access:

- **Authorization** (`/api/oauth2/auth`) - OAuth2 authorization endpoint
- **Token Exchange** (`/api/oauth2/token`) - Exchanges codes for access tokens
- **Bearer Authentication** - API access using Authorization header
- **Scope-based Permissions** - Fine-grained access control

## Core Components

### Session Management

Sessions are managed through Lucia v3 with database persistence:

#### Session Storage

Sessions are stored in the `authSession` table with the following structure:
- **Session ID**: 40-character cryptographically random identifier
- **User ID**: Foreign key reference to the authenticated user
- **Expiration**: 30-day lifetime from creation
- **Cookie Attributes**: httpOnly, secure (production), sameSite (lax)

#### Session Lifecycle

1. **Creation**: Generated after successful authentication
2. **Validation**: Checked on each request via `authHook`
3. **Refresh**: Sessions are not automatically extended
4. **Expiration**: Expired sessions are deleted on validation attempt
5. **Logout**: Explicit session deletion

### Password Security

Passwords are secured using Argon2id with carefully chosen parameters:

#### Hashing Parameters
- **Algorithm**: Argon2id (resistant to side-channel and GPU attacks)
- **Memory Cost**: 19456 KB (19 MB)
- **Time Cost**: 2 iterations
- **Parallelism**: 1 thread
- **Output Length**: 32 bytes
- **Salt**: Unique per password, automatically generated

#### Password Verification

The verification process uses constant-time comparison to prevent timing attacks:
1. Extract stored hash and salt from database
2. Re-compute hash with provided password
3. Compare hashes using constant-time algorithm
4. Return authentication result

### Authentication Hooks

The backend uses Fastify hooks for request-level authentication:

#### authHook (Global)

Runs on every request to establish authentication context:
- Reads session cookie if present
- Validates session against database
- Populates `request.user` and `request.session`
- Handles expired session cleanup
- Skips authentication if database not ready

#### requireAuthHook (Route-specific)

Enforces authentication on protected routes:
- Checks for valid user and session
- Returns 401 Unauthorized if not authenticated
- Used as preValidation hook on protected endpoints

## User Registration Flow

### Email Registration Process

1. **Validation**: Input validation using Zod schemas
2. **Uniqueness Check**: Verify username and email availability
3. **Password Hashing**: Secure hash generation with Argon2
4. **User Creation**: Database insertion with role assignment
5. **First User Logic**: Automatic global_admin role for first user
6. **Email Verification**: Send verification email (if enabled)
7. **Team Creation**: Automatic default team creation
8. **Session Creation**: Immediate login after registration
9. **Response**: User data and success message

### Role Assignment

- **First User**: Automatically assigned `global_admin` role
- **Subsequent Users**: Assigned `global_user` role
- **OAuth Users**: Always assigned `global_user` role
- **Email Verification**: First user auto-verified, others depend on settings

## Login Authentication Flow

### Email Login Process

1. **Global Check**: Verify login is enabled in settings
2. **User Lookup**: Find user by email or username
3. **Password Verification**: Argon2 hash comparison
4. **Email Verification Check**: Ensure email is verified (if required)
5. **Session Creation**: Generate new 30-day session
6. **Cookie Setting**: Set httpOnly session cookie
7. **Response**: User data and session established

### Authentication State

After successful login, the following state is established:
- **Session Cookie**: Contains session ID
- **Database Session**: Active session record
- **User Context**: Available in `request.user`
- **Session Context**: Available in `request.session`

## Email Verification System

### Verification Requirements

- **Controlled by**: `global.send_mail` setting
- **First User**: Always auto-verified for system access
- **Email Users**: Must verify before login (when enabled)
- **OAuth Users**: Auto-verified (provider emails trusted)

### Verification Token Flow

1. **Token Generation**: 32-character random token
2. **Token Storage**: Hashed with Argon2 (same as passwords)
3. **Email Dispatch**: Verification link sent via SMTP
4. **Token Validation**: Constant-time comparison
5. **Account Activation**: Email marked as verified
6. **Token Cleanup**: Single-use, expires after 24 hours

## Password Reset Flow

### Reset Process

1. **Request Initiation**: User provides email address
2. **Token Generation**: Secure random reset token
3. **Token Storage**: Hashed and stored with expiration
4. **Email Notification**: Reset link sent to user
5. **Token Validation**: Verify token and expiration
6. **Password Update**: New password hashed and stored
7. **Token Invalidation**: Used tokens are deleted
8. **Session Creation**: Optional auto-login after reset

### Security Measures

- **Rate Limiting**: Prevent brute force attempts
- **Token Expiration**: 1-hour validity window
- **Single Use**: Tokens invalidated after use
- **User Notification**: Email sent on password change

## Dual Authentication Support

The backend supports both cookie-based and Bearer token authentication simultaneously:

### Cookie Authentication

- **Primary Use**: Web application sessions
- **Validation**: Via `authHook` on every request
- **Storage**: Server-side session with cookie identifier
- **Lifetime**: 30-day expiration

### Bearer Token Authentication

- **Primary Use**: CLI and API access
- **Validation**: Via `oauthMiddleware`
- **Format**: OAuth2 access tokens
- **Lifetime**: 1-hour access, 30-day refresh

### Middleware Integration

Endpoints can accept either authentication method using `requireAuthenticationAny()`:
- First checks for cookie session
- Falls back to Bearer token validation
- Populates same `request.user` interface
- Maintains authentication type in context

## Security Best Practices

### Implementation Security

1. **Password Storage**: Never store plaintext, always use Argon2
2. **Session Management**: Cryptographically secure IDs, proper expiration
3. **Token Security**: Constant-time comparisons, secure random generation
4. **HTTPS Enforcement**: Secure cookies in production
5. **CSRF Protection**: State parameters in OAuth, sameSite cookies

### Validation and Sanitization

- **Input Validation**: All inputs validated with Zod schemas
- **Email Normalization**: Lowercase conversion before storage
- **SQL Injection Prevention**: Parameterized queries via Drizzle ORM
- **XSS Prevention**: httpOnly cookies, no client-side session access

## Database Schema

The authentication system uses the following core tables:

### authUser Table

Stores user account information:
- User identification (id, username, email)
- Authentication data (hashed_password, auth_type)
- Profile information (first_name, last_name)
- OAuth provider IDs (github_id)
- Account status (email_verified, role_id)

### authSession Table

Manages active user sessions:
- Session identification (id)
- User association (user_id)
- Expiration tracking (expires_at)

### Additional Tables

Supporting tables for full functionality:
- **email_verification_tokens**: Email verification tokens
- **password_reset_tokens**: Password reset tokens
- **oauth_authorization_codes**: OAuth2 authorization codes
- **oauth_access_tokens**: OAuth2 access tokens
- **oauth_refresh_tokens**: OAuth2 refresh tokens

## API Reference

For a complete list of authentication API endpoints and their specifications, see the [Backend API Documentation](/development/backend/api). The API documentation includes OpenAPI specifications with detailed request/response schemas for all authentication endpoints.

## Related Documentation

- [Security Policy](/development/backend/security) - Security implementation details
- [Database Schema](/development/backend/database) - Complete database structure
- [API Documentation](/development/backend/api) - Full API reference
- [OAuth Provider Implementation](/development/backend/oauth-providers) - Third-party OAuth login setup
- [OAuth2 Server Implementation](/development/backend/oauth2-server) - OAuth2 server for API access
Loading