Session Configuration
Core Session Settings
The application configures Flask sessions with the following settings inapp/config.py:49:
Maximum session duration for all users.Value:
72000 seconds (20 hours)Purpose: Absolute maximum time a user can remain logged in without re-authenticating.Inactivity timeout for platform administrators.Value:
1800 seconds (30 minutes)Purpose: Platform admin sessions expire after 30 minutes of inactivity for enhanced security.Prevents JavaScript access to session cookies.Value:
TrueSecurity: Protects against XSS attacks stealing session cookies.Name of the session cookie.Value:
notify_admin_sessionRequires HTTPS for session cookies.Value:
True (production), False (development)Security: Prevents session hijacking on insecure connections.Controls when cookies are sent with cross-site requests.Value:
LaxOptions: Strict, Lax, NoneSecurity: Provides CSRF protection while allowing legitimate cross-site navigation.Controls whether session expiry is updated on every request.Value:
FalsePurpose: Reduces unnecessary session updates. Session expiry is updated only when session data changes.Setting
SESSION_REFRESH_EACH_REQUEST to False improves performance by avoiding session writes on every request. The session expiry is still updated by the save_service_or_org_after_request handler for most page loads.Custom Session Interface
GOV.UK Notify Admin implements a custom session interface (NotifyAdminSessionInterface) to support differentiated timeout policies for regular users and platform administrators.
Implementation
The custom session interface is defined inapp/notify_session.py:10 and registered in app/__init__.py:239:
Session Lifecycle
Session Creation
When a user logs in:- User ID is stored in the session:
session["user_id"] = user.id(app/models/user.py:192) - Session start timestamp is recorded:
session["session_start"] = datetime.now(UTC).isoformat()(app/models/user.py:193) - Current session ID from API is stored:
session["current_session_id"] = user.current_session_id(app/utils/login.py:23)
Session Expiry Calculation
TheNotifyAdminSessionInterface calculates session expiry based on user type:
For regular users:
- Session lasts up to 20 hours from login
- No inactivity timeout
- Effectively never needs refreshing
- Session lasts up to 20 hours from login
- Additional: Session expires after 30 minutes of inactivity
- Inactivity timer resets on each page load
app/notify_session.py:11:
Session Validation
On each request,open_session validates the session expiry:
app/notify_session.py:42
Session Updates
Thesave_session method updates session expiry timestamps:
app/notify_session.py:52
Selective Cookie Setting
The session interface implementsshould_set_cookie to avoid setting cookies on specific blueprints:
- Prevents session refresh on auto-updating pages (dashboard, notifications list)
- Avoids race conditions where slow requests overwrite session data from newer requests
- No cookies on letter preview images
json_updates- JSON endpoints for auto-refreshing pagesno_cookie- Letter previews and platform admin email branding previews
app/notify_session.py:64
Session Data
Standard Session Keys
| Key | Type | Purpose | Set By |
|---|---|---|---|
user_id | string | Logged-in user ID | app/models/user.py:192 |
session_start | string (ISO) | Login timestamp | app/models/user.py:193 |
session_expiry | string (ISO) | Calculated expiry time | app/notify_session.py:58 |
current_session_id | string | API session ID | app/utils/login.py:23 |
service_id | string | Currently selected service | Various |
organisation_id | string | Currently selected organisation | Various |
Temporary Session Keys
These keys are used for multi-step flows and cleared after completion:| Key | Purpose | Cleared By |
|---|---|---|
user_details | Registration/login flow data | app/utils/login.py:31 |
file_uploads | Temporary file upload tracking | app/utils/login.py:32 |
invited_user_id | User invitation acceptance | Various |
invited_org_user_id | Org invitation acceptance | Various |
webauthn_registration_state | WebAuthn registration state | app/main/views/webauthn_credentials.py:33 |
disable_platform_admin_view | Platform admin view toggle | app/main/views/your_account.py:272 |
Email and Phone Change Flows
| Key | Purpose | Source |
|---|---|---|
NEW_EMAIL | Email address being changed to | app/main/views/your_account.py:69 |
NEW_MOBILE | Mobile number being changed to | app/main/views/your_account.py:129 |
NEW_MOBILE_PASSWORD_CONFIRMED | Password confirmed for mobile change | app/main/views/your_account.py:168 |
Session Security
CSRF Protection
Enable Flask-WTF CSRF protection.Value:
True (production/development), False (test)CSRF token expiration time.Value:
None (no time limit)Reason: Session timeout provides sufficient protection without requiring token refresh.app/__init__.py:158):
app/__init__.py:490:
Session Invalidation
Sessions are invalidated when:-
User logs out -
app/models/user.py:202: -
Session ID mismatch -
app/models/user.py:181:Protects against session hijacking by ensuring only the most recent session is valid. -
Session expiry - Automatically by
NotifyAdminSessionInterface.open_session
Security Headers
Session cookies are protected by multiple HTTP security headers set inapp/__init__.py:361:
- Strict-Transport-Security:
max-age=31536000; includeSubDomains; preload - X-Content-Type-Options:
nosniff - X-Frame-Options:
SAMEORIGIN - Content-Security-Policy: Restricts resource loading
Development vs Production
Development Configuration
app/config.py:112
Allows testing over HTTP without HTTPS setup.
Production Configuration
app/config.py:52
Testing Sessions
In test environments (app/config.py:143):
Platform Admin Session Timeout
Rationale
Platform administrators have elevated privileges, including:- Viewing all services
- Modifying service settings
- Accessing user data
- Managing platform configuration
- Security: Reduces risk of unauthorized access if admin leaves workstation unlocked
- Usability: 30 minutes is sufficient for most administrative tasks
- Absolute limit: 20-hour maximum still applies
Toggling Platform Admin View
Platform admins can temporarily disable their elevated view (app/main/views/your_account.py:261):
Session Storage
Flask Session Backend
By default, Flask sessions use signed cookies:- Session data is stored client-side in the cookie
- Signed with
SECRET_KEYto prevent tampering - Limited to ~4KB of data
- No server-side storage required
Redis Session Storage
While Redis is configured (REDIS_URL, REDIS_ENABLED), it is used for caching, not session storage. The application uses Flask’s default secure cookie sessions.
Redis configuration in app/config.py:87:
Troubleshooting
Session Expires Immediately
Symptoms: User is logged out immediately after login. Possible causes:SECRET_KEYchanged between requests- Cookie not being sent (check
SESSION_COOKIE_SECUREwith HTTP) - Browser blocking cookies
- System clock skew causing expiry timestamps to be in the past
Platform Admin Session Timeout Too Short
The 30-minute timeout is intentional. To extend:CSRF Token Invalid
Symptoms: Forms fail with “CSRF token invalid” error. Solutions:- Verify
SECRET_KEYis consistent across requests - Check session is not expiring between page load and form submission
- Ensure forms include the CSRF token:
{{ csrf_token() }}
Session Data Lost
Session data may be lost if:-
Session cookie too large (>4KB)
- Solution: Store less data in session, use database for persistent data
-
Concurrent requests on auto-refreshing pages
- Solution: Already handled by
should_set_cookieexcludingjson_updatesblueprint
- Solution: Already handled by
-
Redis connection failure (if using Redis for other features)
- Solution: Check Redis connectivity, session data is in cookies not Redis
Related Documentation
- Environment Variables - Session-related environment variables
- AWS Setup - AWS credentials for production deployments