|
| 1 | +# Authentication & Authorization |
| 2 | + |
| 3 | +The application is designed with the expectation that it operates behind a reverse proxy in a production environment. It does **not** handle user authentication (i.e., logging users in) by itself. Instead, it trusts a header that is injected by an upstream authentication service. |
| 4 | + |
| 5 | +## Production Authentication Flow |
| 6 | + |
| 7 | +The intended flow for user authentication in a production environment is as follows: |
| 8 | + |
| 9 | +``` |
| 10 | + +-----------+ +-----------------+ +----------------+ +--------------------+ |
| 11 | + | | | | | | | | |
| 12 | + | User |----->| Reverse Proxy |----->| Auth Service |----->| Atlas UI Backend | |
| 13 | + | | 1. | | 2. | | 3. | | |
| 14 | + +-----------+ +-----------------+ +----------------+ +--------------------+ |
| 15 | +``` |
| 16 | + |
| 17 | +1. The user makes a request to the application's public URL, which is handled by the **Reverse Proxy**. |
| 18 | +2. The Reverse Proxy communicates with an **Authentication Service** (e.g., an SSO provider, an OAuth server) to validate the user's credentials (like cookies or tokens). |
| 19 | +3. Once the user is authenticated, the Reverse Proxy **injects the user's identity** (e.g., their email address) into an HTTP header and forwards the request to the **Atlas UI Backend**. |
| 20 | + |
| 21 | +The backend application reads this header to identify the user. The header name is configurable via the `AUTH_USER_HEADER` environment variable (default: `X-User-Email`). This allows flexibility for different reverse proxy setups that may use different header names (e.g., `X-Authenticated-User`, `X-Remote-User`). This model is secure only if the backend is not directly exposed to the internet, ensuring that all requests are processed by the proxy first. |
| 22 | + |
| 23 | +If using AWS Application Load Balancer (ALB) as the Auth Service, the following authentication configuration should be used: |
| 24 | + |
| 25 | +``` |
| 26 | + AUTH_USER_HEADER=x-amzn-oidc-data |
| 27 | + AUTH_USER_HEADER_TYPE=aws-alb-jwt |
| 28 | + AUTH_AWS_EXPECTED_ALB_ARN=arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/your-alb-name/... |
| 29 | + AUTH_AWS_REGION=us-east-1 |
| 30 | +``` |
| 31 | + |
| 32 | +This configuration will decode the base64-encoded JWT passed in the x-amzn-oidc-data header, validate it, and extract the user's email address from the validated JWT. |
| 33 | + |
| 34 | +## Development Behavior |
| 35 | + |
| 36 | +In a local development environment (when `DEBUG_MODE=true` in the `.env` file), the system falls back to using a default `[email protected]` user if the configured authentication header is not present. |
| 37 | + |
| 38 | +## Configuring the Authentication Header |
| 39 | + |
| 40 | +Different reverse proxy setups use different header names to pass authenticated user information. The application supports configuring the header name via the `AUTH_USER_HEADER` environment variable. |
| 41 | + |
| 42 | +**Default Configuration:** |
| 43 | +``` |
| 44 | +AUTH_USER_HEADER=X-User-Email |
| 45 | +``` |
| 46 | + |
| 47 | +**Common Alternative Headers:** |
| 48 | +``` |
| 49 | +# For Apache mod_auth setups |
| 50 | +AUTH_USER_HEADER=X-Remote-User |
| 51 | +
|
| 52 | +# For some SSO providers |
| 53 | +AUTH_USER_HEADER=X-Authenticated-User |
| 54 | +
|
| 55 | +# For custom reverse proxy configurations |
| 56 | +AUTH_USER_HEADER=X-Custom-Auth-Header |
| 57 | +``` |
| 58 | + |
| 59 | +This setting allows the application to work with various authentication infrastructures without code changes. |
| 60 | + |
| 61 | +## Proxy Secret Authentication (Optional Security Layer) |
| 62 | + |
| 63 | +For additional security, you can configure the application to require a secret value in a specific header to validate that requests are coming from your trusted reverse proxy. This prevents direct access to the backend application, even if it's accidentally exposed. |
| 64 | + |
| 65 | +**When to Use Proxy Secret Authentication:** |
| 66 | +- When you want an additional layer of security beyond network isolation |
| 67 | +- To prevent unauthorized access if the backend accidentally becomes publicly accessible |
| 68 | +- To ensure requests only come from your approved reverse proxy |
| 69 | + |
| 70 | +**Configuration:** |
| 71 | + |
| 72 | +Add the following to your `.env` file: |
| 73 | + |
| 74 | +```bash |
| 75 | +# Enable proxy secret validation |
| 76 | +FEATURE_PROXY_SECRET_ENABLED=true |
| 77 | + |
| 78 | +# Header name for the proxy secret (default: X-Proxy-Secret) |
| 79 | +PROXY_SECRET_HEADER=X-Proxy-Secret |
| 80 | + |
| 81 | +# The actual secret value - use a strong, randomly generated value |
| 82 | +PROXY_SECRET=your-secure-random-secret-here |
| 83 | + |
| 84 | +# Optional: Customize the redirect URL for failed authentication (default: /auth) |
| 85 | +AUTH_REDIRECT_URL=/auth |
| 86 | +``` |
| 87 | + |
| 88 | +**Reverse Proxy Configuration:** |
| 89 | + |
| 90 | +Configure your reverse proxy to inject the secret header with every request. Examples: |
| 91 | + |
| 92 | +**NGINX:** |
| 93 | +```nginx |
| 94 | +location / { |
| 95 | + proxy_pass http://backend:8000; |
| 96 | + proxy_set_header X-Proxy-Secret "your-secure-random-secret-here"; |
| 97 | + proxy_set_header X-User-Email $remote_user; |
| 98 | + # ... other headers |
| 99 | +} |
| 100 | +``` |
| 101 | + |
| 102 | +**Apache:** |
| 103 | +```apache |
| 104 | +<Location /> |
| 105 | + RequestHeader set X-Proxy-Secret "your-secure-random-secret-here" |
| 106 | + RequestHeader set X-User-Email %{REMOTE_USER}e |
| 107 | + ProxyPass http://backend:8000/ |
| 108 | + ProxyPassReverse http://backend:8000/ |
| 109 | +</Location> |
| 110 | +``` |
| 111 | + |
| 112 | +**Behavior:** |
| 113 | +- When enabled, the middleware validates the proxy secret on every request (except static files and the auth endpoint) |
| 114 | +- If the secret is missing or incorrect: |
| 115 | + - **API endpoints** (`/api/*`): Return 401 Unauthorized |
| 116 | + - **Browser endpoints**: Redirect to the configured auth URL |
| 117 | +- **Debug mode** (`DEBUG_MODE=true`): Proxy secret validation is automatically disabled for local development |
| 118 | + |
| 119 | +**Security Best Practices:** |
| 120 | +- Generate a strong, random secret (e.g., 32+ characters) |
| 121 | +- Store the secret securely in environment variables, not in configuration files |
| 122 | +- Use different secrets for different environments (dev, staging, production) |
| 123 | +- Rotate the secret periodically as part of your security policy |
| 124 | +- Never commit the secret to version control |
| 125 | + |
| 126 | +## Customizing Authorization |
| 127 | + |
| 128 | +**IMPORTANT: For production deployments, configuring authorization is essential.** The default implementation is a mock and **must be replaced** with your organization's actual authorization system. You have two primary methods to achieve this: |
| 129 | + |
| 130 | +### Recommended Method: HTTP Endpoint |
| 131 | + |
| 132 | +You can configure the application to call an external HTTP endpoint to check for group membership. This is the most flexible and maintainable solution, requiring no code changes to the application itself. |
| 133 | + |
| 134 | +1. **Configure the Endpoint in `.env`**: |
| 135 | + Add the following variables to your `.env` file: |
| 136 | + ``` |
| 137 | + # The URL of your authorization service |
| 138 | + AUTH_GROUP_CHECK_URL=https://your-auth-service.example.com/api/check-group |
| 139 | +
|
| 140 | + # The API key for authenticating with your service |
| 141 | + AUTH_GROUP_CHECK_API_KEY=your-secret-api-key |
| 142 | + ``` |
| 143 | +
|
| 144 | +2. **Endpoint Requirements**: |
| 145 | + Your authorization endpoint must: |
| 146 | + * Accept a `POST` request. |
| 147 | + * Expect a JSON body with `user_id` and `group_id`: |
| 148 | + ```json |
| 149 | + { |
| 150 | + |
| 151 | + "group_id": "admin" |
| 152 | + } |
| 153 | + ``` |
| 154 | + * Authenticate requests using a bearer token in the `Authorization` header. |
| 155 | + * Return a JSON response with a boolean `is_member` field: |
| 156 | + ```json |
| 157 | + { |
| 158 | + "is_member": true |
| 159 | + } |
| 160 | + ``` |
| 161 | +
|
| 162 | +If `AUTH_GROUP_CHECK_URL` is not set, the application will fall back to the mock implementation in `backend/core/auth.py`. |
| 163 | +
|
| 164 | +When using the mock implementation (no external endpoint configured), **all users are treated as part of the `users` group by default**. This ensures that basic, non-privileged features remain available even without an authorization service. Higher-privilege groups such as `admin` still require explicit membership via the mock group table or your real authorization system. |
| 165 | +
|
| 166 | +### Legacy Method: Modifying the Code |
| 167 | +
|
| 168 | +For advanced use cases, you can still directly modify the `is_user_in_group` function located in `backend/core/auth.py`. The default implementation is a mock and **must be replaced** if you are not using the HTTP endpoint method. |
0 commit comments