- Zmiana nazwy: "Norda Biznes Hub" → "Norda Biznes Partner" - Aktualizacja modelu AI: Gemini 2.0 Flash → Gemini 3 Flash - Zachowano historyczne odniesienia w timeline i dokumentacji Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
26 KiB
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 Partner 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
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:
- Email validation: Regex pattern check
- Password validation:
- Minimum 8 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 digit
- NIP validation: Must be exactly 10 digits
- Company membership check: NIP lookup in
companiestable
Database Operations:
-- 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_tokencolumn - Expires after 24 hours
- Single-use (cleared after verification)
2. Email Verification Flow
2.1 Email Verification Sequence Diagram
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:
# 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):
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
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:
- Check email exists in database
- Verify password hash matches
- Check
is_active = TRUE - Require
is_verified = TRUE - Create session via Flask-Login
- Update
last_logintimestamp
Session Configuration:
# 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:
# 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:
@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:
# 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
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
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:
@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:
- Flask-Login calls
logout_user() - Session cookie is deleted
- User object is removed from
current_user - Browser redirected to homepage
- All subsequent requests are anonymous
6. Password Reset Flow
6.1 Password Reset Sequence Diagram
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 resetGET /reset-password/<token>- Show reset formPOST /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_tokencolumn
Database Schema:
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
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):
@app.route('/')
def index():
"""Public company directory"""
# No authentication required
return render_template('index.html')
Authenticated Users Only:
@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):
@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
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)
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 usersis_active- Returnsself.is_activeis_anonymous- Always False for logged-in usersget_id()- Returnsstr(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 | 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 = TRUEflag
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 - External actors and integrations
- Container Diagram - Flask app and session storage
- Flask Components - Authentication routes and decorators
- Database Schema - Users table and relationships
11.2 Code Files
Authentication Routes:
app.pylines ~3077-3500 (register, login, logout, verify, reset)
User Model:
database.pylines ~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 Partner architecture documentation. For questions or updates, contact the development team.