✅ 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
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
- OK for public models like countries, states, product categories
- Avoid sudo() on sensitive models (e.g., res.users, account.move)
🔥 Problem Scenario (API Fails With 403 Error)
If CSRF is enabled and you didn’t provide a CSRF token — Odoo will block the request.
🔧 Disabling CSRF for API Endpoints
@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.