nordabiz/docs/architecture/flows/01-authentication-flow.md
Maciej Pienczyn fa4fb92390 docs: Add complete architecture documentation with C4 diagrams
- System Context diagram (C4 Level 1)
- Container diagram (C4 Level 2)
- Flask component diagram (C4 Level 3)
- Deployment architecture with NPM proxy
- Database schema (PostgreSQL)
- External integrations (Gemini AI, Brave Search, PageSpeed)
- Network topology (INPI infrastructure)
- Security architecture
- API endpoints reference
- Troubleshooting guide
- Data flow diagrams (auth, search, AI chat, SEO audit, news monitoring)

All diagrams use Mermaid.js and render automatically on GitHub.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 12:40:52 +01:00

875 lines
26 KiB
Markdown

# Authentication Flow
**Document Version:** 1.0
**Last Updated:** 2026-01-10
**Status:** Production LIVE
**Flow Type:** User Authentication & Session Management
---
## Overview
This document describes the **complete authentication flow** for the Norda Biznes Hub application, covering:
- **User Registration** with email verification
- **Login** with session management
- **Email Verification** process
- **Password Reset** flow
- **Session Management** and cookies
- **Authorization Levels** and access control
**Key Technology:**
- **Authentication Framework:** Flask-Login
- **Password Hashing:** PBKDF2:SHA256 (werkzeug.security)
- **Session Storage:** Server-side session cookies
- **Email Delivery:** Microsoft Graph API (OAuth 2.0)
- **CSRF Protection:** Flask-WTF
**Security Features:**
- Email verification required before login
- Secure session cookies (HttpOnly, Secure, SameSite=Lax)
- CSRF protection on all forms
- Password strength requirements
- Input sanitization against XSS
- Rate limiting on authentication endpoints
---
## 1. User Registration Flow
### 1.1 Registration Sequence Diagram
```mermaid
sequenceDiagram
actor User
participant Browser
participant Flask as Flask App<br/>(app.py)
participant DB as PostgreSQL<br/>(users table)
participant EmailSvc as Email Service<br/>(email_service.py)
participant MSGraph as Microsoft Graph API
User->>Browser: Navigate to /register
Browser->>Flask: GET /register
Flask->>Browser: Render register.html
User->>Browser: Fill form (email, password, name, company_nip)
Browser->>Flask: POST /register
Note over Flask: CSRF token validated (automatic)
Note over Flask: Input sanitization
Flask->>Flask: validate_email(email)
Flask->>Flask: validate_password(password)<br/>(8+ chars, uppercase, lowercase, digit)
Flask->>Flask: validate NIP format (10 digits)
Flask->>DB: SELECT * FROM users WHERE email = ?
DB->>Flask: Check if exists
alt Email already exists
Flask->>Browser: Flash "Email już jest zarejestrowany"
Browser->>User: Show error message
else Email available
Flask->>DB: SELECT * FROM companies WHERE nip = ? AND status = 'active'
DB->>Flask: Company data (if NORDA member)
Flask->>Flask: generate_password_hash(password)<br/>(PBKDF2:SHA256)
Flask->>Flask: secrets.token_urlsafe(32)<br/>(verification token)
Flask->>Flask: Calculate token expiry<br/>(now + 24 hours)
Flask->>DB: INSERT INTO users<br/>(email, password_hash, name,<br/>verification_token, is_verified=FALSE,<br/>company_id, is_norda_member)
DB->>Flask: User created (id)
Flask->>EmailSvc: send_verification_email(email, token)
EmailSvc->>MSGraph: POST /users/noreply@nordabiznes.pl/sendMail<br/>(OAuth 2.0 + Bearer token)
MSGraph->>EmailSvc: 202 Accepted
Flask->>Browser: Flash "Sprawdź email"<br/>Redirect to /login
Browser->>User: Show success message
end
```
### 1.2 Registration Implementation Details
**Route:** `POST /register`
**File:** `app.py` (lines ~3077-3183)
**Rate Limit:** 5 requests per hour per IP
**Input Fields:**
- `email` (required, max 255 chars)
- `password` (required, 8+ chars with complexity requirements)
- `name` (required, max 255 chars)
- `company_nip` (required, 10 digits)
**Validation Steps:**
1. **Email validation:** Regex pattern check
2. **Password validation:**
- Minimum 8 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 digit
3. **NIP validation:** Must be exactly 10 digits
4. **Company membership check:** NIP lookup in `companies` table
**Database Operations:**
```sql
-- Check email uniqueness
SELECT * FROM users WHERE email = ?;
-- Verify company membership
SELECT * FROM companies WHERE nip = ? AND status = 'active';
-- Create user account
INSERT INTO users (
email, password_hash, name, company_nip,
verification_token, verification_token_expires,
is_verified, is_norda_member, company_id,
created_at
) VALUES (?, ?, ?, ?, ?, ?, FALSE, ?, ?, NOW());
```
**Security Measures:**
- **CSRF Protection:** Automatic via Flask-WTF
- **Input Sanitization:** `sanitize_input()` strips HTML/malicious patterns
- **Password Hashing:** PBKDF2:SHA256 (werkzeug default)
- **Rate Limiting:** 5 attempts/hour via Flask-Limiter
- **XSS Prevention:** All user inputs sanitized
**Email Verification Token:**
- Generated via `secrets.token_urlsafe(32)` (256-bit entropy)
- Stored in `users.verification_token` column
- Expires after 24 hours
- Single-use (cleared after verification)
---
## 2. Email Verification Flow
### 2.1 Email Verification Sequence Diagram
```mermaid
sequenceDiagram
actor User
participant Email as Email Client
participant Browser
participant Flask as Flask App<br/>(app.py)
participant DB as PostgreSQL
Note over User,Email: User receives verification email
User->>Email: Open verification email
Email->>Browser: Click link:<br/>https://nordabiznes.pl/verify-email/<token>
Browser->>Flask: GET /verify-email/<token>
Flask->>Flask: Extract token from URL
Flask->>DB: SELECT * FROM users<br/>WHERE verification_token = ?<br/>AND verification_token_expires > NOW()<br/>AND is_active = TRUE
alt Token valid and not expired
DB->>Flask: User found
alt User already verified
Flask->>Browser: Flash "Email został już zweryfikowany"<br/>Redirect to /login
else User not verified yet
Flask->>DB: UPDATE users SET<br/>is_verified = TRUE,<br/>verified_at = NOW(),<br/>verification_token = NULL,<br/>verification_token_expires = NULL<br/>WHERE id = ?
DB->>Flask: Update successful
Flask->>Browser: Flash "Email zweryfikowany!<br/>Możesz się teraz zalogować"<br/>Redirect to /login
Browser->>User: Show success message
end
else Token invalid or expired
Flask->>Browser: Flash "Link weryfikacyjny<br/>jest nieprawidłowy lub wygasł"<br/>Redirect to /login
Browser->>User: Show error message
end
```
### 2.2 Email Verification Implementation Details
**Route:** `GET /verify-email/<token>`
**File:** `app.py` (lines ~3369-3405)
**Rate Limit:** None (public endpoint)
**Verification Logic:**
```python
# Query user by token
user = db.query(User).filter(
User.verification_token == token,
User.verification_token_expires > datetime.now(),
User.is_active == True
).first()
if user and not user.is_verified:
user.is_verified = True
user.verified_at = datetime.now()
user.verification_token = None
user.verification_token_expires = None
db.commit()
```
**Token Expiry:**
- Verification tokens expire after **24 hours**
- Expired tokens cannot be used
- Users can request new verification email via `/resend-verification`
**Database Schema (users table):**
```sql
verification_token VARCHAR(255) NULL
verification_token_expires TIMESTAMP NULL
is_verified BOOLEAN DEFAULT FALSE
verified_at TIMESTAMP NULL
```
---
## 3. Login Flow
### 3.1 Login Sequence Diagram
```mermaid
sequenceDiagram
actor User
participant Browser
participant Flask as Flask App<br/>(app.py)
participant FlaskLogin as Flask-Login
participant DB as PostgreSQL
participant Session as Session Cookie
User->>Browser: Navigate to /login
Browser->>Flask: GET /login
Flask->>Browser: Render login.html with CSRF token
User->>Browser: Enter email & password<br/>(optional: remember me)
Browser->>Flask: POST /login<br/>(email, password, remember, csrf_token)
Note over Flask: CSRF token validated
Flask->>DB: SELECT * FROM users WHERE email = ?
alt User not found
DB->>Flask: No user
Flask->>Browser: Flash "Nieprawidłowy email lub hasło"
Browser->>User: Show error
else User found
DB->>Flask: User data
Flask->>Flask: check_password_hash(<br/>user.password_hash,<br/>password<br/>)
alt Password invalid
Flask->>Browser: Flash "Nieprawidłowy email lub hasło"
Browser->>User: Show error
else Password valid
alt User not active
Flask->>Browser: Flash "Konto zostało dezaktywowane"
Browser->>User: Show error
else User not verified
Flask->>Browser: Flash "Musisz potwierdzić adres email"
Browser->>User: Show error with resend link
else User active and verified
Flask->>FlaskLogin: login_user(user, remember=remember)
FlaskLogin->>Session: Set session cookie<br/>(secure, httponly, samesite=Lax)
Session->>Browser: Store session cookie
Flask->>DB: UPDATE users SET last_login = NOW() WHERE id = ?
DB->>Flask: Update successful
Flask->>Browser: Redirect to /dashboard<br/>(or 'next' URL if specified)
Browser->>User: Show dashboard
end
end
end
```
### 3.2 Login Implementation Details
**Route:** `POST /login`
**File:** `app.py` (lines ~3184-3240)
**Rate Limit:**
- **Development:** 1000 requests per hour
- **Production:** 5 requests per hour per IP
**Login Validation Steps:**
1. Check email exists in database
2. Verify password hash matches
3. Check `is_active = TRUE`
4. Require `is_verified = TRUE`
5. Create session via Flask-Login
6. Update `last_login` timestamp
**Session Configuration:**
```python
# app.py session settings (lines ~161-168)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
app.config['SESSION_COOKIE_SECURE'] = True # HTTPS only in production
app.config['SESSION_COOKIE_HTTPONLY'] = True # No JS access
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax' # CSRF protection
```
**Flask-Login Configuration:**
```python
# app.py Flask-Login setup (lines ~192-196)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.login_message = 'Zaloguj się, aby uzyskać dostęp do tej strony.'
```
**User Loader Function:**
```python
@login_manager.user_loader
def load_user(user_id):
"""Load user from database for Flask-Login"""
db = SessionLocal()
try:
return db.query(User).get(int(user_id))
except:
return None
finally:
db.close()
```
**Remember Me Functionality:**
- When enabled: Session cookie persists for **7 days**
- When disabled: Session cookie expires when browser closes
- Implemented via Flask-Login's `login_user(user, remember=True/False)`
**Next URL Redirect:**
```python
# Prevent open redirect vulnerability
next_page = request.args.get('next')
if next_page and not next_page.startswith('/'):
next_page = None
return redirect(next_page or url_for('dashboard'))
```
---
## 4. Session Management
### 4.1 Session Lifecycle
```mermaid
stateDiagram-v2
[*] --> Anonymous: User visits site
Anonymous --> Authenticated: Successful login
Authenticated --> Authenticated: Normal activity
Authenticated --> Anonymous: Logout
Authenticated --> Anonymous: Session expires (7 days)
Authenticated --> Anonymous: User deactivated
Anonymous --> [*]
```
### 4.2 Session Cookie Details
**Cookie Name:** `session` (Flask default)
**Storage:** Server-side (encrypted session data)
**Attributes:**
- `Secure` = True (HTTPS only in production)
- `HttpOnly` = True (prevents XSS cookie theft)
- `SameSite` = Lax (CSRF protection)
- `Max-Age` = 7 days (with remember me)
**Session Data Stored:**
- User ID (for user loader)
- CSRF token (automatic via Flask-WTF)
- Login timestamp
- Remember me flag
**Session Security:**
- Session cookie is **signed** with `SECRET_KEY`
- Session data is **encrypted** (Flask built-in)
- Session is **regenerated** on login (prevents session fixation)
- Session is **cleared** on logout
---
## 5. Logout Flow
### 5.1 Logout Sequence Diagram
```mermaid
sequenceDiagram
actor User
participant Browser
participant Flask as Flask App
participant FlaskLogin as Flask-Login
participant Session as Session Cookie
User->>Browser: Click "Wyloguj" button
Browser->>Flask: GET /logout
Note over Flask: @login_required decorator<br/>verifies user is authenticated
Flask->>FlaskLogin: logout_user()
FlaskLogin->>Session: Clear session data
Session->>Browser: Delete session cookie
Flask->>Browser: Flash "Wylogowano pomyślnie"<br/>Redirect to /
Browser->>User: Show homepage (logged out)
```
### 5.2 Logout Implementation Details
**Route:** `GET /logout`
**File:** `app.py` (lines ~3242-3248)
**Authentication:** Required (`@login_required`)
**Implementation:**
```python
@app.route('/logout')
@login_required
def logout():
"""User logout"""
logout_user() # Flask-Login clears session
flash('Wylogowano pomyślnie.', 'success')
return redirect(url_for('index'))
```
**What Happens on Logout:**
1. Flask-Login calls `logout_user()`
2. Session cookie is deleted
3. User object is removed from `current_user`
4. Browser redirected to homepage
5. All subsequent requests are anonymous
---
## 6. Password Reset Flow
### 6.1 Password Reset Sequence Diagram
```mermaid
sequenceDiagram
actor User
participant Browser
participant Flask as Flask App
participant DB as PostgreSQL
participant EmailSvc as Email Service
participant MSGraph as Microsoft Graph API
Note over User,Browser: Phase 1: Request Reset
User->>Browser: Navigate to /forgot-password
Browser->>Flask: GET /forgot-password
Flask->>Browser: Render forgot_password.html
User->>Browser: Enter email address
Browser->>Flask: POST /forgot-password (email)
Flask->>Flask: validate_email(email)
Flask->>DB: SELECT * FROM users<br/>WHERE email = ? AND is_active = TRUE
alt User found
DB->>Flask: User data
Flask->>Flask: secrets.token_urlsafe(32)<br/>(generate reset token)
Flask->>Flask: Calculate expiry (now + 1 hour)
Flask->>DB: UPDATE users SET<br/>reset_token = ?,<br/>reset_token_expires = ?<br/>WHERE email = ?
DB->>Flask: Update successful
Flask->>EmailSvc: send_password_reset_email(email, token)
EmailSvc->>MSGraph: POST /users/noreply@nordabiznes.pl/sendMail
MSGraph->>EmailSvc: 202 Accepted
Flask->>Browser: Flash "Sprawdź email"<br/>Redirect to /login
Browser->>User: Show message
else User not found
Note over Flask: Still show success message<br/>(prevent email enumeration)
Flask->>Browser: Flash "Sprawdź email"<br/>Redirect to /login
Browser->>User: Show message
end
Note over User,Browser: Phase 2: Reset Password
User->>Browser: Click link in email:<br/>https://nordabiznes.pl/reset-password/<token>
Browser->>Flask: GET /reset-password/<token>
Flask->>DB: SELECT * FROM users<br/>WHERE reset_token = ?<br/>AND reset_token_expires > NOW()
alt Token valid
DB->>Flask: User found
Flask->>Browser: Render reset_password.html<br/>(password form)
User->>Browser: Enter new password (twice)
Browser->>Flask: POST /reset-password/<token><br/>(new_password, confirm_password)
Flask->>Flask: validate_password(new_password)
Flask->>Flask: Check passwords match
Flask->>Flask: generate_password_hash(new_password)
Flask->>DB: UPDATE users SET<br/>password_hash = ?,<br/>reset_token = NULL,<br/>reset_token_expires = NULL<br/>WHERE reset_token = ?
DB->>Flask: Update successful
Flask->>Browser: Flash "Hasło zostało zmienione"<br/>Redirect to /login
Browser->>User: Show success message
else Token invalid or expired
Flask->>Browser: Flash "Link resetowania<br/>jest nieprawidłowy lub wygasł"<br/>Redirect to /forgot-password
Browser->>User: Show error
end
```
### 6.2 Password Reset Implementation Details
**Routes:**
- `POST /forgot-password` - Request reset
- `GET /reset-password/<token>` - Show reset form
- `POST /reset-password/<token>` - Process new password
**Files:** `app.py` (lines ~3251-3368)
**Rate Limit:** 5 requests per hour per IP
**Reset Token Properties:**
- Generated via `secrets.token_urlsafe(32)` (256-bit entropy)
- Expires after **1 hour**
- Single-use (cleared after successful reset)
- Stored in `users.reset_token` column
**Database Schema:**
```sql
reset_token VARCHAR(255) NULL
reset_token_expires TIMESTAMP NULL
```
**Security Considerations:**
- Reset tokens expire after 1 hour
- Tokens are cleared after use
- Password strength validation applied
- Email enumeration prevented (always show success message)
- Rate limiting prevents brute force
---
## 7. Authorization & Access Control
### 7.1 Authorization Levels
```mermaid
graph TB
subgraph "Authorization Hierarchy"
Public[👥 Public<br/>Anonymous Users]
Auth[🔐 Authenticated<br/>Logged-in Users]
Member[👔 NORDA Members<br/>is_norda_member=TRUE]
Admin[👨‍💼 Administrators<br/>is_admin=TRUE]
Public --> Auth
Auth --> Member
Member --> Admin
end
subgraph "Access Permissions"
PublicRoutes["Public Routes:<br/>/, /search, /company/*,<br/>/audit/*, /api/companies"]
AuthRoutes["Authenticated Routes:<br/>/dashboard, /chat,<br/>/forum/*, /wiadomosci/*,<br/>/kalendarz/*, /tablica/*"]
AdminRoutes["Admin Routes:<br/>/admin/*, /api/*/audit"]
end
Public --> PublicRoutes
Auth --> AuthRoutes
Admin --> AdminRoutes
```
### 7.2 Route Protection Decorators
**Public Access (No decorator):**
```python
@app.route('/')
def index():
"""Public company directory"""
# No authentication required
return render_template('index.html')
```
**Authenticated Users Only:**
```python
@app.route('/dashboard')
@login_required
def dashboard():
"""User dashboard - requires login"""
# current_user is automatically available
return render_template('dashboard.html', user=current_user)
```
**Admin Only (Custom check):**
```python
@app.route('/admin/users')
@login_required
def admin_users():
"""Admin user management"""
if not current_user.is_admin:
flash('Brak uprawnień administratora.', 'error')
return redirect(url_for('index'))
# Admin logic here
return render_template('admin/users.html')
```
### 7.3 Access Control Matrix
| Route Category | Public | Authenticated | NORDA Member | Admin |
|---------------|--------|---------------|--------------|-------|
| `/` (Company directory) | ✅ | ✅ | ✅ | ✅ |
| `/search` | ✅ | ✅ | ✅ | ✅ |
| `/company/<slug>` | ✅ | ✅ | ✅ | ✅ |
| `/audit/*/<slug>` | ✅ | ✅ | ✅ | ✅ |
| `/api/companies` | ✅ | ✅ | ✅ | ✅ |
| `/register`, `/login` | ✅ | ❌ | ❌ | ❌ |
| `/dashboard` | ❌ | ✅ | ✅ | ✅ |
| `/chat` | ❌ | ✅ | ✅ | ✅ |
| `/forum/*` | ❌ | ✅ | ✅ | ✅ |
| `/wiadomosci/*` | ❌ | ✅ | ✅ | ✅ |
| `/kalendarz/*` | ❌ | ✅ | ✅ | ✅ |
| `/tablica/*` | ❌ | ✅ | ✅ | ✅ |
| `/admin/*` | ❌ | ❌ | ❌ | ✅ |
| `/api/*/audit` | ❌ | ❌ | ❌ | ✅ |
**Legend:**
- ✅ Access granted
- ❌ Access denied (redirect to login or show error)
---
## 8. User Model Database Schema
### 8.1 Users Table Structure
```sql
CREATE TABLE users (
-- Primary Key
id SERIAL PRIMARY KEY,
-- Authentication
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
-- Profile
name VARCHAR(255),
phone VARCHAR(50),
-- Company Association
company_nip VARCHAR(10),
company_id INTEGER REFERENCES companies(id),
-- Status Flags
is_active BOOLEAN DEFAULT TRUE,
is_verified BOOLEAN DEFAULT FALSE,
is_admin BOOLEAN DEFAULT FALSE,
is_norda_member BOOLEAN DEFAULT FALSE,
-- Timestamps
created_at TIMESTAMP DEFAULT NOW(),
last_login TIMESTAMP,
verified_at TIMESTAMP,
-- Email Verification
verification_token VARCHAR(255),
verification_token_expires TIMESTAMP,
-- Password Reset
reset_token VARCHAR(255),
reset_token_expires TIMESTAMP,
-- Indexes
INDEX idx_users_email (email),
INDEX idx_users_company_id (company_id)
);
```
### 8.2 User Model (SQLAlchemy)
**File:** `database.py` (lines ~119-158)
```python
class User(Base, UserMixin):
"""User accounts with Flask-Login integration"""
__tablename__ = 'users'
# Authentication fields
id = Column(Integer, primary_key=True)
email = Column(String(255), unique=True, nullable=False, index=True)
password_hash = Column(String(255), nullable=False)
# Profile
name = Column(String(255))
company_nip = Column(String(10))
company_id = Column(Integer, ForeignKey('companies.id'), nullable=True)
phone = Column(String(50))
# Status
is_active = Column(Boolean, default=True)
is_verified = Column(Boolean, default=False)
is_admin = Column(Boolean, default=False)
is_norda_member = Column(Boolean, default=False)
# Timestamps
created_at = Column(DateTime, default=datetime.now)
last_login = Column(DateTime)
verified_at = Column(DateTime)
# Verification token
verification_token = Column(String(255))
verification_token_expires = Column(DateTime)
# Password reset token
reset_token = Column(String(255))
reset_token_expires = Column(DateTime)
# Relationships
company = relationship('Company', backref='users', lazy='joined')
conversations = relationship('AIChatConversation', back_populates='user')
forum_topics = relationship('ForumTopic', back_populates='author')
forum_replies = relationship('ForumReply', back_populates='author')
```
**UserMixin Methods (Flask-Login):**
- `is_authenticated` - Always True for logged-in users
- `is_active` - Returns `self.is_active`
- `is_anonymous` - Always False for logged-in users
- `get_id()` - Returns `str(self.id)` for session storage
---
## 9. Security Features Summary
### 9.1 Security Measures Implemented
| Feature | Implementation | Protection Against |
|---------|----------------|---------------------|
| **CSRF Protection** | Flask-WTF automatic tokens | Cross-Site Request Forgery |
| **Password Hashing** | PBKDF2:SHA256 (werkzeug) | Rainbow table attacks |
| **Secure Cookies** | HttpOnly, Secure, SameSite=Lax | XSS cookie theft, CSRF |
| **Email Verification** | Required before login | Fake accounts |
| **Input Sanitization** | `sanitize_input()` function | XSS attacks |
| **Rate Limiting** | Flask-Limiter (5 req/hour) | Brute force attacks |
| **Session Regeneration** | Flask-Login automatic | Session fixation |
| **Token Expiry** | 24h (verify), 1h (reset) | Token replay attacks |
| **Open Redirect Prevention** | Next URL validation | Phishing attacks |
| **Password Strength** | 8+ chars, complexity rules | Weak passwords |
### 9.2 Security Best Practices
**Implemented:**
✅ HTTPS enforced in production
✅ Password hashing with secure algorithm
✅ CSRF protection on all forms
✅ Session cookies with security flags
✅ Email verification required
✅ Rate limiting on auth endpoints
✅ Input sanitization
✅ Token expiry
✅ Open redirect prevention
**Potential Improvements:**
⚠️ Add account lockout after N failed attempts
⚠️ Implement 2FA (TOTP) for admins
⚠️ Add password history (prevent reuse)
⚠️ Log authentication events for auditing
⚠️ Add CAPTCHA on registration
⚠️ Implement session timeout (idle)
⚠️ Add IP-based rate limiting
---
## 10. Testing Accounts (Production)
### 10.1 Test Users
**IMPORTANT:** Use only these accounts for testing production features.
| Account | Email | Role | Purpose |
|---------|-------|------|---------|
| Test User | `test@nordabiznes.pl` | Regular User | Test user-level features |
| Test Admin | `testadmin@nordabiznes.pl` | Administrator | Test admin features |
**Test Account Credentials:**
- Password stored in CLAUDE.md (do not commit to repository)
- Accounts are pre-verified (`is_verified = TRUE`)
- Test Admin has `is_admin = TRUE` flag
**Usage:**
- Always use test accounts for production testing
- Never modify real user accounts for testing
- Test authentication flows, authorization, session management
---
## 11. Related Documentation
### 11.1 Architecture Documents
- [System Context Diagram](../01-system-context.md) - External actors and integrations
- [Container Diagram](../02-container-diagram.md) - Flask app and session storage
- [Flask Components](../04-flask-components.md) - Authentication routes and decorators
- [Database Schema](../05-database-schema.md) - Users table and relationships
### 11.2 Code Files
**Authentication Routes:**
- `app.py` lines ~3077-3500 (register, login, logout, verify, reset)
**User Model:**
- `database.py` lines ~119-158 (User model with Flask-Login)
**Email Service:**
- `email_service.py` (Microsoft Graph email sending)
**Security Utilities:**
- `app.py` - `sanitize_input()`, `validate_email()`, `validate_password()`
### 11.3 External Dependencies
- **Flask-Login:** User session management
- **Flask-WTF:** CSRF protection
- **Flask-Limiter:** Rate limiting
- **werkzeug.security:** Password hashing
- **secrets:** Cryptographic token generation
---
## 12. Maintenance & Updates
### 12.1 When to Update This Document
Update this document when:
- Authentication flow changes (new steps, validation)
- Session management changes (cookie settings, expiry)
- Security measures are added/modified
- New authorization levels are introduced
- User model schema changes
### 12.2 Verification Checklist
When updating authentication flow:
- [ ] Test registration with valid/invalid data
- [ ] Test email verification with valid/expired tokens
- [ ] Test login with correct/incorrect credentials
- [ ] Test session persistence (remember me)
- [ ] Test logout clears session
- [ ] Test password reset flow end-to-end
- [ ] Verify CSRF protection is active
- [ ] Verify rate limiting works
- [ ] Test authorization levels (public, user, admin)
- [ ] Verify security headers on cookies
### 12.3 Security Audit Checklist
Periodic security audit:
- [ ] Review password hashing algorithm (current: PBKDF2:SHA256)
- [ ] Check token expiry times (verify: 24h, reset: 1h)
- [ ] Verify session cookie security flags
- [ ] Review rate limiting thresholds
- [ ] Check for SQL injection vulnerabilities
- [ ] Test XSS prevention in inputs
- [ ] Verify CSRF protection coverage
- [ ] Review authentication logs for anomalies
- [ ] Test open redirect prevention
- [ ] Verify email verification enforcement
---
**Document End**
*This document is maintained as part of the Norda Biznes Hub architecture documentation. For questions or updates, contact the development team.*