diff --git a/.env.example b/.env.example deleted file mode 100644 index 0f7525f..0000000 --- a/.env.example +++ /dev/null @@ -1,53 +0,0 @@ -# ATAuth Gateway Configuration -# Copy this file to .env and customize for your deployment - -# ============================================================================= -# SECURITY NOTICE -# ============================================================================= -# 1. NEVER commit your .env file to version control -# 2. Generate all tokens with: openssl rand -hex 32 -# 3. Use HTTPS in production (configure via reverse proxy) -# 4. Restrict CORS_ORIGINS to your actual domains -# ============================================================================= - -# ============================================================================= -# REQUIRED SETTINGS -# ============================================================================= - -# Admin API token for registering applications -# SECURITY: Generate a unique 64-character hex token: -# openssl rand -hex 32 -# DO NOT use the placeholder value in production! -ADMIN_TOKEN= - -# Your gateway's public URL (where users will be redirected) -# This must be accessible from the internet for OAuth to work -OAUTH_CLIENT_ID=https://auth.yourdomain.com/client-metadata.json -OAUTH_REDIRECT_URI=https://auth.yourdomain.com/auth/callback - -# ============================================================================= -# OPTIONAL SETTINGS -# ============================================================================= - -# Allowed CORS origins (comma-separated) -# SECURITY: List ONLY your actual application domains -# DO NOT use wildcards (*) in production -# Example: CORS_ORIGINS=https://myapp.com,https://admin.myapp.com -CORS_ORIGINS= - -# ============================================================================= -# SELF-HOSTED PDS CONFIGURATION -# ============================================================================= -# If you run your own PDS, your users authenticate with handles like: -# @alice.your-pds.com -# @bob.your-pds.com -# -# The gateway automatically discovers the user's PDS from their handle. -# No additional configuration needed - just ensure your PDS is accessible. -# -# Benefits of self-hosted PDS: -# - Full control over your identity infrastructure -# - No dependency on Bluesky's servers -# - Works on air-gapped networks (if PDS is local) -# - Your data stays on your infrastructure -# ============================================================================= diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e6c547..e924e30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -208,12 +208,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Input validation and sanitization - Rate limiting on all endpoints -[2.2.0]: https://github.com/Cache8063/atauth/compare/v2.1.0...v2.2.0 -[2.1.0]: https://github.com/Cache8063/atauth/compare/v2.0.3...v2.1.0 -[2.0.3]: https://github.com/Cache8063/atauth/compare/v2.0.2...v2.0.3 -[2.0.2]: https://github.com/Cache8063/atauth/compare/v2.0.1...v2.0.2 -[2.0.1]: https://github.com/Cache8063/atauth/compare/v2.0.0...v2.0.1 -[2.0.0]: https://github.com/Cache8063/atauth/compare/v1.3.0...v2.0.0 -[1.3.0]: https://github.com/Cache8063/atauth/compare/v1.2.0...v1.3.0 -[1.2.0]: https://github.com/Cache8063/atauth/compare/v1.0.0...v1.2.0 -[1.0.0]: https://github.com/Cache8063/atauth/releases/tag/v1.0.0 +[2.2.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v2.1.0...v2.2.0 +[2.1.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v2.0.3...v2.1.0 +[2.0.3]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v2.0.2...v2.0.3 +[2.0.2]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v2.0.1...v2.0.2 +[2.0.1]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v2.0.0...v2.0.1 +[2.0.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v1.3.0...v2.0.0 +[1.3.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v1.2.0...v1.3.0 +[1.2.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/compare/v1.0.0...v1.2.0 +[1.0.0]: https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e42e71..58309d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,96 +1,48 @@ # Contributing to ATAuth -Thank you for your interest in contributing to ATAuth! This document provides guidelines and information for contributors. - -## Getting Started - -1. Fork the repository -2. Clone your fork locally -3. Create a new branch for your changes -4. Make your changes -5. Submit a pull request - ## Development Setup -### Rust Library - ```bash -# Build the library -cargo build - -# Run tests -cargo test - -# Run examples -cargo run --example basic_verification -cargo run --example with_session_store --features session-sqlite +cd gateway +npm install +cp .env.example .env # Fill in required secrets +npm run dev # Hot reload with tsx watch ``` -### TypeScript Package +### Verify Your Changes ```bash -cd ts -npm install -npm run build -npm test +npm run typecheck # Type checking +npm run lint # ESLint +npm run test:run # 394 tests across 22 files ``` -### Gateway Server +### Client Libraries + +Rust (`src/`) and TypeScript (`ts/`) client libraries for token verification: ```bash -cd gateway -npm install -cp .env.example .env -# Edit .env with your configuration -npm run dev +cargo test # Rust library +cd ts && npm test # TypeScript library ``` ## Code Style -### Rust - -- Follow standard Rust formatting (`cargo fmt`) -- Run clippy for linting (`cargo clippy`) -- Write doc comments for public APIs -- Add tests for new functionality - -### TypeScript - -- Use TypeScript strict mode -- Follow ESLint rules (`npm run lint`) -- Write JSDoc comments for exported functions -- Add tests for new functionality - -## Pull Request Guidelines - -1. **Keep PRs focused**: One feature or fix per PR -2. **Write clear commit messages**: Describe what and why -3. **Add tests**: Cover new functionality with tests -4. **Update documentation**: Keep README and docs current -5. **Follow existing patterns**: Match the codebase style - -## Reporting Issues - -When reporting issues, please include: - -- A clear description of the problem -- Steps to reproduce -- Expected vs actual behavior -- Environment details (OS, Rust/Node version) -- Relevant logs or error messages - -## Feature Requests +- TypeScript strict mode, ESLint rules enforced +- Express 5 async error handling (`throw` pattern, no try/catch wrappers) +- Tests live alongside source: `foo.ts` -> `foo.test.ts` +- In-memory SQLite for test isolation -Feature requests are welcome! Please: +## Pull Requests -- Check existing issues first -- Clearly describe the use case -- Explain why this would benefit users +1. One feature or fix per PR +2. Add tests for new functionality +3. All CI checks must pass (typecheck, lint, vitest) ## Security -If you discover a security vulnerability, please report it privately rather than opening a public issue. See SECURITY.md for details. +Report vulnerabilities privately -- see [SECURITY.md](SECURITY.md). ## License -By contributing, you agree that your contributions will be licensed under the MIT License. +Contributions are licensed under MIT. diff --git a/README.md b/README.md index f98a3d2..c776d45 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Any app that supports OpenID Connect can use ATAuth. No custom integration neede ### 1. Clone and configure ```bash -git clone https://github.com/Cache8063/atauth.git +git clone https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth.git cd atauth cp gateway/.env.example .env ``` diff --git a/docs/HOMELAB.md b/docs/HOMELAB.md index ba0c76d..96d7444 100644 --- a/docs/HOMELAB.md +++ b/docs/HOMELAB.md @@ -12,7 +12,7 @@ ATAuth provides OIDC-based authentication for your homelab using AT Protocol ide ## Deploy ```bash -git clone https://github.com/Cache8063/atauth.git +git clone https://gitea.cloudforest-basilisk.ts.net/Arcnode.xyz/atauth.git cd atauth cp gateway/.env.example .env ``` diff --git a/gateway/DEPLOYMENT.md b/gateway/DEPLOYMENT.md deleted file mode 100644 index b9ee7f0..0000000 --- a/gateway/DEPLOYMENT.md +++ /dev/null @@ -1,276 +0,0 @@ -# ATAuth Gateway Deployment Guide - -This guide covers deploying ATAuth Gateway using Docker and Kubernetes. - -## Prerequisites - -- Docker 20.10+ (for Docker deployment) -- Kubernetes 1.25+ (for K8s deployment) -- kubectl configured for your cluster -- Helm 3.x (optional, for Helm deployment) - -## Quick Start with Docker - -### Build the Image - -```bash -cd gateway -docker build -t atauth-gateway:latest . -``` - -### Run with Docker - -```bash -# Generate secrets -export ADMIN_TOKEN=$(openssl rand -hex 32) -export OIDC_KEY_SECRET=$(openssl rand -hex 32) -export MFA_ENCRYPTION_KEY=$(openssl rand -hex 32) - -# Run container -docker run -d \ - --name atauth-gateway \ - -p 3100:3100 \ - -v atauth-data:/app/data \ - -e ADMIN_TOKEN=$ADMIN_TOKEN \ - -e OIDC_KEY_SECRET=$OIDC_KEY_SECRET \ - -e MFA_ENCRYPTION_KEY=$MFA_ENCRYPTION_KEY \ - -e OIDC_ENABLED=true \ - -e OIDC_ISSUER=https://auth.example.com \ - -e OAUTH_CLIENT_ID=https://auth.example.com/client-metadata.json \ - -e OAUTH_REDIRECT_URI=https://auth.example.com/auth/callback \ - -e WEBAUTHN_RP_ID=auth.example.com \ - -e WEBAUTHN_ORIGIN=https://auth.example.com \ - atauth-gateway:latest -``` - -### Run with Docker Compose - -```bash -# Create .env file with secrets -cat > .env << EOF -ADMIN_TOKEN=$(openssl rand -hex 32) -OIDC_KEY_SECRET=$(openssl rand -hex 32) -MFA_ENCRYPTION_KEY=$(openssl rand -hex 32) -EOF - -# Start services -docker-compose up -d - -# View logs -docker-compose logs -f -``` - -## Kubernetes Deployment - -### Option 1: Using kubectl (Kustomize) - -1. **Update configuration** - - Edit `k8s/configmap.yaml` with your domain settings: - ```yaml - OAUTH_CLIENT_ID: "https://your-domain.com/client-metadata.json" - OIDC_ISSUER: "https://your-domain.com" - WEBAUTHN_RP_ID: "your-domain.com" - ``` - -2. **Generate and update secrets** - - ```bash - # Generate secrets - ADMIN_TOKEN=$(openssl rand -hex 32) - OIDC_KEY_SECRET=$(openssl rand -hex 32) - MFA_ENCRYPTION_KEY=$(openssl rand -hex 32) - - # Update k8s/secret.yaml with generated values - ``` - -3. **Deploy** - - ```bash - # Apply all resources - kubectl apply -k k8s/ - - # Or apply individually - kubectl apply -f k8s/namespace.yaml - kubectl apply -f k8s/configmap.yaml - kubectl apply -f k8s/secret.yaml - kubectl apply -f k8s/pvc.yaml - kubectl apply -f k8s/deployment.yaml - kubectl apply -f k8s/service.yaml - kubectl apply -f k8s/ingress.yaml - ``` - -4. **Verify deployment** - - ```bash - kubectl -n atauth get pods - kubectl -n atauth get svc - kubectl -n atauth logs -f deployment/atauth-gateway - ``` - -### Option 2: Using Helm - -1. **Install the chart** - - ```bash - helm install atauth ./helm/atauth-gateway \ - --namespace atauth \ - --create-namespace \ - --set config.oidc.issuer=https://auth.example.com \ - --set config.oauthClientId=https://auth.example.com/client-metadata.json \ - --set config.oauthRedirectUri=https://auth.example.com/auth/callback \ - --set config.passkey.rpId=auth.example.com \ - --set config.passkey.origin=https://auth.example.com \ - --set secrets.adminToken=$(openssl rand -hex 32) \ - --set secrets.oidcKeySecret=$(openssl rand -hex 32) \ - --set secrets.mfaEncryptionKey=$(openssl rand -hex 32) \ - --set ingress.enabled=true \ - --set ingress.hosts[0].host=auth.example.com \ - --set ingress.hosts[0].paths[0].path=/ \ - --set ingress.hosts[0].paths[0].pathType=Prefix - ``` - -2. **Using a values file** - - Create `my-values.yaml`: - ```yaml - config: - oidc: - issuer: "https://auth.example.com" - oauthClientId: "https://auth.example.com/client-metadata.json" - oauthRedirectUri: "https://auth.example.com/auth/callback" - passkey: - rpId: "auth.example.com" - origin: "https://auth.example.com" - - secrets: - adminToken: "your-generated-token" - oidcKeySecret: "your-oidc-secret" - mfaEncryptionKey: "your-mfa-key" - - ingress: - enabled: true - className: traefik # or nginx - annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod - hosts: - - host: auth.example.com - paths: - - path: / - pathType: Prefix - tls: - - secretName: atauth-tls - hosts: - - auth.example.com - ``` - - Then install: - ```bash - helm install atauth ./helm/atauth-gateway \ - --namespace atauth \ - --create-namespace \ - -f my-values.yaml - ``` - -3. **Upgrade** - - ```bash - helm upgrade atauth ./helm/atauth-gateway \ - --namespace atauth \ - -f my-values.yaml - ``` - -## TLS Configuration - -### With cert-manager - -1. Install cert-manager if not already installed -2. Create a ClusterIssuer for Let's Encrypt -3. Add annotations to Ingress: - ```yaml - annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod - ``` - -### With Traefik (k3s default) - -```yaml -annotations: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - traefik.ingress.kubernetes.io/router.tls: "true" -``` - -## Environment Variables Reference - -| Variable | Description | Default | -|----------|-------------|---------| -| `PORT` | Server port | `3100` | -| `HOST` | Server host | `0.0.0.0` | -| `DB_PATH` | SQLite database path | `/app/data/gateway.db` | -| `ADMIN_TOKEN` | Admin API token | Required | -| `OAUTH_CLIENT_ID` | AT Protocol OAuth client ID | Required | -| `OAUTH_REDIRECT_URI` | OAuth callback URL | Required | -| `CORS_ORIGINS` | Allowed CORS origins (comma-separated) | `http://localhost:3000` | -| `OIDC_ENABLED` | Enable OIDC provider | `false` | -| `OIDC_ISSUER` | OIDC issuer URL | Required if OIDC enabled | -| `OIDC_KEY_SECRET` | Encryption key for OIDC keys | Required if OIDC enabled | -| `OIDC_KEY_ALGORITHM` | JWT signing algorithm | `ES256` | -| `PASSKEY_ENABLED` | Enable passkey auth | `true` | -| `WEBAUTHN_RP_NAME` | WebAuthn relying party name | `ATAuth` | -| `WEBAUTHN_RP_ID` | WebAuthn relying party ID | Required | -| `WEBAUTHN_ORIGIN` | WebAuthn expected origin | Required | -| `MFA_ENABLED` | Enable TOTP MFA | `true` | -| `MFA_TOTP_ISSUER` | TOTP issuer name | `ATAuth` | -| `MFA_ENCRYPTION_KEY` | Encryption key for TOTP secrets | Required if MFA enabled | -| `EMAIL_ENABLED` | Enable email verification | `false` | -| `EMAIL_PROVIDER` | Email provider (smtp/resend/sendgrid) | `smtp` | -| `EMAIL_FROM` | From address for emails | Required if email enabled | - -## Health Checks - -The gateway exposes a health endpoint at `/health`: - -```bash -curl http://localhost:3100/health -``` - -Response: -```json -{ - "status": "ok", - "service": "atauth-gateway", - "timestamp": "2024-01-15T10:30:00.000Z" -} -``` - -## Admin UI - -The Admin UI is available at `/admin` when the gateway is running. Use your `ADMIN_TOKEN` to authenticate. - -## Troubleshooting - -### Pod not starting - -Check logs: -```bash -kubectl -n atauth logs -f deployment/atauth-gateway -``` - -### Database permission issues - -Ensure the PVC is properly provisioned and the container has write access: -```bash -kubectl -n atauth describe pvc atauth-gateway-data -``` - -### Ingress not working - -Verify Ingress controller is running: -```bash -kubectl get pods -A | grep -E "(traefik|ingress-nginx)" -``` - -Check Ingress status: -```bash -kubectl -n atauth describe ingress atauth-gateway -``` diff --git a/gateway/README.md b/gateway/README.md deleted file mode 100644 index 2f44a77..0000000 --- a/gateway/README.md +++ /dev/null @@ -1,265 +0,0 @@ -# ATAuth Gateway - -AT Protocol OAuth gateway for application authentication. This gateway handles the OAuth flow with Bluesky/AT Protocol and issues HMAC-signed tokens that your backend servers can verify. - -## Quick Start - -### 1. Install Dependencies - -```bash -cd gateway -npm install -``` - -### 2. Configure Environment - -```bash -cp .env.example .env -``` - -Edit `.env`: -```bash -# Your gateway's public URL -OAUTH_CLIENT_ID=https://auth.yourdomain.com/client-metadata.json -OAUTH_REDIRECT_URI=https://auth.yourdomain.com/auth/callback - -# Admin API token (generate a secure random string) -ADMIN_TOKEN=your-secure-admin-token - -# Your application domains -CORS_ORIGINS=https://app.yourdomain.com,http://localhost:3000 -``` - -### 3. Start the Gateway - -```bash -npm run dev # Development -npm start # Production -``` - -### 4. Register Your Application - -```bash -curl -X POST http://localhost:3100/admin/apps \ - -H "Authorization: Bearer your-admin-token" \ - -H "Content-Type: application/json" \ - -d '{ - "id": "myapp", - "name": "My Application", - "callback_url": "https://myapp.com/auth/callback" - }' -``` - -Save the returned `hmac_secret` - you'll need it for your backend! - -## API Endpoints - -### Authentication Flow - -#### `POST /auth/init` -Start OAuth flow. - -```json -{ - "app_id": "myapp", - "handle": "user.bsky.social", - "redirect_uri": "https://myapp.com/auth/callback" -} -``` - -Returns: -```json -{ - "auth_url": "https://bsky.social/oauth/authorize?...", - "state": "...", - "app_id": "myapp" -} -``` - -#### `GET /auth/callback` -OAuth callback (redirects with token). - -Query params from AT Protocol OAuth flow are processed, then redirects to your `redirect_uri` with: -- `token` - HMAC-signed token for your backend -- `session_id` - Gateway session ID -- `needs_linking` - `true` if user not yet linked to app account - -#### `POST /auth/link` -Link AT Protocol identity to your app's user account. - -```json -{ - "session_id": "...", - "user_id": 123, - "app_id": "myapp" -} -``` - -#### `POST /auth/refresh` -Get a fresh token for an existing session. - -#### `POST /auth/logout` -Invalidate a session. - -### Token Verification - -#### `POST /token/verify` -Verify a token (for your backend server). - -```json -{ - "token": "...", - "app_id": "myapp" -} -``` - -Returns: -```json -{ - "valid": true, - "payload": { - "did": "did:plc:...", - "handle": "user.bsky.social", - "user_id": 123, - "app_id": "myapp", - "exp": 1699903600 - } -} -``` - -### Session Management - -#### `POST /session/check-conflict` -Check for existing sessions (multi-device support). - -#### `POST /session/resolve-conflict` -Resolve session conflict (`transfer`, `cancel`, `close_others`). - -#### `POST /session/update-state` -Update connection state (for WebSocket apps). - -### Admin - -#### `POST /admin/apps` -Register a new application (returns HMAC secret). - -#### `PUT /admin/apps/:id` -Update app config (can rotate secret). - -## Integration Examples - -### Frontend (React) - -```tsx -import { redirectToAuth, handleCallback } from 'atauth'; - -// Start login -const startLogin = async (handle: string) => { - const response = await fetch('https://auth.yourdomain.com/auth/init', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - app_id: 'myapp', - handle, - redirect_uri: window.location.origin + '/auth/callback' - }) - }); - const { auth_url } = await response.json(); - window.location.href = auth_url; -}; - -// Handle callback page -const CallbackPage = () => { - useEffect(() => { - const params = new URLSearchParams(window.location.search); - const token = params.get('token'); - if (token) { - // Store token and redirect to app - localStorage.setItem('auth_token', token); - window.location.href = '/dashboard'; - } - }, []); -}; -``` - -### Backend (Node.js) - -```typescript -import { TokenVerifier } from 'atauth'; - -const verifier = new TokenVerifier(process.env.HMAC_SECRET); - -app.use('/api', (req, res, next) => { - const token = req.headers.authorization?.replace('Bearer ', ''); - if (!token) { - return res.status(401).json({ error: 'No token' }); - } - - try { - const payload = verifier.verify(token); - req.user = payload; - next(); - } catch (e) { - res.status(401).json({ error: 'Invalid token' }); - } -}); -``` - -### Backend (Rust) - -```rust -use atauth::TokenVerifier; - -let verifier = TokenVerifier::new(hmac_secret.as_bytes()); - -match verifier.verify(&token) { - Ok(payload) => { - println!("User: {} ({})", payload.handle, payload.did); - // payload.user_id is your app's user ID if linked - } - Err(e) => { - eprintln!("Auth failed: {}", e); - } -} -``` - -## Deployment - -### Docker - -```dockerfile -FROM node:20-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm ci --only=production -COPY dist ./dist -COPY data ./data -EXPOSE 3100 -CMD ["node", "dist/index.js"] -``` - -### systemd - -```ini -[Unit] -Description=ATAuth Gateway -After=network.target - -[Service] -Type=simple -User=atauth -WorkingDirectory=/opt/atauth-gateway -ExecStart=/usr/bin/node dist/index.js -Restart=on-failure -Environment=NODE_ENV=production - -[Install] -WantedBy=multi-user.target -``` - -## Security Notes - -1. **HTTPS Required**: Always run behind HTTPS in production -2. **Secure Admin Token**: Use a long random string for `ADMIN_TOKEN` -3. **Store HMAC Secrets Safely**: Never commit secrets to git -4. **Restrict CORS**: Only allow your application domains diff --git a/gateway/docker-compose.yaml b/gateway/docker-compose.yaml deleted file mode 100644 index 5c42875..0000000 --- a/gateway/docker-compose.yaml +++ /dev/null @@ -1,68 +0,0 @@ -version: '3.8' - -services: - gateway: - build: - context: . - dockerfile: Dockerfile - image: atauth-gateway:latest - container_name: atauth-gateway - restart: unless-stopped - ports: - - "3100:3100" - volumes: - - atauth-data:/app/data - environment: - # Server - - PORT=3100 - - HOST=0.0.0.0 - - NODE_ENV=production - - # OAuth client configuration - - OAUTH_CLIENT_ID=https://auth.example.com/client-metadata.json - - OAUTH_REDIRECT_URI=https://auth.example.com/auth/callback - - # CORS - - CORS_ORIGINS=https://example.com,http://localhost:3000 - - # Admin token (REQUIRED - generate with: openssl rand -hex 32) - - ADMIN_TOKEN=${ADMIN_TOKEN:?Set ADMIN_TOKEN} - - # OIDC configuration - - OIDC_ENABLED=true - - OIDC_ISSUER=https://auth.example.com - # REQUIRED when OIDC_ENABLED=true (generate with: openssl rand -hex 32) - - OIDC_KEY_SECRET=${OIDC_KEY_SECRET:?Set OIDC_KEY_SECRET} - - OIDC_KEY_ALGORITHM=ES256 - - # Passkey/WebAuthn - - PASSKEY_ENABLED=true - - WEBAUTHN_RP_NAME=ATAuth - - WEBAUTHN_RP_ID=auth.example.com - - WEBAUTHN_ORIGIN=https://auth.example.com - - # MFA - - MFA_ENABLED=true - - MFA_TOTP_ISSUER=ATAuth - # REQUIRED when MFA is enabled (generate with: openssl rand -hex 64) - - MFA_ENCRYPTION_KEY=${MFA_ENCRYPTION_KEY:?Set MFA_ENCRYPTION_KEY} - - # Email (disabled by default) - - EMAIL_ENABLED=false - # - EMAIL_PROVIDER=smtp - # - EMAIL_FROM=ATAuth - # - SMTP_HOST=smtp.example.com - # - SMTP_PORT=587 - # - SMTP_USER=noreply@example.com - # - SMTP_PASS=your-password - - healthcheck: - test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3100/health"] - interval: 30s - timeout: 3s - retries: 3 - start_period: 5s - -volumes: - atauth-data: - driver: local