Add comprehensive Docker setup and documentation
Some checks failed
tests / ci (push) Waiting to run
linter / quality (push) Has been cancelled

- Update Makefile with complete Docker workflow targets
  - Build, run, test, and debug containers
  - Separate production and test environments
  - Volume management and cleanup commands
  - Interactive debugging and health checks
  - Comprehensive help system with usage examples

- Create detailed README with setup instructions
  - Complete Docker and manual installation guides
  - Automatic setup explanation (hook.sh behavior)
  - OIDC endpoint documentation and integration examples
  - Troubleshooting guide and security considerations
  - Development workflow and testing information

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Javier Feliz 2025-08-02 19:54:27 -04:00
parent 6a3971257a
commit e78eaf7eff
2 changed files with 542 additions and 24 deletions

216
Makefile
View File

@ -1,33 +1,207 @@
IMAGE_NAME = laravel-app
CONTAINER_NAME = laravel-app
IMAGE_NAME = authentikate
CONTAINER_NAME = authentikate
PORT = 8889
VERSION = "latest"
VERSION = latest
TEST_PORT = 8890
TEST_CONTAINER_NAME = authentikate-test
.PHONY: build run rebuild
# Default target
.DEFAULT_GOAL := help
# Make sure to get set up with octane and frankenPHP
# since that's what the base docker image expects
setup:
setup: ## Install Laravel Octane with FrankenPHP
composer require laravel/octane
php artisan octane:install --server=frankenphp
# Build the Docker image
build:
npm run build
docker build -t $(IMAGE_NAME) .
# Docker build targets
build: ## Build the Docker image
@echo "Building Docker image: $(IMAGE_NAME):$(VERSION)"
docker build -t $(IMAGE_NAME):$(VERSION) .
@echo "✅ Image built successfully"
# Run the container
run:
docker run -it --rm -p $(PORT):8000 -e APP_DEBUG=true --name $(CONTAINER_NAME) $(IMAGE_NAME)
build-no-cache: ## Build the Docker image without cache
@echo "Building Docker image without cache: $(IMAGE_NAME):$(VERSION)"
docker build --no-cache -t $(IMAGE_NAME):$(VERSION) .
@echo "✅ Image built successfully"
# Rebuild (force rebuild without cache)
rebuild:
docker build --no-cache -t $(IMAGE_NAME) .
# Run targets
run: ## Run the container (production-like)
@echo "Starting container: $(CONTAINER_NAME)"
@docker stop $(CONTAINER_NAME) 2>/dev/null || true
@docker rm $(CONTAINER_NAME) 2>/dev/null || true
docker run -d \
--name $(CONTAINER_NAME) \
-p $(PORT):8000 \
-e APP_NAME="AuthentiKate" \
-e APP_ENV=production \
-e APP_DEBUG=false \
-e APP_KEY=base64:$$(openssl rand -base64 32) \
-e APP_URL=http://localhost:$(PORT) \
-e DB_CONNECTION=sqlite \
-e DB_DATABASE=/app/database/database.sqlite \
-v authentikate_keys:/app/storage/oauth \
-v authentikate_db:/app/database \
-v authentikate_storage:/app/storage \
$(IMAGE_NAME):$(VERSION)
@echo "✅ Container started on http://localhost:$(PORT)"
@echo "📋 Check logs with: make logs"
@echo "🔑 Admin credentials will be shown in the logs"
docker-publish:
docker image tag flowtodo:latest gitgud.foo/thegrind/flowtodo:$(VERSION)
docker push gitgud.foo/thegrind/flowtodo:$(VERSION)
run-test: ## Run a test container with different settings
@echo "Starting test container: $(TEST_CONTAINER_NAME)"
@docker stop $(TEST_CONTAINER_NAME) 2>/dev/null || true
@docker rm $(TEST_CONTAINER_NAME) 2>/dev/null || true
docker run -d \
--name $(TEST_CONTAINER_NAME) \
-p $(TEST_PORT):8000 \
-e APP_NAME="AuthentiKate Test" \
-e APP_ENV=testing \
-e APP_DEBUG=true \
-e APP_KEY=base64:$$(openssl rand -base64 32) \
-e APP_URL=http://localhost:$(TEST_PORT) \
-e DB_CONNECTION=sqlite \
-e DB_DATABASE=/app/database/test.sqlite \
-v authentikate_test_keys:/app/storage/oauth \
-v authentikate_test_db:/app/database \
-v authentikate_test_storage:/app/storage \
$(IMAGE_NAME):$(VERSION)
@echo "✅ Test container started on http://localhost:$(TEST_PORT)"
@echo "📋 Check logs with: make logs-test"
test-remote-image:
docker pull gitgud.foo/thegrind/flowtodo:latest
docker run --rm -p 8889:8000 gitgud.foo/thegrind/flowtodo:latest
run-interactive: ## Run container interactively for debugging
@echo "Starting interactive container"
@docker stop $(CONTAINER_NAME)-debug 2>/dev/null || true
@docker rm $(CONTAINER_NAME)-debug 2>/dev/null || true
docker run -it --rm \
--name $(CONTAINER_NAME)-debug \
-p $(PORT):8000 \
-e APP_ENV=local \
-e APP_DEBUG=true \
-e APP_KEY=base64:$$(openssl rand -base64 32) \
-e APP_URL=http://localhost:$(PORT) \
-e DB_CONNECTION=sqlite \
-e DB_DATABASE=/app/database/debug.sqlite \
$(IMAGE_NAME):$(VERSION) \
/bin/bash
# Container management
stop: ## Stop the running container
@echo "Stopping container: $(CONTAINER_NAME)"
@docker stop $(CONTAINER_NAME) 2>/dev/null || echo "Container not running"
@docker rm $(CONTAINER_NAME) 2>/dev/null || echo "Container already removed"
stop-test: ## Stop the test container
@echo "Stopping test container: $(TEST_CONTAINER_NAME)"
@docker stop $(TEST_CONTAINER_NAME) 2>/dev/null || echo "Container not running"
@docker rm $(TEST_CONTAINER_NAME) 2>/dev/null || echo "Container already removed"
stop-all: ## Stop all containers
@echo "Stopping all containers"
@docker stop $(CONTAINER_NAME) $(TEST_CONTAINER_NAME) 2>/dev/null || true
@docker rm $(CONTAINER_NAME) $(TEST_CONTAINER_NAME) 2>/dev/null || true
restart: stop run ## Restart the container
restart-test: stop-test run-test ## Restart the test container
# Logs and debugging
logs: ## Show container logs
docker logs -f $(CONTAINER_NAME)
logs-test: ## Show test container logs
docker logs -f $(TEST_CONTAINER_NAME)
logs-tail: ## Show last 50 lines of container logs
docker logs --tail 50 $(CONTAINER_NAME)
shell: ## Open a shell in the running container
docker exec -it $(CONTAINER_NAME) /bin/bash
shell-test: ## Open a shell in the test container
docker exec -it $(TEST_CONTAINER_NAME) /bin/bash
# Full workflow targets
rebuild: build stop run ## Rebuild image and restart container
rebuild-test: build stop-test run-test ## Rebuild image and restart test container
rebuild-no-cache: build-no-cache stop run ## Rebuild without cache and restart
test-full: build run-test ## Build and run test container
@echo "⏳ Waiting for container to start..."
@sleep 10
@echo "🧪 Running health check..."
@curl -f http://localhost:$(TEST_PORT) >/dev/null 2>&1 && echo "✅ Health check passed" || echo "❌ Health check failed"
@echo "📋 Test container logs:"
@docker logs $(TEST_CONTAINER_NAME) | tail -20
# Cleanup targets
clean: ## Remove containers and images
@echo "Cleaning up containers and images"
@docker stop $(CONTAINER_NAME) $(TEST_CONTAINER_NAME) 2>/dev/null || true
@docker rm $(CONTAINER_NAME) $(TEST_CONTAINER_NAME) 2>/dev/null || true
@docker rmi $(IMAGE_NAME):$(VERSION) 2>/dev/null || echo "Image not found"
clean-volumes: ## Remove all volumes (WARNING: destroys data)
@echo "⚠️ WARNING: This will destroy all data in volumes!"
@read -p "Are you sure? (y/N) " -n 1 -r; \
if [[ $$REPLY =~ ^[Yy]$$ ]]; then \
echo; \
docker volume rm authentikate_keys authentikate_db authentikate_storage 2>/dev/null || true; \
docker volume rm authentikate_test_keys authentikate_test_db authentikate_test_storage 2>/dev/null || true; \
echo "✅ Volumes removed"; \
else \
echo; \
echo "❌ Cancelled"; \
fi
clean-all: clean clean-volumes ## Remove everything (containers, images, volumes)
# Development targets
dev-build-test: ## Quick development build and test cycle
@echo "🔄 Development build and test cycle"
@make build
@make run-test
@echo "⏳ Waiting for container to start..."
@sleep 5
@make logs-tail
@echo "🌐 Test instance available at: http://localhost:$(TEST_PORT)"
# Status and info
status: ## Show container status
@echo "=== Container Status ==="
@docker ps -a --filter name=$(CONTAINER_NAME) --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
@docker ps -a --filter name=$(TEST_CONTAINER_NAME) --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
@echo
@echo "=== Volume Status ==="
@docker volume ls --filter name=authentikate
info: ## Show image and container information
@echo "=== Image Information ==="
@docker images $(IMAGE_NAME):$(VERSION) --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}\t{{.CreatedAt}}"
@echo
@echo "=== Container Information ==="
@docker inspect $(CONTAINER_NAME) 2>/dev/null | grep -E '"IPAddress"|"Ports"' || echo "Container not running"
urls: ## Show accessible URLs
@echo "=== Available URLs ==="
@echo "Production: http://localhost:$(PORT)"
@echo "Test: http://localhost:$(TEST_PORT)"
@echo "Health: http://localhost:$(PORT)/up"
# Help target
help: ## Show this help message
@echo "AuthentiKate Docker Makefile"
@echo "============================="
@echo
@echo "Usage: make [target]"
@echo
@echo "Targets:"
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
@echo
@echo "Quick Start:"
@echo " make build run # Build and run production container"
@echo " make dev-build-test # Quick dev cycle"
@echo " make logs # View logs"
@echo " make clean # Clean up"

350
README.md
View File

@ -2,6 +2,350 @@
![banner](banner.png)
The SSO/OIDC solution for homelabbers. No more Authentik overcomplicating your life
(and taking 2G of ram) just so you can give your friends convenient access to your
services.
The SSO/OIDC solution for homelabbers. No more Authentik overcomplicating your life (and taking 2G of ram) just so you can give your friends convenient access to your services.
## What is AuthentiKate?
AuthentiKate is a lightweight, self-hosted Single Sign-On (SSO) and OpenID Connect (OIDC) provider designed specifically for homelab enthusiasts. Built with Laravel and Livewire, it provides enterprise-grade authentication features without the complexity and resource overhead of larger solutions.
### Key Features
- **OpenID Connect Provider**: Full OIDC implementation with JWT tokens signed with RSA256
- **PKCE Support**: Secure authorization code flow with Proof Key for Code Exchange
- **User Management**: Admin interface for managing users and invitations
- **Application Management**: Easy registration and management of OAuth applications
- **Auto-Approval**: Skip consent screens for previously authorized applications
- **Avatar Support**: User profile pictures with file upload
- **Responsive Design**: Modern UI built with Flux components and Tailwind CSS
- **Lightweight**: Minimal resource usage compared to alternatives
- **Docker Ready**: Containerized deployment with automatic setup
## Quick Start with Docker
The easiest way to run AuthentiKate is using Docker:
```bash
# Build the container
docker build -t authentikate .
# Run with basic configuration
docker run -p 8000:8000 \
-e APP_NAME="AuthentiKate" \
-e APP_ENV=production \
-e APP_KEY=base64:$(openssl rand -base64 32) \
-e APP_URL=https://auth.yourdomain.com \
-e DB_CONNECTION=sqlite \
-e DB_DATABASE=/app/database/database.sqlite \
-v authentikate_keys:/app/storage/oauth \ # Important to keys persist between restarts
-v authentikate_db:/app/database \
authentikate
```
### What Happens on First Run
When the container starts for the first time, the following setup commands run automatically via `hook.sh`:
1. **RSA Key Generation** (`php artisan app:generate-keys`)
- Generates 2048-bit RSA private/public key pair
- Keys stored in `/app/storage/oauth/private.pem` and `/app/storage/oauth/public.pem`
- Used for signing JWT tokens with RS256 algorithm
2. **Initial Admin Creation** (`php artisan authentikate:create-admin`)
- Creates the first admin user account
- Prompts for email and name (or uses defaults)
- Generates a secure random password
- **Important**: Check the container logs for login credentials!
### After First Run
1. **Check the logs** for your admin credentials:
```bash
docker logs <container_name>
```
Look for output like:
```
✅ Initial admin user created successfully!
🔐 Login Credentials:
📧 Email: admin@authentikate.local
🔑 Password: Xy9$kL2m@Qp3nR8t
⚠️ Please log in and change your password immediately!
```
2. **Log in and change your password**:
- Navigate to your AuthentiKate URL
- Log in with the generated credentials
- Go to Settings → Profile to change your password
3. **Configure your first application**:
- Go to Applications in the admin panel
- Create a new OAuth application
- Note the Client ID and Client Secret for your services
## Manual Installation
### Requirements
- PHP 8.2 or higher
- Composer
- Node.js 18+ and npm
- Database (SQLite, MySQL, or PostgreSQL)
- Web server (Apache, Nginx, etc.)
### Installation Steps
1. **Clone the repository**:
```bash
git clone <repository-url> authentikate
cd authentikate
```
2. **Install dependencies**:
```bash
composer install --no-dev --optimize-autoloader
npm install && npm run build
```
3. **Environment configuration**:
```bash
cp .env.example .env
php artisan key:generate
```
4. **Configure your `.env` file**:
```env
APP_NAME=AuthentiKate
APP_ENV=production
APP_DEBUG=false
APP_URL=https://auth.yourdomain.com
# Database (SQLite example)
DB_CONNECTION=sqlite
DB_DATABASE=/path/to/database.sqlite
# Mail configuration (for invitations)
MAIL_MAILER=smtp
MAIL_HOST=your-smtp-server
MAIL_PORT=587
MAIL_USERNAME=your-email
MAIL_PASSWORD=your-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@yourdomain.com
MAIL_FROM_NAME="AuthentiKate"
```
5. **Set up the database**:
```bash
php artisan migrate
```
6. **Generate OIDC keys**:
```bash
php artisan app:generate-keys
```
7. **Create initial admin user**:
```bash
php artisan authentikate:create-admin
```
8. **Set up the web server**:
Configure your web server to serve the `public` directory. For Nginx:
```nginx
server {
listen 80;
server_name auth.yourdomain.com;
root /path/to/authentikate/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
}
```
9. **Set up queue worker** (optional but recommended):
```bash
php artisan queue:work --tries=3 --daemon
```
## Configuration
### Environment Variables
Key environment variables for AuthentiKate:
- `APP_URL`: Your AuthentiKate instance URL (used in OIDC issuer)
- `DB_*`: Database connection settings
- `MAIL_*`: Email configuration for sending invitations
- `SESSION_DRIVER`: Session storage (database recommended for production)
- `CACHE_DRIVER`: Cache storage (redis recommended for production)
### File Permissions
Ensure proper permissions for:
- `storage/` directory: writable by web server
- `storage/oauth/`: contains RSA keys (600 for private key)
- `bootstrap/cache/`: writable by web server
### SSL/TLS
**Important**: AuthentiKate should always run behind HTTPS in production. OIDC requires secure connections for security. Configure your reverse proxy or web server to handle SSL termination.
## Usage
### Managing Applications
1. Log in as an admin
2. Navigate to "Applications" in the sidebar
3. Click "New Application"
4. Fill in:
- **Name**: Display name for your application
- **Redirect URI**: The callback URL for your application
5. Save and note the generated Client ID and Client Secret
### Managing Users
1. Navigate to "User Management"
2. Create invitations for new users by email
3. Optionally send invitation emails automatically
4. Users can register using the invitation code
5. Promote users to admin status as needed
### User Features
- **Profile Management**: Users can update their name, email, and avatar
- **Auto-Approval**: Users can enable auto-approval for previously authorized apps
- **Token Management**: View and revoke active authentication tokens
## Integration Examples
### Grafana
```yaml
# grafana.yml
auth:
generic_oauth:
enabled: true
name: AuthentiKate
client_id: your-client-id
client_secret: your-client-secret
scopes: openid profile email
auth_url: https://auth.yourdomain.com/auth/authorize
token_url: https://auth.yourdomain.com/auth/token
api_url: https://auth.yourdomain.com/auth/userinfo
use_pkce: true
```
### Traefik ForwardAuth
```yaml
# docker-compose.yml
version: '3.7'
services:
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2
environment:
- DEFAULT_PROVIDER=oidc
- PROVIDERS_OIDC_ISSUER_URL=https://auth.yourdomain.com
- PROVIDERS_OIDC_CLIENT_ID=your-client-id
- PROVIDERS_OIDC_CLIENT_SECRET=your-client-secret
- SECRET=your-secret-key
- AUTH_HOST=auth.yourdomain.com
- COOKIE_DOMAIN=yourdomain.com
```
## Troubleshooting
### Common Issues
1. **"Invalid client" errors**:
- Verify Client ID and Client Secret
- Check that redirect URI matches exactly
2. **JWT verification failures**:
- Ensure RSA keys are properly generated
- Check that `/auth/keys` endpoint is accessible
3. **Email not working**:
- Verify MAIL_* environment variables
- Check Laravel logs in `storage/logs/laravel.log`
4. **Permission denied errors**:
- Check file permissions on storage directories
- Ensure web server can write to storage/cache
### Logs
Check the following logs for debugging:
- Application logs: `storage/logs/laravel.log`
- Web server logs: Check your web server's error logs
- Docker logs: `docker logs <container_name>`
## Development
### Running for Development
```bash
composer install
npm install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan app:generate-keys
php artisan authentikate:create-admin
# Start development servers
composer run dev # Starts server, queue, logs, and vite
```
### Testing
```bash
composer run test
```
The test suite includes comprehensive coverage for:
- OIDC endpoints and JWT generation
- User authentication and authorization
- Application management
- UUID-based user identification
## Security
- JWT tokens use RS256 signing with 2048-bit RSA keys
- PKCE support for secure authorization flows
- UUID-based user identification in tokens (not sequential IDs)
- Secure session management
- Input validation and CSRF protection
- Email verification for new users
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes with tests
4. Submit a pull request
## License
[Add your license information here]
## Support
For issues and questions:
- Check the troubleshooting section above
- Review logs for error details
- Open an issue on the repository
---
**Remember**: Always run AuthentiKate behind HTTPS in production and keep your instance updated with security patches.