diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py
index 4a378b0174b..799618d34ef 100644
--- a/addons/auth_oauth/controllers/main.py
+++ b/addons/auth_oauth/controllers/main.py
@@ -1,10 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
+import base64
import functools
-import logging
-
import json
+import logging
+import os
import werkzeug.urls
import werkzeug.utils
@@ -65,6 +65,7 @@ class OAuthLogin(Home):
redirect_uri=return_url,
scope=provider['scope'],
state=json.dumps(state),
+ nonce=base64.urlsafe_b64encode(os.urandom(16)),
)
provider['auth_link'] = "%s?%s" % (provider['auth_endpoint'], werkzeug.urls.url_encode(params))
return providers
diff --git a/addons/auth_oauth/data/auth_oauth_data.xml b/addons/auth_oauth/data/auth_oauth_data.xml
index b6579cd82ae..5eab16dc318 100644
--- a/addons/auth_oauth/data/auth_oauth_data.xml
+++ b/addons/auth_oauth/data/auth_oauth_data.xml
@@ -6,7 +6,6 @@
https://accounts.odoo.com/oauth2/auth
userinfo
https://accounts.odoo.com/oauth2/tokeninfo
-
fa fa-fw o_custom_icon
Log in with Odoo.com
@@ -23,9 +22,8 @@
Google OAuth2
https://accounts.google.com/o/oauth2/auth
- https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
- https://www.googleapis.com/oauth2/v1/tokeninfo
- https://www.googleapis.com/oauth2/v1/userinfo
+ openid profile email
+ https://www.googleapis.com/oauth2/v3/userinfo
fa fa-fw fa-google
Log in with Google
diff --git a/addons/auth_oauth/i18n/auth_oauth.pot b/addons/auth_oauth/i18n/auth_oauth.pot
index f3d45984724..b8bc9db53aa 100644
--- a/addons/auth_oauth/i18n/auth_oauth.pot
+++ b/addons/auth_oauth/i18n/auth_oauth.pot
@@ -48,12 +48,7 @@ msgstr ""
#. module: auth_oauth
#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__auth_endpoint
-msgid "Authentication URL"
-msgstr ""
-
-#. module: auth_oauth
-#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__body
-msgid "Body"
+msgid "Authorization URL"
msgstr ""
#. module: auth_oauth
@@ -89,7 +84,7 @@ msgstr ""
#. module: auth_oauth
#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__data_endpoint
-msgid "Data URL"
+msgid "Data Endpoint"
msgstr ""
#. module: auth_oauth
@@ -127,11 +122,6 @@ msgstr ""
msgid "Last Updated on"
msgstr ""
-#. module: auth_oauth
-#: model:ir.model.fields,help:auth_oauth.field_auth_oauth_provider__body
-msgid "Link text in Login Dialog"
-msgstr ""
-
#. module: auth_oauth
#: model:auth.oauth.provider,body:auth_oauth.provider_facebook
msgid "Log in with Facebook"
@@ -147,6 +137,11 @@ msgstr ""
msgid "Log in with Odoo.com"
msgstr ""
+#. module: auth_oauth
+#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__body
+msgid "Login button label"
+msgstr ""
+
#. module: auth_oauth
#: model:ir.model.fields,field_description:auth_oauth.field_res_users__oauth_access_token
msgid "OAuth Access Token"
@@ -225,13 +220,13 @@ msgid "System Parameter"
msgstr ""
#. module: auth_oauth
-#: model:ir.model,name:auth_oauth.model_res_users
-msgid "Users"
+#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__validation_endpoint
+msgid "UserInfo URL"
msgstr ""
#. module: auth_oauth
-#: model:ir.model.fields,field_description:auth_oauth.field_auth_oauth_provider__validation_endpoint
-msgid "Validation URL"
+#: model:ir.model,name:auth_oauth.model_res_users
+msgid "Users"
msgstr ""
#. module: auth_oauth
diff --git a/addons/auth_oauth/models/auth_oauth.py b/addons/auth_oauth/models/auth_oauth.py
index cbee5ced816..6efb044b7ed 100644
--- a/addons/auth_oauth/models/auth_oauth.py
+++ b/addons/auth_oauth/models/auth_oauth.py
@@ -12,13 +12,13 @@ class AuthOAuthProvider(models.Model):
name = fields.Char(string='Provider name', required=True) # Name of the OAuth2 entity, Google, etc
client_id = fields.Char(string='Client ID') # Our identifier
- auth_endpoint = fields.Char(string='Authentication URL', required=True) # OAuth provider URL to authenticate users
- scope = fields.Char() # OAUth user data desired to access
- validation_endpoint = fields.Char(string='Validation URL', required=True) # OAuth provider URL to validate tokens
- data_endpoint = fields.Char(string='Data URL')
+ auth_endpoint = fields.Char(string='Authorization URL', required=True) # OAuth provider URL to authenticate users
+ scope = fields.Char(default='openid profile email') # OAUth user data desired to access
+ validation_endpoint = fields.Char(string='UserInfo URL', required=True) # OAuth provider URL to get user information
+ data_endpoint = fields.Char()
enabled = fields.Boolean(string='Allowed')
css_class = fields.Char(string='CSS class', default='fa fa-fw fa-sign-in text-primary')
- body = fields.Char(required=True, help='Link text in Login Dialog', translate=True)
+ body = fields.Char(required=True, string="Login button label", help='Link text in Login Dialog', translate=True)
sequence = fields.Integer(default=10)
def _neutralize(self):
diff --git a/addons/auth_oauth/models/res_users.py b/addons/auth_oauth/models/res_users.py
index 56f78b8f535..ecaa68d0530 100644
--- a/addons/auth_oauth/models/res_users.py
+++ b/addons/auth_oauth/models/res_users.py
@@ -4,6 +4,7 @@
import json
import requests
+import werkzeug.http
from odoo import api, fields, models
from odoo.exceptions import AccessDenied, UserError
@@ -23,9 +24,21 @@ class ResUsers(models.Model):
('uniq_users_oauth_provider_oauth_uid', 'unique(oauth_provider_id, oauth_uid)', 'OAuth UID must be unique per provider'),
]
- @api.model
def _auth_oauth_rpc(self, endpoint, access_token):
- return requests.get(endpoint, params={'access_token': access_token}).json()
+ if self.env['ir.config_parameter'].sudo().get_param('auth_oauth.authorization_header'):
+ response = requests.get(endpoint, headers={'Authorization': 'Bearer %s' % access_token}, timeout=10)
+ else:
+ response = requests.get(endpoint, params={'access_token': access_token}, timeout=10)
+
+ if response.ok: # nb: could be a successful failure
+ return response.json()
+
+ auth_challenge = werkzeug.http.parse_www_authenticate_header(
+ response.headers.get('WWW-Authenticate'))
+ if auth_challenge.type == 'bearer' and 'error' in auth_challenge:
+ return dict(auth_challenge)
+
+ return {'error': 'invalid_request'}
@api.model
def _auth_oauth_validate(self, provider, access_token):
@@ -37,11 +50,26 @@ class ResUsers(models.Model):
if oauth_provider.data_endpoint:
data = self._auth_oauth_rpc(oauth_provider.data_endpoint, access_token)
validation.update(data)
+ # unify subject key under standard (sub), pop all possible and get most sensible
+ subject = next(filter(None, [
+ validation.pop(key, None)
+ for key in [
+ 'sub', # standard
+ 'id', # google v1 userinfo, facebook opengraph
+ 'user_id', # google tokeninfo, odoo (tokeninfo)
+ ]
+ ]), None)
+ if not subject:
+ raise AccessDenied('Missing subject identity')
+ # also set on user_id for BC reasons, remove in master when the entire
+ # thing gets reworked
+ validation['sub'] = validation['user_id'] = subject
+
return validation
@api.model
def _generate_signup_values(self, provider, validation, params):
- oauth_uid = validation['user_id']
+ oauth_uid = validation['sub']
email = validation.get('email', 'provider_%s_user_%s' % (provider, oauth_uid))
name = validation.get('name', email)
return {
@@ -65,7 +93,7 @@ class ResUsers(models.Model):
This method can be overridden to add alternative signin methods.
"""
- oauth_uid = validation['user_id']
+ oauth_uid = validation['sub']
try:
oauth_user = self.search([("oauth_uid", "=", oauth_uid), ('oauth_provider_id', '=', provider)])
if not oauth_user:
@@ -94,13 +122,6 @@ class ResUsers(models.Model):
# continue with the process
access_token = params.get('access_token')
validation = self._auth_oauth_validate(provider, access_token)
- # required check
- if not validation.get('user_id'):
- # Workaround: facebook does not send 'user_id' in Open Graph Api
- if validation.get('id'):
- validation['user_id'] = validation['id']
- else:
- raise AccessDenied()
# retrieve and sign in user
login = self._auth_oauth_signin(provider, validation, params)