Initial
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
# Login System Documentation
|
||||
|
||||
## Overview
|
||||
A complete authentication system for the CGW Product Finder that integrates with the user management system. Users must log in with their credentials to access the product finder application.
|
||||
|
||||
## Features
|
||||
|
||||
### 🔐 Secure Authentication
|
||||
- Password verification using PBKDF2-SHA256 hashing
|
||||
- Session-based authentication with HTTP-only cookies
|
||||
- Automatic session management
|
||||
- Active/inactive user account checking
|
||||
|
||||
### 📍 Multi-Location Support
|
||||
- Automatic location selection for single-location users
|
||||
- Location selection page for users with multiple accessible locations
|
||||
- Default location preference
|
||||
- Location-based access control
|
||||
|
||||
### 🛡️ Security Features
|
||||
- Login required decorator protects all main routes
|
||||
- Inactive accounts are automatically blocked
|
||||
- Sessions expire on logout or server restart
|
||||
- Secure session cookies (HTTP-only, SameSite)
|
||||
|
||||
## User Flow
|
||||
|
||||
### 1. Login Process
|
||||
1. User visits the app → Redirected to `/login`
|
||||
2. User enters **username** and **password**
|
||||
3. System validates credentials against `users.json`
|
||||
4. System checks if user account is **active**
|
||||
5. If valid and active:
|
||||
- Session is created
|
||||
- User accessible locations are loaded
|
||||
|
||||
### 2. Location Selection (if applicable)
|
||||
- **Single Accessible Location**: User goes directly to main app (location selection bypassed)
|
||||
- **Multiple Accessible Locations**: User is redirected to `/select-location`
|
||||
- Shows all accessible locations
|
||||
- Default location is pre-selected
|
||||
- User can choose their working location
|
||||
- Selection is saved to session
|
||||
- **Note**: Default location is automatically marked as accessible when user is created
|
||||
|
||||
### 3. Main Application Access
|
||||
- User accesses the Product Finder
|
||||
- User info displayed in header (username + current location)
|
||||
- Logout button available in header
|
||||
|
||||
## Routes
|
||||
|
||||
### Public Routes (No Login Required)
|
||||
- `GET /login` - Login page
|
||||
- `POST /api/login` - Login endpoint
|
||||
- `GET /users` - User management page
|
||||
|
||||
### Protected Routes (Login Required)
|
||||
- `GET /` - Main product finder app
|
||||
- `GET /quiz` - Quiz page (alias for main app)
|
||||
- `GET /image-test` - Image generation test page
|
||||
- `GET /select-location` - Location selection page
|
||||
- `POST /api/select-location` - Set current location
|
||||
|
||||
### Session Routes
|
||||
- `GET /api/session` - Get current session info
|
||||
- `POST /api/logout` - Logout and clear session
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### POST /api/login
|
||||
Authenticate user and create session.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"username": "john_doe",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
|
||||
**Success Response:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Login successful",
|
||||
"requiresLocationSelection": true
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses:**
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Invalid username or password"
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "error",
|
||||
"message": "Account is inactive. Please contact an administrator."
|
||||
}
|
||||
```
|
||||
|
||||
### GET /api/session
|
||||
Get current user session information.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"username": "john_doe",
|
||||
"defaultLocation": "LINDS",
|
||||
"currentLocation": "KC",
|
||||
"accessibleLocations": ["LINDS", "KC", "IOLA"]
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/select-location
|
||||
Select a location for the current session.
|
||||
|
||||
**Request:**
|
||||
```json
|
||||
{
|
||||
"location": "KC"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Location selected",
|
||||
"currentLocation": "KC"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /api/logout
|
||||
Log out and clear session.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"status": "success",
|
||||
"message": "Logged out successfully"
|
||||
}
|
||||
```
|
||||
|
||||
## Session Data
|
||||
|
||||
The session stores:
|
||||
- `user_id`: Index of user in users.json
|
||||
- `username`: Username string
|
||||
- `defaultLocation`: User's default location code
|
||||
- `currentLocation`: Currently selected location code
|
||||
- `accessibleLocations`: Array of location codes user can access
|
||||
|
||||
## Authentication Decorator
|
||||
|
||||
The `@login_required` decorator protects routes:
|
||||
|
||||
```python
|
||||
@app.route('/protected-page')
|
||||
@login_required
|
||||
def protected_page():
|
||||
return render_template('protected.html')
|
||||
```
|
||||
|
||||
The decorator:
|
||||
1. Checks if user is logged in (has `user_id` in session)
|
||||
2. Validates user still exists in users.json
|
||||
3. Checks if user account is still active
|
||||
4. Redirects to login if any check fails
|
||||
|
||||
## Getting Current User in Routes
|
||||
|
||||
```python
|
||||
@app.route('/my-route')
|
||||
@login_required
|
||||
def my_route():
|
||||
user = get_current_user()
|
||||
username = session.get('username')
|
||||
current_location = session.get('currentLocation')
|
||||
|
||||
# Use user data...
|
||||
return render_template('page.html')
|
||||
```
|
||||
|
||||
## Location-Based Access Control
|
||||
|
||||
Users can only access locations they have permission for:
|
||||
|
||||
```python
|
||||
# In users.json
|
||||
{
|
||||
"username": "john_doe",
|
||||
"locationSettings": {
|
||||
"LINDS": { "accessible": true },
|
||||
"IOLA": { "accessible": false },
|
||||
"KC": { "accessible": true },
|
||||
"BMD": { "accessible": false }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This user can access:
|
||||
- ✅ Lindsborg (LINDS)
|
||||
- ✅ KC (KC)
|
||||
- ❌ Iola (IOLA)
|
||||
- ❌ BMD (BMD)
|
||||
|
||||
## User Interface Components
|
||||
|
||||
### Login Page (`/login`)
|
||||
- Clean, centered login form
|
||||
- Username and password fields
|
||||
- Submit button with loading spinner
|
||||
- Link to User Management page
|
||||
- Error message display
|
||||
|
||||
### Location Selection Page (`/select-location`)
|
||||
- Shows logged-in username
|
||||
- Shows default location
|
||||
- Radio buttons for each accessible location
|
||||
- Default location is pre-selected
|
||||
- Continue and Logout buttons
|
||||
|
||||
### Main App Header
|
||||
- User info display: `👤 username | 📍 location`
|
||||
- Logout button in header
|
||||
- Positioned in top-right corner
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Password Security
|
||||
- Passwords are hashed using PBKDF2-SHA256
|
||||
- Hashes are never reversed or displayed
|
||||
- Hash verification happens server-side only
|
||||
|
||||
### Session Security
|
||||
- Sessions use secure random keys
|
||||
- Cookies are HTTP-only (not accessible via JavaScript)
|
||||
- SameSite cookie policy prevents CSRF attacks
|
||||
- Sessions cleared on logout
|
||||
|
||||
### Account Status
|
||||
- Inactive accounts cannot log in
|
||||
- If account is deactivated while logged in, next request will log them out
|
||||
- User must have at least one accessible location
|
||||
|
||||
## Testing the Login System
|
||||
|
||||
### Test User Creation
|
||||
1. Go to `/users`
|
||||
2. Create a test user:
|
||||
- Username: `testuser`
|
||||
- Password: `password123`
|
||||
- Default Location: Lindsborg
|
||||
- Check "Accessible" for Lindsborg and KC
|
||||
|
||||
### Test Login Flow
|
||||
1. Go to `/` (should redirect to `/login`)
|
||||
2. Enter credentials: `testuser` / `password123`
|
||||
3. Click "Sign In"
|
||||
4. Since user has 2 accessible locations → redirected to `/select-location`
|
||||
5. Choose a location and click "Continue"
|
||||
6. Now viewing main Product Finder app
|
||||
7. See user info in header
|
||||
8. Click "Logout" to end session
|
||||
|
||||
### Test Single Location User
|
||||
1. Create user with only 1 accessible location
|
||||
2. Log in
|
||||
3. Should go directly to main app (skip location selection)
|
||||
|
||||
### Test Inactive User
|
||||
1. Create and log in as a user
|
||||
2. In User Management, toggle user to "Inactive"
|
||||
3. Try to log in → Should see "Account is inactive" message
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Please log in first" on all pages
|
||||
- Session may have expired
|
||||
- Server may have restarted (sessions are in-memory)
|
||||
- Clear browser cookies and log in again
|
||||
|
||||
### "User not found" error
|
||||
- User may have been deleted while logged in
|
||||
- Log out and log back in
|
||||
|
||||
### Can't access certain locations
|
||||
- Check user's "Accessible" checkboxes in User Management
|
||||
- User must have at least one accessible location
|
||||
|
||||
### Stuck on location selection page
|
||||
- User must have multiple accessible locations
|
||||
- If this shouldn't happen, check user's location settings
|
||||
- Or click "Sign Out" and contact administrator
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential additions:
|
||||
- [ ] Remember me checkbox (persistent sessions)
|
||||
- [ ] Password reset functionality
|
||||
- [ ] Session timeout after inactivity
|
||||
- [ ] Login attempt limiting (brute force protection)
|
||||
- [ ] Two-factor authentication
|
||||
- [ ] Session management dashboard
|
||||
- [ ] Location switching without re-login
|
||||
- [ ] Audit log of login attempts
|
||||
Reference in New Issue
Block a user