I'm trying to implement a custom login mechanism in Odoo where users authenticate using an OTP (One-Time Password) sent to their email, instead of the traditional username/password method.
My goal:
- User enters their email address on the login screen.
- An OTP is generated and sent to their email.
- User enters the OTP, and if it’s valid and not expired, they are logged in.
What I’ve tried:
- Created a custom controller with
@http.route('/web/login/request_otp', ...)to handle OTP generation. - Stored OTPs in a new model (
res.users.otp) with expiry timestamp. - Used
mail.templateto send OTP to the user’s email. - Created another route to validate the OTP and authenticate using
request.session.authenticate(...).
Issue:
When I try to access /web/login/[email protected], I get a 404 Not Found error.
I'm not sure if:
- The route is properly registered.
- The controller is loaded correctly.
- The module was upgraded properly.
Environment:
- Odoo version: 16 (also trying on 17)
- Custom module created under
addons/ - SMTP is configured and working for other mail templates
My questions:
- What's the correct way to register a custom public HTTP route for login in Odoo?
- How to properly authenticate a user from a controller using OTP (without requiring password)?
- Any security concerns or best practices I should follow when implementing OTP login in Odoo?
Any help or working example would be appreciated 🙏
Route code:
from odoo import http
from odoo.http import request
class AuthOtpController(http.Controller):
@http.route('/web/login/verify_password', type='http',
auth='public', website=True, csrf=False)
def verify_password(self, **kwargs):
# Logic for verifying email and password, # generating OTP, storing it, and sending it via email.
@http.route('/web/login/confirm_otp', type='http',
auth='public', website=True, csrf=False)
def confirm_otp(self, **kwargs):
# Logic for verifying the OTP and authenticating the user.
@http.route('/web/login/request_otp', type='http', auth='public', website=True, csrf=False)
def request_otp(self, **kwargs):
email = kwargs.get('email')
# Logic for generating OTP and sending it via email return "OTP sent"