diff --git a/docs/authentication-setup.md b/docs/authentication-setup.md new file mode 100644 index 0000000..e7ce86f --- /dev/null +++ b/docs/authentication-setup.md @@ -0,0 +1,481 @@ +# Authentication Setup Guide + +This guide explains how to configure login credentials for the TSA Chapter Organizer application. The application uses BCrypt password hashing and supports multiple authentication methods depending on your environment. + +## Table of Contents + +- [Overview](#overview) +- [Available Roles](#available-roles) +- [Generating Password Hashes](#generating-password-hashes) +- [Development Setup](#development-setup) +- [Production Setup](#production-setup) +- [Managing Users](#managing-users) +- [Security Best Practices](#security-best-practices) +- [Troubleshooting](#troubleshooting) + +--- + +## Overview + +The TSA Chapter Organizer uses cookie-based authentication with BCrypt password hashing. User credentials are stored securely and never in plain text. The authentication system supports: + +- **BCrypt password hashing** (work factor 11) +- **Case-insensitive email matching** +- **Role-based access control** +- **Rate limiting** (5 failed attempts result in 15-minute lockout) +- **Session management** (20 minutes default, 30 days with "Remember Me") + +## Available Roles + +The application supports two user roles: + +- **Administrator** - Full access to all features +- **Advisor** - Standard user access + +Roles are defined in `WebApp/Authentication/AuthRoles.cs` and can be customized if needed. + +--- + +## Generating Password Hashes + +Before setting up authentication, you need to generate BCrypt hashes for your passwords. The application provides a development endpoint for this purpose. + +### Method 1: Development Endpoint (Recommended) + +1. Start the application in **Development** mode +2. Navigate to: `https://localhost:/dev/hash-password?password=YourPassword` +3. Copy the `hash` value from the JSON response + +**Example:** +``` +https://localhost:5001/dev/hash-password?password=MySecurePass123 +``` + +**Response:** +```json +{ + "password": "MySecurePass123", + "hash": "$2a$11$xyz123...", + "message": "Copy the hash value to your User Secrets configuration" +} +``` + +### Method 2: Programmatic (for automation) + +You can also generate hashes programmatically using the `PasswordHashGenerator` class: + +```csharp +using WebApp.Authentication; + +var hash = PasswordHashGenerator.GenerateHash("YourPassword"); +``` + +--- + +## Development Setup + +For local development, the application uses .NET User Secrets to store authentication credentials securely. + +### Prerequisites + +- .NET SDK installed +- User Secrets ID configured in `WebApp.csproj` (already configured: `73972335-ec46-4ad6-a959-8ebe0b06147d`) + +### Steps + +1. **Generate password hashes** using the development endpoint (see above) + +2. **Configure User Secrets** using the .NET CLI: + + ```bash + cd WebApp + + # Set authentication configuration + dotnet user-secrets set "Authentication:Users:0:Email" "admin@example.com" + dotnet user-secrets set "Authentication:Users:0:PasswordHash" "$2a$11$your_hash_here" + dotnet user-secrets set "Authentication:Users:0:Role" "Administrator" + dotnet user-secrets set "Authentication:Users:0:DisplayName" "Administrator" + + # Add additional users (increment the index) + dotnet user-secrets set "Authentication:Users:1:Email" "advisor@example.com" + dotnet user-secrets set "Authentication:Users:1:PasswordHash" "$2a$11$your_hash_here" + dotnet user-secrets set "Authentication:Users:1:Role" "Advisor" + dotnet user-secrets set "Authentication:Users:1:DisplayName" "Chapter Advisor" + ``` + +3. **Verify your configuration**: + + ```bash + dotnet user-secrets list + ``` + +4. **Run the application**: + + ```bash + dotnet run + ``` + +5. **Login** at `https://localhost:/login` + +### Example: Complete Development Setup + +```bash +# 1. Navigate to WebApp directory +cd WebApp + +# 2. Generate hash (via browser or curl) +# Visit: https://localhost:5001/dev/hash-password?password=admin123 + +# 3. Configure admin user +dotnet user-secrets set "Authentication:Users:0:Email" "admin@myschool.edu" +dotnet user-secrets set "Authentication:Users:0:PasswordHash" "$2a$11$REPLACE_WITH_GENERATED_HASH" +dotnet user-secrets set "Authentication:Users:0:Role" "Administrator" +dotnet user-secrets set "Authentication:Users:0:DisplayName" "TSA Admin" + +# 4. Configure advisor user (optional) +dotnet user-secrets set "Authentication:Users:1:Email" "advisor@myschool.edu" +dotnet user-secrets set "Authentication:Users:1:PasswordHash" "$2a$11$REPLACE_WITH_GENERATED_HASH" +dotnet user-secrets set "Authentication:Users:1:Role" "Advisor" +dotnet user-secrets set "Authentication:Users:1:DisplayName" "Chapter Advisor" + +# 5. Verify +dotnet user-secrets list + +# 6. Run +dotnet run +``` + +--- + +## Production Setup + +For production environments (Docker, deployed servers), you have two options for configuring authentication credentials. + +### Option 1: Volume-Mounted JSON File (Recommended) + +This approach allows you to edit credentials without rebuilding the container. + +#### Steps + +1. **Generate Password Hashes** (on your development machine): + - Use the development endpoint or programmatic method + +2. **Create `auth-secrets.json`** on your Docker host: + ```bash + cp auth-secrets.example.json auth-secrets.json + ``` + +3. **Edit `auth-secrets.json`** and replace the placeholder hashes: + ```json + { + "Authentication": { + "Users": [ + { + "Email": "admin@example.com", + "PasswordHash": "$2a$11$actual.hash.here", + "Role": "Administrator", + "DisplayName": "Administrator" + }, + { + "Email": "advisor@example.com", + "PasswordHash": "$2a$11$actual.hash.here", + "Role": "Advisor", + "DisplayName": "Chapter Advisor" + } + ] + } + } + ``` + +4. **Set proper file permissions**: + ```bash + chmod 600 auth-secrets.json + chown root:root auth-secrets.json # Adjust user/group as needed + ``` + +5. **Configure Docker Compose** (see `docker-compose.example.yml`): + ```yaml + volumes: + - ./auth-secrets.json:/app/Data/auth-secrets.json:ro + ``` + +6. **Update credentials**: Simply edit `auth-secrets.json` on the host and restart the container: + ```bash + docker-compose restart webapp + ``` + +**Security Note**: Never commit `auth-secrets.json` to version control. It should be in `.gitignore`. + +### Option 2: Environment Variables + +This approach is useful for container orchestration platforms (Kubernetes, Docker Swarm, etc.). + +#### Docker Compose Example + +```yaml +environment: + - TSA_Authentication__Users__0__Email=admin@example.com + - TSA_Authentication__Users__0__PasswordHash=$2a$11$hash... + - TSA_Authentication__Users__0__Role=Administrator + - TSA_Authentication__Users__0__DisplayName=Administrator + - TSA_Authentication__Users__1__Email=advisor@example.com + - TSA_Authentication__Users__1__PasswordHash=$2a$11$hash... + - TSA_Authentication__Users__1__Role=Advisor + - TSA_Authentication__Users__1__DisplayName=Chapter Advisor +``` + +#### Docker Run Example + +```bash +docker run -d \ + -p 8080:8080 \ + -e ASPNETCORE_ENVIRONMENT=Production \ + -e TSA_Authentication__Users__0__Email=admin@example.com \ + -e TSA_Authentication__Users__0__PasswordHash='$2a$11$hash...' \ + -e TSA_Authentication__Users__0__Role=Administrator \ + -e TSA_Authentication__Users__0__DisplayName=Administrator \ + tsa-chapter-organizer:latest +``` + +#### Kubernetes Secret Example + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: tsa-auth-secrets +type: Opaque +stringData: + TSA_Authentication__Users__0__Email: "admin@example.com" + TSA_Authentication__Users__0__PasswordHash: "$2a$11$hash..." + TSA_Authentication__Users__0__Role: "Administrator" + TSA_Authentication__Users__0__DisplayName: "Administrator" +``` + +**Note**: Environment variable names use double underscores (`__`) to represent nested configuration paths, with the `TSA_` prefix. + +--- + +## Managing Users + +### Adding a New User + +#### Development (User Secrets) + +```bash +# Find the next available index (check existing users first) +dotnet user-secrets list | grep "Authentication:Users" + +# Add new user (replace N with the next index) +dotnet user-secrets set "Authentication:Users:N:Email" "newuser@example.com" +dotnet user-secrets set "Authentication:Users:N:PasswordHash" "$2a$11$hash..." +dotnet user-secrets set "Authentication:Users:N:Role" "Advisor" +dotnet user-secrets set "Authentication:Users:N:DisplayName" "New User Name" +``` + +#### Production (JSON File) + +1. Edit `auth-secrets.json` on the host +2. Add a new entry to the `Users` array: + ```json + { + "Email": "newuser@example.com", + "PasswordHash": "$2a$11$hash...", + "Role": "Advisor", + "DisplayName": "New User Name" + } + ``` +3. Restart the container: `docker-compose restart webapp` + +#### Production (Environment Variables) + +Add new environment variables with the next index number and recreate the container. + +### Changing a Password + +1. **Generate a new password hash**: + - Development: Use `/dev/hash-password` endpoint + - Production: Generate on local dev machine + +2. **Update the configuration**: + - **Development**: `dotnet user-secrets set "Authentication:Users:N:PasswordHash" "$2a$11$new_hash"` + - **Production (JSON)**: Edit `auth-secrets.json` and update the `PasswordHash` value + - **Production (Env Vars)**: Update the environment variable + +3. **Restart the application**: + - Development: Restart the app (automatic on file change for User Secrets) + - Production: `docker-compose restart webapp` + +### Removing a User + +1. **Remove from configuration**: + - **Development**: Remove the User Secrets entries for that user index + - **Production (JSON)**: Remove the user entry from `auth-secrets.json` + - **Production (Env Vars)**: Remove the environment variables + +2. **Restart the application** + +**Note**: When removing users from JSON or environment variables, you may need to renumber remaining users' indices, or leave gaps (both approaches work). + +--- + +## Security Best Practices + +1. **Strong Passwords**: Use strong, unique passwords for each user. Consider using a password manager. + +2. **File Permissions** (Production): + ```bash + chmod 600 auth-secrets.json + chown root:root auth-secrets.json + ``` + +3. **Never Commit Secrets**: Ensure the following are in `.gitignore`: + - `auth-secrets.json` + - `docker-compose.yml` (if it contains secrets) + - User Secrets files (automatically ignored by .NET) + +4. **Use HTTPS in Production**: Always configure SSL/TLS certificates for production deployments. + +5. **Backup Credentials Securely**: Store encrypted backups of `auth-secrets.json` in a secure location. + +6. **Password Rotation**: Periodically regenerate password hashes and update credentials. + +7. **Monitor Access**: Review application logs for failed login attempts: + ```bash + docker-compose logs webapp | grep "Failed login" + ``` + +8. **Limit Access**: Only grant Administrator role to trusted users. Use Advisor role for general access. + +9. **Rate Limiting**: The application automatically enforces rate limiting (5 failed attempts = 15 minute lockout). Monitor logs for suspicious activity. + +--- + +## Troubleshooting + +### Can't Login + +1. **Verify configuration is loaded**: + - **Development**: Check User Secrets are configured: `dotnet user-secrets list` + - **Production**: Check if file is mounted: `docker exec tsa-app ls -la /app/Data/auth-secrets.json` + +2. **Check JSON syntax** (if using JSON file): + ```bash + cat auth-secrets.json | jq . + ``` + Or validate online using a JSON validator. + +3. **Verify password hash**: Ensure the hash was copied completely (BCrypt hashes are ~60 characters) + +4. **Check application logs**: + ```bash + docker-compose logs webapp | grep -i "authentication\|login" + ``` + +5. **Verify email is correct**: Email matching is case-insensitive, but ensure it matches exactly what you configured. + +### Rate Limited / Locked Out + +If you see "Too many failed attempts" error: + +1. Wait 15 minutes for the lockout to expire +2. Or restart the container (resets rate limit tracking): + ```bash + docker-compose restart webapp + ``` + +### Forgot Admin Password + +1. Generate a new password hash using the dev endpoint (on local dev machine) +2. Update `auth-secrets.json` (production) or User Secrets (development) +3. Restart the application + +### No Users Configured Error + +If you see "Authentication system not configured" or "No users configured": + +1. **Development**: Verify User Secrets are set: + ```bash + dotnet user-secrets list | grep Authentication + ``` + +2. **Production**: Check that `auth-secrets.json` exists and is properly mounted: + ```bash + docker exec tsa-app cat /app/Data/auth-secrets.json + ``` + +3. Verify the JSON structure matches the expected format (see `auth-secrets.example.json`) + +### Container Won't Start + +Check logs for configuration errors: +```bash +docker-compose logs webapp +``` + +Common issues: +- JSON syntax error in `auth-secrets.json` +- File permissions preventing file access +- Missing required configuration values + +--- + +## Example: Complete Production Setup + +```bash +# 1. Generate password hash locally (in development) +# Visit: https://localhost:5001/dev/hash-password?password=SecurePass123 + +# 2. Create secrets file +cat > auth-secrets.json <