Since PR odoo/odoo#78857 , the TOTP authentication support is broken when used inside either Android or iOS mobile apps. Due to our inability to update the iOS app (following review from Apple), this commit aims at restoring the bare minimum requirements to make the current mobile apps (specially iOS but also Android) authentication workflow works. As extended explanation: - Set-Cookie header is expected to be sent even when session_id hasn't changed (iOS specific). - Successful credentials check on `/web/session/authenticate` expect a successful response with a result containing `uid` set to `null` to mark the need of an additional totp handshake (both platforms). closes odoo/odoo#85463 Signed-off-by: Julien Castiaux <juc@odoo.com>
79 lines
3.3 KiB
Python
79 lines
3.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
import re
|
|
|
|
import odoo.addons.web.controllers.main
|
|
from odoo import http, _
|
|
from odoo.exceptions import AccessDenied
|
|
from odoo.http import request
|
|
|
|
TRUSTED_DEVICE_COOKIE = 'td_id'
|
|
TRUSTED_DEVICE_AGE = 90*86400 # 90 days expiration
|
|
|
|
|
|
class Home(odoo.addons.web.controllers.main.Home):
|
|
@http.route(
|
|
'/web/login/totp',
|
|
type='http', auth='public', methods=['GET', 'POST'], sitemap=False,
|
|
website=True, # website breaks the login layout...
|
|
)
|
|
def web_totp(self, redirect=None, **kwargs):
|
|
if request.session.uid:
|
|
return request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
|
|
|
|
if not request.session.pre_uid:
|
|
return request.redirect('/web/login')
|
|
|
|
error = None
|
|
|
|
user = request.env['res.users'].browse(request.session.pre_uid)
|
|
if user and request.httprequest.method == 'GET':
|
|
cookies = request.httprequest.cookies
|
|
key = cookies.get(TRUSTED_DEVICE_COOKIE)
|
|
if key:
|
|
checked_credentials = request.env['auth_totp.device']._check_credentials(scope="browser", key=key)
|
|
if checked_credentials == user.id:
|
|
request.session.finalize(request.env)
|
|
return request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
|
|
|
|
elif user and request.httprequest.method == 'POST' and kwargs.get('totp_token'):
|
|
try:
|
|
with user._assert_can_auth():
|
|
user._totp_check(int(re.sub(r'\s', '', kwargs['totp_token'])))
|
|
except AccessDenied as e:
|
|
error = str(e)
|
|
except ValueError:
|
|
error = _("Invalid authentication code format.")
|
|
else:
|
|
request.session.finalize(request.env)
|
|
request.update_env(user=request.session.uid)
|
|
request.update_context(**request.session.context)
|
|
response = request.redirect(self._login_redirect(request.session.uid, redirect=redirect))
|
|
if kwargs.get('remember'):
|
|
name = _("%(browser)s on %(platform)s",
|
|
browser=request.httprequest.user_agent.browser.capitalize(),
|
|
platform=request.httprequest.user_agent.platform.capitalize(),
|
|
)
|
|
geoip = request.session.geoip
|
|
if geoip:
|
|
name += " (%s, %s)" % (geoip['city'], geoip['country_name'])
|
|
|
|
key = request.env['auth_totp.device']._generate("browser", name)
|
|
response.set_cookie(
|
|
key=TRUSTED_DEVICE_COOKIE,
|
|
value=key,
|
|
max_age=TRUSTED_DEVICE_AGE,
|
|
httponly=True,
|
|
samesite='Lax'
|
|
)
|
|
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@)
|
|
request.session.should_touch = True
|
|
return response
|
|
|
|
# Crapy workaround for unupdatable Odoo Mobile App iOS (Thanks Apple :@)
|
|
request.session.should_touch = True
|
|
return request.render('auth_totp.auth_totp_form', {
|
|
'user': user,
|
|
'error': error,
|
|
'redirect': redirect,
|
|
})
|