Understanding of Session and CSRF Handling in Odoo
✅ Overview
When exposing Odoo to the web — especially for portal users, unauthenticated users, or public APIs — managing sessions and CSRF (Cross-Site Request Forgery) is critical for security and reliability.
📌 Key Concepts
Term | Meaning |
Session | A user’s persistent identity across HTTP requests in Odoo |
CSRF Token | A hidden token Odoo uses to prevent forged POST requests |
Portal | Public-facing access for partners or customers with limited permissions |
Public API | An endpoint exposed to external apps/systems without login |
🔄 Session Management in Portal
Odoo Automatically Uses Sessions in Web Controllers
When a portal user logs in via /web/login, a session is created and stored in a session cookie.
Cookie: session_id=abcdef12345
This allows Odoo to:
- Authenticate user requests
- Preserve state (cart, quote, etc.)
- Access request.env.user, request.session, etc.
🔧 Example: Accessing Portal Data via Session
from odoo import http
from odoo.http import request
class PortalController(http.Controller):
@http.route('/my/orders', auth='user', website=True)
def portal_orders(self):
partner = request.env.user.partner_id
orders = request.env['sale.order'].search([('partner_id', '=', partner.id)])
return request.render('my_module.portal_orders_template', {'orders': orders})
- Works because the portal user is authenticated through a session.
- auth='user' ensures only logged-in users can access it.
🛡 CSRF Protection in Odoo
By default, all routes with type='http' and auth='user' or auth='public' have CSRF protection enabled.
🔥 Problem Scenario (API Fails With 403 Error)
You call a public POST route like:
curl -X POST https://your-odoo.com/api/submit -d '{"data": "value"}'
If CSRF is enabled and you didn’t provide a CSRF token — Odoo will block the request.
🔧 Disabling CSRF for API Endpoints
For trusted external integrations, disable CSRF token requirement by adding:
@http.route('/api/submit', type='json', auth='public',
methods=['POST'], csrf=False)
def api_submit(self, **kwargs):
# Your logic here
- csrf=False disables the CSRF check (⚠️ use cautiously)
- Recommended only if:
- Auth is handled via token or header
- Endpoint is secured via HTTPS
🔐 Secure API Pattern (Without Session)
@http.route('/api/secure-data', type='json', auth='none', methods=['POST'], csrf=False)
def secure_api(self, **kwargs):
token = kwargs.get('token')
if token != 'expected-api-key':
return {'error': 'Unauthorized'}
# Your logic here
return {'data': 'OK'}
✅ Summary Table
Feature | Session-Based Portal | Stateless API Endpoint |
Auth Mechanism | session_id (cookie) | Token-based (in headers or body) |
CSRF Enabled by Default | ✅ Yes | ✅ Yes (disable with csrf=False) |
Use request.env.user | ✅ Yes | ❌ No (use sudo / token auth) |
Good For | Web views, My Account pages | Mobile apps, 3rd-party integrations |
auth='public' | Access without login | Useful for open endpoints |
auth='user' | Requires login | Portal or internal usage |
🧠 Best Practices
- Use csrf=False only for stateless API integrations with token security.
- Never expose session-specific logic over auth='public' without token-based checks.
- Always use HTTPS to protect session and token data.
- For portals, rely on session-based access to ensure login-required content is protected.