Skip to content

Commit fee1355

Browse files
OIDC test
1 parent 70c7207 commit fee1355

14 files changed

+1090
-34
lines changed

OIDC_IMPLEMENTATION.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# OIDC Implementation Summary
2+
3+
This document summarizes the OIDC authentication implementation for the Subscription Tracker application.
4+
5+
## Files Modified
6+
7+
### 1. **app/models.py**
8+
- Added `OIDCSettings` model to store OIDC configuration:
9+
- `display_name`: Name shown on the login button
10+
- `client_id`: OAuth2 client ID
11+
- `client_secret`: OAuth2 client secret
12+
- `discovery_endpoint`: OIDC discovery URL
13+
- `user_mapping_field`: Field to use for user matching (username, email, or custom)
14+
- `custom_attribute`: Custom OIDC claim name (if custom mapping selected)
15+
- `is_enabled`: Enable/disable OIDC (also controlled by environment variable)
16+
17+
### 2. **app/forms.py**
18+
- Added `OIDCSettingsForm` with validation for:
19+
- Display name
20+
- Client credentials
21+
- Discovery endpoint URL format
22+
- User mapping configuration
23+
- Custom attribute requirement when using custom mapping
24+
25+
### 3. **app/routes.py**
26+
- Updated imports to include `OIDCSettings` model and `OIDCSettingsForm`
27+
- Modified `login()` route to:
28+
- Check if OIDC is enabled via environment variable
29+
- Show OIDC login button when enabled
30+
- Hide normal login form when OIDC is active
31+
- Pass OIDC configuration to template
32+
- Added `oidc_settings()` route for admin configuration page
33+
- Added `oidc_login()` route to initiate OIDC authentication flow
34+
- Added `oidc_callback()` route to handle OIDC authentication callback with user mapping
35+
36+
### 4. **app/__init__.py**
37+
- Added Migration 4 to `migrate_database()` function:
38+
- Automatically creates `oidc_settings` table on startup
39+
- Supports PostgreSQL, MySQL, and SQLite
40+
41+
### 5. **app/templates/login.html**
42+
- Added conditional rendering:
43+
- Shows "Login with {OIDC_NAME}" button when OIDC is enabled
44+
- Shows normal username/password form when OIDC is disabled
45+
- Displays informational message when using OIDC
46+
47+
### 6. **app/templates/oidc_settings.html** (New File)
48+
- Admin-only OIDC configuration page
49+
- Form fields for all OIDC settings
50+
- JavaScript to show/hide custom attribute field
51+
- Displays callback URL for configuring in IdP
52+
- Informational alerts about OIDC behavior
53+
54+
### 7. **app/templates/base.html**
55+
- Added "OIDC Settings" link to Settings dropdown menu (admin only)
56+
57+
### 8. **config.py**
58+
- Added `OIDC_ENABLED` configuration option that reads from environment variable
59+
- Defaults to `false` if not set
60+
61+
### 9. **requirements.txt**
62+
- Added `Authlib==1.3.2` for OIDC/OAuth2 support
63+
64+
### 10. **docker-compose.yml**
65+
- Added `OIDC_ENABLED` environment variable (defaults to `false`)
66+
67+
### 11. **docker-compose.security.yml**
68+
- Added `OIDC_ENABLED` environment variable (defaults to `false`)
69+
70+
### 12. **OIDC_SETUP.md** (New File)
71+
- Comprehensive documentation for OIDC setup
72+
- Configuration steps
73+
- User mapping explanation
74+
- Troubleshooting guide
75+
- Examples for Keycloak and Azure AD
76+
77+
## How It Works
78+
79+
### 1. Configuration Flow
80+
1. Admin logs in with normal credentials (OIDC disabled)
81+
2. Admin navigates to Settings > OIDC Settings
82+
3. Admin configures:
83+
- Display name for the login button
84+
- Client ID and secret from IdP
85+
- Discovery endpoint URL
86+
- User mapping strategy
87+
4. Admin notes the callback URL to configure in IdP
88+
5. Admin sets `OIDC_ENABLED=true` in environment
89+
6. Application restarts with OIDC enabled
90+
91+
### 2. Authentication Flow (OIDC Enabled)
92+
1. User visits login page
93+
2. Instead of username/password form, sees "Login with {OIDC_NAME}" button
94+
3. User clicks button → redirected to IdP
95+
4. User authenticates at IdP
96+
5. IdP redirects back to `/auth/callback` with authorization code
97+
6. Application exchanges code for tokens
98+
7. Application extracts user info from ID token
99+
8. Application matches user based on configured mapping field:
100+
- Username: Match `preferred_username` or `username` claim against `username` in DB
101+
- Email: Match `email` claim against `email` in DB
102+
- Custom: Match custom claim against both `username` and `email` in DB
103+
9. If match found → user logged in successfully
104+
10. If no match → login denied with error message
105+
106+
### 3. User Mapping Logic
107+
- **No automatic user creation** - users must exist in database
108+
- **Exact matching required** - claim must exactly match database field
109+
- **Security by design** - unknown users cannot gain access
110+
- **Flexible mapping** - supports username, email, or custom attributes
111+
112+
## Environment Variables
113+
114+
### OIDC_ENABLED
115+
- **Type**: Boolean
116+
- **Default**: `false`
117+
- **Values**: `true`, `1`, `yes` (case-insensitive) = enabled; anything else = disabled
118+
- **Effect**:
119+
- When `true`: Disables normal login, shows OIDC button
120+
- When `false`: Normal username/password login
121+
122+
## Security Features
123+
124+
1. **Admin-only configuration** - Only admin users can configure OIDC
125+
2. **No automatic provisioning** - Users must exist before OIDC login
126+
3. **Failed login stops** - Unknown users are denied access immediately
127+
4. **Client secret protection** - Stored securely in database
128+
5. **Session management** - Uses Flask session for state management
129+
6. **CSRF protection** - Standard Flask-WTF CSRF protection on forms
130+
131+
## Testing Checklist
132+
133+
- [ ] OIDC settings page accessible by admin
134+
- [ ] Non-admin users cannot access OIDC settings
135+
- [ ] OIDC configuration saves successfully
136+
- [ ] Callback URL displays correctly
137+
- [ ] Login page shows OIDC button when `OIDC_ENABLED=true`
138+
- [ ] Login page shows normal form when `OIDC_ENABLED=false`
139+
- [ ] OIDC login redirects to IdP
140+
- [ ] OIDC callback handles successful authentication
141+
- [ ] User mapping works correctly (username/email/custom)
142+
- [ ] Unknown users are denied access
143+
- [ ] Database migration creates `oidc_settings` table
144+
- [ ] Application works normally with OIDC disabled
145+
146+
## Future Enhancements (Not Implemented)
147+
148+
- Automatic user provisioning from OIDC claims
149+
- Role mapping from OIDC groups
150+
- Multiple OIDC providers
151+
- Just-in-time user attribute updates
152+
- SSO session management
153+
- SAML support
154+
155+
## Compatibility
156+
157+
- **Python**: 3.8+
158+
- **Flask**: 3.0+
159+
- **Authlib**: 1.3.2+
160+
- **Databases**: SQLite, PostgreSQL, MySQL/MariaDB
161+
- **OIDC Providers**: Any standards-compliant OIDC provider

OIDC_QUICK_REFERENCE.md

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# OIDC Quick Reference
2+
3+
## Environment Variable
4+
5+
```bash
6+
# Enable OIDC authentication (disables normal login)
7+
OIDC_ENABLED=true
8+
9+
# Disable OIDC authentication (enables normal login)
10+
OIDC_ENABLED=false
11+
```
12+
13+
## Configuration Page
14+
15+
**URL**: Settings > OIDC Settings (Admin only)
16+
17+
**Required Fields**:
18+
- Display Name (appears on login button)
19+
- Client ID
20+
- Client Secret
21+
- Discovery Endpoint URL
22+
23+
**User Mapping Options**:
24+
- `username` - Match OIDC username/preferred_username claim
25+
- `email` - Match OIDC email claim
26+
- `custom` - Match a custom OIDC claim (must specify claim name)
27+
28+
## Callback URL
29+
30+
Configure this in your Identity Provider:
31+
```
32+
https://your-domain.com/auth/callback
33+
```
34+
35+
## Login Behavior
36+
37+
| OIDC_ENABLED | Login Page Shows |
38+
|--------------|------------------|
39+
| `true` | "Login with {OIDC_NAME}" button only |
40+
| `false` | Username/Password form only |
41+
42+
## User Matching Rules
43+
44+
1. OIDC claim value extracted from token
45+
2. Search database for matching user:
46+
- Username mapping: Check `User.username` field
47+
- Email mapping: Check `User.email` field
48+
- Custom mapping: Check both `User.username` and `User.email` fields
49+
3. If match found: Login successful
50+
4. If no match: Login denied (no auto-provisioning)
51+
52+
## Common OIDC Providers
53+
54+
### Keycloak
55+
```
56+
Discovery: https://keycloak.example.com/realms/{realm}/.well-known/openid-configuration
57+
Mapping: email or username
58+
```
59+
60+
### Azure AD / Entra ID
61+
```
62+
Discovery: https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration
63+
Mapping: email
64+
```
65+
66+
### Google Workspace
67+
```
68+
Discovery: https://accounts.google.com/.well-known/openid-configuration
69+
Mapping: email
70+
```
71+
72+
### Okta
73+
```
74+
Discovery: https://{your-okta-domain}/oauth2/default/.well-known/openid-configuration
75+
Mapping: email or username
76+
```
77+
78+
## Emergency Access
79+
80+
If OIDC is misconfigured:
81+
82+
1. Set `OIDC_ENABLED=false`
83+
2. Restart container: `docker-compose restart`
84+
3. Login with username/password
85+
4. Fix OIDC configuration
86+
5. Re-enable OIDC
87+
88+
## Troubleshooting Commands
89+
90+
```bash
91+
# View application logs
92+
docker-compose logs -f web
93+
94+
# Restart with OIDC disabled
95+
OIDC_ENABLED=false docker-compose restart
96+
97+
# Check OIDC discovery endpoint
98+
curl https://your-idp.com/.well-known/openid-configuration
99+
```
100+
101+
## Important Notes
102+
103+
⚠️ **Users must exist in database before OIDC login**
104+
⚠️ **Match field (username/email) must be populated in database**
105+
⚠️ **Normal login disabled when OIDC_ENABLED=true**
106+
⚠️ **Keep a backup admin account accessible**
107+
108+
## Quick Setup Steps
109+
110+
1. Configure OIDC settings in app (with OIDC_ENABLED=false)
111+
2. Add callback URL to IdP
112+
3. Test with OIDC_ENABLED=false first
113+
4. Set OIDC_ENABLED=true
114+
5. Restart container
115+
6. Test login
116+
117+
## Files Reference
118+
119+
- Configuration Page: `app/templates/oidc_settings.html`
120+
- Routes: `app/routes.py` (oidc_login, oidc_callback, oidc_settings)
121+
- Model: `app/models.py` (OIDCSettings)
122+
- Full Guide: `OIDC_SETUP.md`

0 commit comments

Comments
 (0)