From a169182522c4b07395f4027e9a0cadec151d4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Schenkels?= Date: Fri, 5 Oct 2018 15:43:47 +0200 Subject: [PATCH 01/46] [ADD] auth_oidc module --- auth_oidc/__init__.py | 6 +++ auth_oidc/__manifest__.py | 25 +++++++++ auth_oidc/controllers/__init__.py | 6 +++ auth_oidc/controllers/main.py | 25 +++++++++ auth_oidc/models/__init__.py | 6 +++ auth_oidc/models/auth_oauth_provider.py | 64 ++++++++++++++++++++++++ auth_oidc/models/res_users.py | 25 +++++++++ auth_oidc/static/description/icon.png | Bin 0 -> 2780 bytes auth_oidc/views/auth_oauth_provider.xml | 14 ++++++ 9 files changed, 171 insertions(+) create mode 100644 auth_oidc/__init__.py create mode 100644 auth_oidc/__manifest__.py create mode 100644 auth_oidc/controllers/__init__.py create mode 100644 auth_oidc/controllers/main.py create mode 100644 auth_oidc/models/__init__.py create mode 100644 auth_oidc/models/auth_oauth_provider.py create mode 100644 auth_oidc/models/res_users.py create mode 100755 auth_oidc/static/description/icon.png create mode 100644 auth_oidc/views/auth_oauth_provider.xml diff --git a/auth_oidc/__init__.py b/auth_oidc/__init__.py new file mode 100644 index 0000000000..000324e3e2 --- /dev/null +++ b/auth_oidc/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from . import controllers +from . import models diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py new file mode 100644 index 0000000000..bb96a321f1 --- /dev/null +++ b/auth_oidc/__manifest__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + 'name': 'Authentication OpenID Connect', + 'version': '10.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'ICTSTUDIO, André Schenkels', + 'website': 'https://www.ictstudio.eu', + 'description': """ +Allow users to login through OpenID Connect Provider. +===================================================== +- Keycloak with ClientID and Secret + Implicit Flow + +""", + 'external_dependencies': {'python': [ + 'jose', + 'cryptography' + ]}, + 'depends': ['auth_oauth'], + 'data': [ + 'views/auth_oauth_provider.xml', + ], +} diff --git a/auth_oidc/controllers/__init__.py b/auth_oidc/controllers/__init__.py new file mode 100644 index 0000000000..832d7f07a1 --- /dev/null +++ b/auth_oidc/controllers/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import main + diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py new file mode 100644 index 0000000000..7e99f1e930 --- /dev/null +++ b/auth_oidc/controllers/main.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import werkzeug.utils +import uuid + +from odoo.addons.auth_oauth.controllers.main import OAuthLogin + + +class OpenIDLogin(OAuthLogin): + + def list_providers(self): + providers = super(OpenIDLogin, self).list_providers() + for provider in providers: + if provider.get('flow') == 'id_token': + provider['nonce'] = uuid.uuid1().hex + params = werkzeug.url_decode(provider['auth_link'].split('?')[-1]) + params.pop('response_type') + params.update(dict(response_type='id_token', + nonce=provider['nonce'])) + if provider.get('scope'): + params['scope'] = provider['scope'] + provider['auth_link'] = "%s?%s" % (provider['auth_endpoint'], werkzeug.url_encode(params)) + return providers diff --git a/auth_oidc/models/__init__.py b/auth_oidc/models/__init__.py new file mode 100644 index 0000000000..37e1a690a6 --- /dev/null +++ b/auth_oidc/models/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from . import auth_oauth_provider +from . import res_users diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py new file mode 100644 index 0000000000..a593736b9a --- /dev/null +++ b/auth_oidc/models/auth_oauth_provider.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models, fields, api +from jose import jwt +import urllib2 +import json + + +class AuthOauthProvider(models.Model): + _inherit = 'auth.oauth.provider' + + flow = fields.Selection([ + ('access_token', 'OAuth2'), + ('id_token', 'OpenID Connect') + ], + string='Auth Flow', + required=True, + default='access_token') + + token_map = fields.Char( + help="Some Oauth providers don't map keys in their responses " + "exactly as required. It is important to ensure user_id and " + "email at least are mapped. For OpenID Connect user_id is " + "the sub key in the standard.") + + validation_endpoint = fields.Char( + help='For OpenID Connect this should be the location for public keys ') + + @api.model + def _get_key(self, header): + if self.flow != 'id_token': + return False + f = urllib2.urlopen(self.validation_endpoint) + response = json.loads(f.read()) + rsa_key = {} + for key in response["keys"]: + if key["kid"] == header.get('kid'): + rsa_key = key + return rsa_key + + @api.model + def map_token_values(self, res): + if self.token_map: + for pair in self.token_map.split(' '): + from_key, to_key = pair.split(':') + if to_key not in res: + res[to_key] = res.get(from_key, '') + return res + + @api.multi + def _parse_id_token(self, id_token): + self.ensure_one() + res = {} + header = jwt.get_unverified_header(id_token) + res.update(jwt.decode( + id_token, + self._get_key(header), + algorithms=['RS256'], + audience=self.client_id)) + + res.update(self.map_token_values(res)) + return res \ No newline at end of file diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py new file mode 100644 index 0000000000..1c67ab2d43 --- /dev/null +++ b/auth_oidc/models/res_users.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright© 2016 ICTSTUDIO +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models, api + + +class ResUsers(models.Model): + _inherit = 'res.users' + + @api.model + def _auth_oauth_validate(self, provider, access_token): + """ return the validation data corresponding to the access token """ + oauth_provider = self.env['auth.oauth.provider'].browse(provider) + if oauth_provider.flow == 'id_token': + return oauth_provider._parse_id_token(access_token) + else: + return super(ResUsers, self)._auth_oauth_validate() + + @api.model + def auth_oauth(self, provider, params): + id_token = params.get('id_token') + if id_token: + params['access_token'] = id_token + return super(ResUsers, self).auth_oauth(provider, params) diff --git a/auth_oidc/static/description/icon.png b/auth_oidc/static/description/icon.png new file mode 100755 index 0000000000000000000000000000000000000000..06a05ebd480fdd359c2600a27660aced7a5a48a0 GIT binary patch literal 2780 zcma)8_gfN-7Dhxcw`q!d?}`e^kpow1&aAX7Z>S}1Gc_lPBhy4$_&E?q!;xw3G?Srz zWrZVirKmX)Ck`xy9M|vu1K)klc%SDz=lt?M=Z81L)!9~9Kvn<%00`ULS-Tw|_MhSh zAM_40?=J@c_HnSa2JHV6#cgFz512!@?5;!{TnztHJeJzTm;(|LY42nM`2hh59zJ#T zh5o@zAi&<*;$qC`3TgEr!Q;qB3KKZ?B16;b0ZKrQG2}ygCh2bJjkbw)$nW#SW!NFq z0~7x43FeB<3;J$e%A;9N`Uie9$Zt45CjI$n0Q!#3*QiS2ORrD%1s-{c{7U`HixHo& z&M1QSwzrG0>GknuVW=XK(jNx9jaeAM!m#*27GnRJsn;y8U{1m#h-9sn^Qr(L@c5<^ z9G>2_8$XuSkB6Ns|GP4zscHcS@l6m(R7&sky1Dwz9@ih zY%fIL3SY!*<+mwOCMlWPTyulwFLz>rGC-+$t_-#gJb;TXtDX+(-4}rEuB=k&j`I;o zNA%V%_ZlZeyYR>jzXk#Pt4auUO8lCE4k?Wh{FmPY`($58>cb_)3wKwR47ZnLZ>{8q zuY+p9gmZVBLV9o=DZJWJOk94zN~!h`Wsb0fb8Adf`!#&6ro0i)rxt)>#$(Tg z0AGIfoSk5Xf{PHTZ}dckQO${Z>4X?;&*G1Q(WRAN=f>A#cWOgNFn0BwtR0R7TKN+ z(jL})?+nc>b*}MG1NaH6-i*6z%K9`^C4u9I{w(2<6vUwX3SyR&MmP*XDjk6}oc|%O zFJ59Y@^G43_N=nR-2V~@nTEXz1*c2m2b>yulQPotKX5;*wVpp3>>y;Hs<5<5+s-i; zjjZxtJFFWQ6c9;|PgXvm4!E%>63Yg)NCCu+)GjVcXuDsx7Ybe6t7f)N^d2vFm;>#Y z(0YDtp>{rL9Tsoy?Mn)M;@vI+q08zDL3y7ub);O4SbNoO%s8^}bOEhH1)wde89Mm~ z_<*t9(Pj!wyscD|(83YAN5~abdybHoA?kZgW)>^hsEQ*|CthA;u;s3!Y=geBGkGzk z(YLJt1ZWmudD-j!`kMrv`9%Q%WVh94p9+atV}2ZvoVH^cH?G!tl0j)yshJrVd+YJA zC>GLM7_X8CZ|*@);*FC)N$hTR2R00ELNU?DTD3N%DusKkj8&d7ualaA{El@IlfnW2 z5MCh?5_h(;@_a)VK?-_U_gPMMiTbEk-?5f?(pGiHc9ItM4C}<|P4g;wyV97=n+MV) zQ8(%fi=X=fPF<+|lq|CHVW;1}tp(Ll0PWhG+UNtM35vE9bp(tPGvJCWugr7a_qPCa z+WeHMWre5J%hTSt`2 z(n*2+R76n1i)PP!+rml4OjwK8g$ZU_ind|^InS*zbVvf!t7RtypqRnLcx5~eKEF8f zwPGtL&5m*;cFz({@F@}fNoyBQ(DR+WJbp~qzB(P?Sm;8ojqE?FHDM!8iv)tJEtIHtw zwW`~3O>I#rdOcVmmy`uh&6C+dCAfi5@xqQRzld*0jyk{9+x+5tJ%yyX*8;N3z3Vhm z7To>tnw77$>c7)`mD*sjoAgr5=d#4fxZQ`U*b}mQYzhqEQQEH-;I|XAimoO;MV}nc z=X%YZj|6N_6yDW$y8Jy6cHNm}q!ARpuUy2XRd8rn5*A2yZ;uhkCfF?V*yzQJklRQ44;b%Fe9TQ{ASmfbp^ zyxr?%1k;f}_Tdq-bmO>&*W;Nsr?Pl<2FmRr63FKT?c`Ulm~Sf07(YWQQ@w2p>$G8O#*uSyQZ!KP`3_{qTyjQ6qfRUnfeU zhXHN6GO#BzGYes7b`{;JQlp7&B4v(`J86?vl$;otDbe*M#_m+R4wev~qQky}&>jMX$k?JLfd& z;Yg|I>h$QIMq1WwjtqTR(@Xf@%%#Vq`ed;dw6W^wIH75#28yU2cUfl!)CEWg{ z8_lRP?d*?x_c9x-+b;b!*ipnU(3q16DEw7@A;KB=kh=ZOoBj>yA^kh-`|N#0tJm?0 zmyI^Kkf(!6%@S&&q55Y<5-U?odAlGNS~5T@I=)lur>I?XPM3FSXR;v0^Wx#3i+Af~ z_l-ieljFFyKQdR3vD&|Qjpt$(D-C32b3V`>%eS2SU{2Z7C}**j8Xe85LZi>LRLY?# zwUimvY{x$OvO)BaJ@iKE+5ikcwI)}FT?n8sLu;yz8lp8ys*mG>CsNDIx4v`09~-v$ ziqS?glZfrgwCA>37 z2p{2$>2;0Y&j@dS+EtyyI+h2;+94F4N#-Q%Ua_3I^*Bd`dL*`Ly-M47zAKlmmn6v- z9j>AtnG<|1Pzy1UwF=7!L28m0kg&gMA=seYS^sV2W5M$}gw~_fbOdAd5#nDR9H`{p aj#6WMOub(s7;@l40rob|*437% + + + auth.oidc.provider.form + auth.oauth.provider + + + + + + + + + From 25c81d44279fa4e0324f18537df62c14307c7801 Mon Sep 17 00:00:00 2001 From: Alexandre Fayolle Date: Thu, 19 Nov 2020 18:15:08 +0100 Subject: [PATCH 02/46] Adapt to OCA standards update manifest, add README, update requirements.txt --- auth_oidc/README.rst | 57 +++++++++++++++++++++++++ auth_oidc/__init__.py | 2 +- auth_oidc/__manifest__.py | 13 ++---- auth_oidc/controllers/__init__.py | 5 +-- auth_oidc/controllers/main.py | 13 ++++-- auth_oidc/models/__init__.py | 2 +- auth_oidc/models/auth_oauth_provider.py | 11 +++-- auth_oidc/models/res_users.py | 4 +- auth_oidc/readme/CONFIGURE.rst | 28 ++++++++++++ auth_oidc/readme/CONTRIBUTORS.rst | 1 + auth_oidc/readme/DESCRIPTION.rst | 6 +++ auth_oidc/readme/HISTORY.rst | 4 ++ auth_oidc/readme/ROADMAP.rst | 3 ++ auth_oidc/readme/USAGE.rst | 1 + 14 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 auth_oidc/README.rst create mode 100644 auth_oidc/readme/CONFIGURE.rst create mode 100644 auth_oidc/readme/CONTRIBUTORS.rst create mode 100644 auth_oidc/readme/DESCRIPTION.rst create mode 100644 auth_oidc/readme/HISTORY.rst create mode 100644 auth_oidc/readme/ROADMAP.rst create mode 100644 auth_oidc/readme/USAGE.rst diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst new file mode 100644 index 0000000000..2bcb185e11 --- /dev/null +++ b/auth_oidc/README.rst @@ -0,0 +1,57 @@ +================================= +Authentication via OpenID Connect +================================= + +This module allows users to login through an OpenID Connect provider. + +This includes: + +- Keycloak with ClientID and secret + Implicit Flow +- Microsoft Azure + + +Usage +===== + +Setup for Microsoft Azure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +# configure a new web application in Azure with OpenID and implicit flow (see + the `provider documentation + `_) +# in this application the redirect url must be be "/auth_oauth/signin" and of course this URL should be reachable from + Azure +# create a new authentication provider in Odoo with the following + parameters (see the `portal documentation + `_ + for more information): + +* Provider Name: Azure +* Auth Flow: OpenID Connect +* Client ID: use the value of the OAuth2 autorization endoing (v2) from the Azure Endpoints list +* Body: Azure SSO +* Authentication URL: use the value of "OAuth2 autorization endpoint (v2)" from the Azure endpoints list +* Scope: openid email +* Validation URL: use the value of "OAuth2 token endpoint (v2)" from the Azure endpoints list +* Allowed: yes + + +Setup for Keycloak +~~~~~~~~~~~~~~~~~~ + +write me... + + +Credits +======= + +Authors +~~~~~~~ + +* ICTSTUDIO, André Schenkels + +Contributors +~~~~~~~~~~~~ + +* Alexandre Fayolle diff --git a/auth_oidc/__init__.py b/auth_oidc/__init__.py index 000324e3e2..19700dee41 100644 --- a/auth_oidc/__init__.py +++ b/auth_oidc/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from . import controllers diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index bb96a321f1..98e4966a01 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -1,19 +1,14 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { 'name': 'Authentication OpenID Connect', 'version': '10.0.1.0.0', 'license': 'AGPL-3', - 'author': 'ICTSTUDIO, André Schenkels', - 'website': 'https://www.ictstudio.eu', - 'description': """ -Allow users to login through OpenID Connect Provider. -===================================================== -- Keycloak with ClientID and Secret + Implicit Flow - -""", + 'author': 'ICTSTUDIO, André Schenkels, Odoo Community Association (OCA)', + 'website': 'https://github.com/OCA/server-auth', + 'summary': "Allow users to login through OpenID Connect Provider", 'external_dependencies': {'python': [ 'jose', 'cryptography' diff --git a/auth_oidc/controllers/__init__.py b/auth_oidc/controllers/__init__.py index 832d7f07a1..ed684a50f9 100644 --- a/auth_oidc/controllers/__init__.py +++ b/auth_oidc/controllers/__init__.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import main - +from . import main diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index 7e99f1e930..135ea9a005 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import werkzeug.utils @@ -15,11 +15,18 @@ def list_providers(self): for provider in providers: if provider.get('flow') == 'id_token': provider['nonce'] = uuid.uuid1().hex - params = werkzeug.url_decode(provider['auth_link'].split('?')[-1]) + params = werkzeug.url_decode( + provider['auth_link'].split('?')[-1] + ) params.pop('response_type') params.update(dict(response_type='id_token', nonce=provider['nonce'])) if provider.get('scope'): params['scope'] = provider['scope'] - provider['auth_link'] = "%s?%s" % (provider['auth_endpoint'], werkzeug.url_encode(params)) + provider['auth_link'] = ( + "%s?%s" % ( + provider['auth_endpoint'], + werkzeug.url_encode(params) + ) + ) return providers diff --git a/auth_oidc/models/__init__.py b/auth_oidc/models/__init__.py index 37e1a690a6..b32fa1a4d3 100644 --- a/auth_oidc/models/__init__.py +++ b/auth_oidc/models/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from . import auth_oauth_provider diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index a593736b9a..7d2a0040f0 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -1,11 +1,16 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import models, fields, api -from jose import jwt import urllib2 import json +import logging + +try: + from jose import jwt +except ImportError: + logging.getLogger(__name__).debug('jose library not installed') class AuthOauthProvider(models.Model): @@ -61,4 +66,4 @@ def _parse_id_token(self, id_token): audience=self.client_id)) res.update(self.map_token_values(res)) - return res \ No newline at end of file + return res diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index 1c67ab2d43..7ebbce2446 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright© 2016 ICTSTUDIO +# Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import models, api @@ -16,7 +16,7 @@ def _auth_oauth_validate(self, provider, access_token): return oauth_provider._parse_id_token(access_token) else: return super(ResUsers, self)._auth_oauth_validate() - + @api.model def auth_oauth(self, provider, params): id_token = params.get('id_token') diff --git a/auth_oidc/readme/CONFIGURE.rst b/auth_oidc/readme/CONFIGURE.rst new file mode 100644 index 0000000000..f2ac933218 --- /dev/null +++ b/auth_oidc/readme/CONFIGURE.rst @@ -0,0 +1,28 @@ +Setup for Microsoft Azure +~~~~~~~~~~~~~~~~~~~~~~~~~ + +# configure a new web application in Azure with OpenID and implicit flow (see + the `provider documentation + `_) +# in this application the redirect url must be be "/auth_oauth/signin" and of course this URL should be reachable from + Azure +# create a new authentication provider in Odoo with the following + parameters (see the `portal documentation + `_ + for more information): + +* Provider Name: Azure +* Auth Flow: OpenID Connect +* Client ID: use the value of the OAuth2 autorization endoing (v2) from the Azure Endpoints list +* Body: Azure SSO +* Authentication URL: use the value of "OAuth2 autorization endpoint (v2)" from the Azure endpoints list +* Scope: openid email +* Validation URL: use the value of "OAuth2 token endpoint (v2)" from the Azure endpoints list +* Allowed: yes + + +Setup for Keycloak +~~~~~~~~~~~~~~~~~~ + +write me... diff --git a/auth_oidc/readme/CONTRIBUTORS.rst b/auth_oidc/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..8785f72c06 --- /dev/null +++ b/auth_oidc/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Alexandre Fayolle diff --git a/auth_oidc/readme/DESCRIPTION.rst b/auth_oidc/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..540c7c2c95 --- /dev/null +++ b/auth_oidc/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module allows users to login through an OpenID Connect provider. + +This includes: + +- Keycloak with ClientID and secret + Implicit Flow +- Microsoft Azure diff --git a/auth_oidc/readme/HISTORY.rst b/auth_oidc/readme/HISTORY.rst new file mode 100644 index 0000000000..5aee5128a7 --- /dev/null +++ b/auth_oidc/readme/HISTORY.rst @@ -0,0 +1,4 @@ +10.0.1.0.0 2018-10-05 +~~~~~~~~~~~~~~~~~~~~~ + +* Initial implementation diff --git a/auth_oidc/readme/ROADMAP.rst b/auth_oidc/readme/ROADMAP.rst new file mode 100644 index 0000000000..c99e29f2c9 --- /dev/null +++ b/auth_oidc/readme/ROADMAP.rst @@ -0,0 +1,3 @@ + +* When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link +* When doing a logout an extra option to also logout at the SSO provider. diff --git a/auth_oidc/readme/USAGE.rst b/auth_oidc/readme/USAGE.rst new file mode 100644 index 0000000000..bb66969269 --- /dev/null +++ b/auth_oidc/readme/USAGE.rst @@ -0,0 +1 @@ +On the login page, click on the authentication provider you configured. From 4d337ff91810530b70750802308dea66665c2c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Wed, 24 Mar 2021 19:16:25 +0100 Subject: [PATCH 03/46] auth_oidc: improve docs Add some install instructions and configuration instructions for keycloak. --- auth_oidc/readme/CONFIGURE.rst | 20 +++++++++++++++++++- auth_oidc/readme/INSTALL.rst | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 auth_oidc/readme/INSTALL.rst diff --git a/auth_oidc/readme/CONFIGURE.rst b/auth_oidc/readme/CONFIGURE.rst index f2ac933218..c2ea288891 100644 --- a/auth_oidc/readme/CONFIGURE.rst +++ b/auth_oidc/readme/CONFIGURE.rst @@ -25,4 +25,22 @@ Setup for Microsoft Azure Setup for Keycloak ~~~~~~~~~~~~~~~~~~ -write me... +In Keycloak: + +# configure a new Client +# make sure Implicit Flow is Enabled. +# configure the redirect url to be "/auth_oauth/signin" + +In Odoo, create a new Oauth Provider with the following parameters: + +* Provider name: Keycloak (or any name you like that identify your keycloak +provider) +* Auth Flow: OpenID Connect +* Client ID: the same Client ID you entered when configuring the client in Keycloak +* Allowed: yes +* Body: the link text to appear on the login page, such as Login with Keycloak +* Authentication URL: The "authorization_endpoint" URL found in the + OpenID Endpoint Configuration of your Keycloak realm +* Scope: email +* Validation URL: The "jwks_uri" URL found in the + OpenID Endpoint Configuration of your Keycloak realm diff --git a/auth_oidc/readme/INSTALL.rst b/auth_oidc/readme/INSTALL.rst new file mode 100644 index 0000000000..bccbbbad79 --- /dev/null +++ b/auth_oidc/readme/INSTALL.rst @@ -0,0 +1,2 @@ +This module depends on the `python-jose `__ +library, not to be confused with ``jose`` which is also available on PyPI. From 876a8a856c689cb0e790e5a5f60a5f5383f500af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 9 Apr 2021 16:07:29 +0200 Subject: [PATCH 04/46] [MIG] auth_oidc: update dotfiles --- auth_oidc/__init__.py | 1 - auth_oidc/__manifest__.py | 24 +++++------ auth_oidc/controllers/__init__.py | 1 - auth_oidc/controllers/main.py | 29 +++++-------- auth_oidc/models/__init__.py | 1 - auth_oidc/models/auth_oauth_provider.py | 54 +++++++++++++------------ auth_oidc/models/res_users.py | 13 +++--- auth_oidc/readme/CONFIGURE.rst | 2 +- auth_oidc/readme/USAGE.rst | 2 +- auth_oidc/views/auth_oauth_provider.xml | 9 +++-- 10 files changed, 63 insertions(+), 73 deletions(-) diff --git a/auth_oidc/__init__.py b/auth_oidc/__init__.py index 19700dee41..8b36099f76 100644 --- a/auth_oidc/__init__.py +++ b/auth_oidc/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index 98e4966a01..e96ca28480 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -1,20 +1,14 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { - 'name': 'Authentication OpenID Connect', - 'version': '10.0.1.0.0', - 'license': 'AGPL-3', - 'author': 'ICTSTUDIO, André Schenkels, Odoo Community Association (OCA)', - 'website': 'https://github.com/OCA/server-auth', - 'summary': "Allow users to login through OpenID Connect Provider", - 'external_dependencies': {'python': [ - 'jose', - 'cryptography' - ]}, - 'depends': ['auth_oauth'], - 'data': [ - 'views/auth_oauth_provider.xml', - ], + "name": "Authentication OpenID Connect", + "version": "13.0.1.0.0", + "license": "AGPL-3", + "author": "ICTSTUDIO, André Schenkels, Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-auth", + "summary": "Allow users to login through OpenID Connect Provider", + "external_dependencies": {"python": ["jose", "cryptography"]}, + "depends": ["auth_oauth"], + "data": ["views/auth_oauth_provider.xml"], } diff --git a/auth_oidc/controllers/__init__.py b/auth_oidc/controllers/__init__.py index ed684a50f9..eaa83817e9 100644 --- a/auth_oidc/controllers/__init__.py +++ b/auth_oidc/controllers/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index 135ea9a005..466a09c89a 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -1,32 +1,25 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import werkzeug.utils import uuid +import werkzeug.utils + from odoo.addons.auth_oauth.controllers.main import OAuthLogin class OpenIDLogin(OAuthLogin): - def list_providers(self): providers = super(OpenIDLogin, self).list_providers() for provider in providers: - if provider.get('flow') == 'id_token': - provider['nonce'] = uuid.uuid1().hex - params = werkzeug.url_decode( - provider['auth_link'].split('?')[-1] - ) - params.pop('response_type') - params.update(dict(response_type='id_token', - nonce=provider['nonce'])) - if provider.get('scope'): - params['scope'] = provider['scope'] - provider['auth_link'] = ( - "%s?%s" % ( - provider['auth_endpoint'], - werkzeug.url_encode(params) - ) + if provider.get("flow") == "id_token": + provider["nonce"] = uuid.uuid1().hex + params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) + params.pop("response_type") + params.update(dict(response_type="id_token", nonce=provider["nonce"])) + if provider.get("scope"): + params["scope"] = provider["scope"] + provider["auth_link"] = "{}?{}".format( + provider["auth_endpoint"], werkzeug.url_encode(params) ) return providers diff --git a/auth_oidc/models/__init__.py b/auth_oidc/models/__init__.py index b32fa1a4d3..ebc8e5bfda 100644 --- a/auth_oidc/models/__init__.py +++ b/auth_oidc/models/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 7d2a0040f0..5ca62b58d6 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -1,57 +1,58 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models, fields, api -import urllib2 import json import logging +import urllib2 + +from odoo import api, fields, models try: from jose import jwt except ImportError: - logging.getLogger(__name__).debug('jose library not installed') + logging.getLogger(__name__).debug("jose library not installed") class AuthOauthProvider(models.Model): - _inherit = 'auth.oauth.provider' + _inherit = "auth.oauth.provider" - flow = fields.Selection([ - ('access_token', 'OAuth2'), - ('id_token', 'OpenID Connect') - ], - string='Auth Flow', + flow = fields.Selection( + [("access_token", "OAuth2"), ("id_token", "OpenID Connect")], + string="Auth Flow", required=True, - default='access_token') + default="access_token", + ) token_map = fields.Char( help="Some Oauth providers don't map keys in their responses " - "exactly as required. It is important to ensure user_id and " - "email at least are mapped. For OpenID Connect user_id is " - "the sub key in the standard.") + "exactly as required. It is important to ensure user_id and " + "email at least are mapped. For OpenID Connect user_id is " + "the sub key in the standard." + ) validation_endpoint = fields.Char( - help='For OpenID Connect this should be the location for public keys ') + help="For OpenID Connect this should be the location for public keys " + ) @api.model def _get_key(self, header): - if self.flow != 'id_token': + if self.flow != "id_token": return False f = urllib2.urlopen(self.validation_endpoint) response = json.loads(f.read()) rsa_key = {} for key in response["keys"]: - if key["kid"] == header.get('kid'): + if key["kid"] == header.get("kid"): rsa_key = key return rsa_key @api.model def map_token_values(self, res): if self.token_map: - for pair in self.token_map.split(' '): - from_key, to_key = pair.split(':') + for pair in self.token_map.split(" "): + from_key, to_key = pair.split(":") if to_key not in res: - res[to_key] = res.get(from_key, '') + res[to_key] = res.get(from_key, "") return res @api.multi @@ -59,11 +60,14 @@ def _parse_id_token(self, id_token): self.ensure_one() res = {} header = jwt.get_unverified_header(id_token) - res.update(jwt.decode( - id_token, - self._get_key(header), - algorithms=['RS256'], - audience=self.client_id)) + res.update( + jwt.decode( + id_token, + self._get_key(header), + algorithms=["RS256"], + audience=self.client_id, + ) + ) res.update(self.map_token_values(res)) return res diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index 7ebbce2446..c72bcd2af8 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -1,25 +1,24 @@ -# -*- coding: utf-8 -*- # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models, api +from odoo import api, models class ResUsers(models.Model): - _inherit = 'res.users' + _inherit = "res.users" @api.model def _auth_oauth_validate(self, provider, access_token): """ return the validation data corresponding to the access token """ - oauth_provider = self.env['auth.oauth.provider'].browse(provider) - if oauth_provider.flow == 'id_token': + oauth_provider = self.env["auth.oauth.provider"].browse(provider) + if oauth_provider.flow == "id_token": return oauth_provider._parse_id_token(access_token) else: return super(ResUsers, self)._auth_oauth_validate() @api.model def auth_oauth(self, provider, params): - id_token = params.get('id_token') + id_token = params.get("id_token") if id_token: - params['access_token'] = id_token + params["access_token"] = id_token return super(ResUsers, self).auth_oauth(provider, params) diff --git a/auth_oidc/readme/CONFIGURE.rst b/auth_oidc/readme/CONFIGURE.rst index c2ea288891..4993f4aea6 100644 --- a/auth_oidc/readme/CONFIGURE.rst +++ b/auth_oidc/readme/CONFIGURE.rst @@ -34,7 +34,7 @@ In Keycloak: In Odoo, create a new Oauth Provider with the following parameters: * Provider name: Keycloak (or any name you like that identify your keycloak -provider) + provider) * Auth Flow: OpenID Connect * Client ID: the same Client ID you entered when configuring the client in Keycloak * Allowed: yes diff --git a/auth_oidc/readme/USAGE.rst b/auth_oidc/readme/USAGE.rst index bb66969269..0fa74256b4 100644 --- a/auth_oidc/readme/USAGE.rst +++ b/auth_oidc/readme/USAGE.rst @@ -1 +1 @@ -On the login page, click on the authentication provider you configured. +On the login page, click on the authentication provider you configured. diff --git a/auth_oidc/views/auth_oauth_provider.xml b/auth_oidc/views/auth_oauth_provider.xml index 0cfe735814..bf7b20ddc4 100644 --- a/auth_oidc/views/auth_oauth_provider.xml +++ b/auth_oidc/views/auth_oauth_provider.xml @@ -1,4 +1,4 @@ - + auth.oidc.provider.form @@ -6,8 +6,11 @@ - - + + From 0288419a9142fd6cc71f10b0c3900ed19df6cd96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 9 Apr 2021 17:56:49 +0200 Subject: [PATCH 05/46] [MIG] auth_oidc from 10.0 --- auth_oidc/__manifest__.py | 2 +- auth_oidc/models/auth_oauth_provider.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index e96ca28480..78b15bf229 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -8,7 +8,7 @@ "author": "ICTSTUDIO, André Schenkels, Odoo Community Association (OCA)", "website": "https://github.com/OCA/server-auth", "summary": "Allow users to login through OpenID Connect Provider", - "external_dependencies": {"python": ["jose", "cryptography"]}, + "external_dependencies": {"python": ["python-jose"]}, "depends": ["auth_oauth"], "data": ["views/auth_oauth_provider.xml"], } diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 5ca62b58d6..93cf1820a5 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -1,9 +1,9 @@ # Copyright 2016 ICTSTUDIO # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import json import logging -import urllib2 + +import requests from odoo import api, fields, models @@ -38,8 +38,9 @@ class AuthOauthProvider(models.Model): def _get_key(self, header): if self.flow != "id_token": return False - f = urllib2.urlopen(self.validation_endpoint) - response = json.loads(f.read()) + r = requests.get(self.validation_endpoint) + r.raise_for_status() + response = r.json() rsa_key = {} for key in response["keys"]: if key["kid"] == header.get("kid"): @@ -47,7 +48,7 @@ def _get_key(self, header): return rsa_key @api.model - def map_token_values(self, res): + def _map_token_values(self, res): if self.token_map: for pair in self.token_map.split(" "): from_key, to_key = pair.split(":") @@ -55,7 +56,6 @@ def map_token_values(self, res): res[to_key] = res.get(from_key, "") return res - @api.multi def _parse_id_token(self, id_token): self.ensure_one() res = {} @@ -69,5 +69,5 @@ def _parse_id_token(self, id_token): ) ) - res.update(self.map_token_values(res)) + res.update(self._map_token_values(res)) return res From de790553349c9710aeee6b14fa632ac91932bec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 9 Apr 2021 20:03:21 +0200 Subject: [PATCH 06/46] auth_oidc: simplify and use "id_token token" Avoid replacing the access token by the id token. This may cause confusion. Copy a little piece of code from auth_oauth() method, to make the code easier to follow, and prepare for implementing the authorization code flow. --- auth_oidc/controllers/main.py | 7 ++++-- auth_oidc/models/auth_oauth_provider.py | 6 +++-- auth_oidc/models/res_users.py | 29 +++++++++++++++---------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index 466a09c89a..378b59c1af 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -1,4 +1,5 @@ # Copyright 2016 ICTSTUDIO +# Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import uuid @@ -13,10 +14,12 @@ def list_providers(self): providers = super(OpenIDLogin, self).list_providers() for provider in providers: if provider.get("flow") == "id_token": - provider["nonce"] = uuid.uuid1().hex + provider["nonce"] = uuid.uuid1().hex # TODO Better nonce params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) params.pop("response_type") - params.update(dict(response_type="id_token", nonce=provider["nonce"])) + params.update( + dict(response_type="id_token token", nonce=provider["nonce"]) + ) if provider.get("scope"): params["scope"] = provider["scope"] provider["auth_link"] = "{}?{}".format( diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 93cf1820a5..875600c6a8 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -1,4 +1,5 @@ # Copyright 2016 ICTSTUDIO +# Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import logging @@ -17,7 +18,7 @@ class AuthOauthProvider(models.Model): _inherit = "auth.oauth.provider" flow = fields.Selection( - [("access_token", "OAuth2"), ("id_token", "OpenID Connect")], + [("access_token", "OAuth2"), ("id_token", "OpenID Connect (implicit flow")], string="Auth Flow", required=True, default="access_token", @@ -56,7 +57,7 @@ def _map_token_values(self, res): res[to_key] = res.get(from_key, "") return res - def _parse_id_token(self, id_token): + def _parse_id_token(self, id_token, access_token): self.ensure_one() res = {} header = jwt.get_unverified_header(id_token) @@ -66,6 +67,7 @@ def _parse_id_token(self, id_token): self._get_key(header), algorithms=["RS256"], audience=self.client_id, + access_token=access_token, ) ) diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index c72bcd2af8..571c115618 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -1,24 +1,29 @@ # Copyright 2016 ICTSTUDIO +# Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) from odoo import api, models +from odoo.exceptions import AccessDenied class ResUsers(models.Model): _inherit = "res.users" @api.model - def _auth_oauth_validate(self, provider, access_token): - """ return the validation data corresponding to the access token """ + def auth_oauth(self, provider, params): oauth_provider = self.env["auth.oauth.provider"].browse(provider) - if oauth_provider.flow == "id_token": - return oauth_provider._parse_id_token(access_token) - else: - return super(ResUsers, self)._auth_oauth_validate() + if oauth_provider.flow != "id_token": + return super(ResUsers, self).auth_oauth(provider, params) - @api.model - def auth_oauth(self, provider, params): - id_token = params.get("id_token") - if id_token: - params["access_token"] = id_token - return super(ResUsers, self).auth_oauth(provider, params) + access_token = params.get('access_token') + id_token = params.get('id_token') + validation = oauth_provider._parse_id_token(id_token, access_token) + # required check + if not validation.get('user_id'): + raise AccessDenied() + # retrieve and sign in user + login = self._auth_oauth_signin(provider, validation, params) + if not login: + raise AccessDenied() + # return user credentials + return (self.env.cr.dbname, login, access_token) From 6e587823a758c99cc43889c3a8aae083f63bd7ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 9 Apr 2021 23:58:10 +0200 Subject: [PATCH 07/46] auth_oidc: _get_key and _map_token_values are not model methods --- auth_oidc/models/auth_oauth_provider.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 875600c6a8..8af713d94c 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -6,7 +6,7 @@ import requests -from odoo import api, fields, models +from odoo import fields, models try: from jose import jwt @@ -35,7 +35,6 @@ class AuthOauthProvider(models.Model): help="For OpenID Connect this should be the location for public keys " ) - @api.model def _get_key(self, header): if self.flow != "id_token": return False @@ -48,7 +47,6 @@ def _get_key(self, header): rsa_key = key return rsa_key - @api.model def _map_token_values(self, res): if self.token_map: for pair in self.token_map.split(" "): From 2bf19a63ee10c0e44f3c77830d0f6e153a8230d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 00:42:10 +0200 Subject: [PATCH 08/46] auth_oidc: cache _get_key --- auth_oidc/models/auth_oauth_provider.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 8af713d94c..38699aec8a 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -6,7 +6,7 @@ import requests -from odoo import fields, models +from odoo import fields, models, tools try: from jose import jwt @@ -35,17 +35,17 @@ class AuthOauthProvider(models.Model): help="For OpenID Connect this should be the location for public keys " ) - def _get_key(self, header): + @tools.ormcache("self.validation_endpoint", "kid") + def _get_key(self, kid): if self.flow != "id_token": return False r = requests.get(self.validation_endpoint) r.raise_for_status() response = r.json() - rsa_key = {} for key in response["keys"]: - if key["kid"] == header.get("kid"): - rsa_key = key - return rsa_key + if key["kid"] == kid: + return key + return {} def _map_token_values(self, res): if self.token_map: @@ -62,7 +62,7 @@ def _parse_id_token(self, id_token, access_token): res.update( jwt.decode( id_token, - self._get_key(header), + self._get_key(header.get("kid")), algorithms=["RS256"], audience=self.client_id, access_token=access_token, From 755895a907b8ea43ed4e9373a42f7a3738b7750c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 01:43:57 +0200 Subject: [PATCH 09/46] auth_oidc: add authorization code flow --- auth_oidc/controllers/main.py | 21 +++++++++++----- auth_oidc/models/auth_oauth_provider.py | 11 ++++++--- auth_oidc/models/res_users.py | 32 +++++++++++++++++++++---- auth_oidc/views/auth_oauth_provider.xml | 6 +++++ 4 files changed, 57 insertions(+), 13 deletions(-) diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index 378b59c1af..c4d4a610f7 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -2,25 +2,34 @@ # Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import logging import uuid import werkzeug.utils from odoo.addons.auth_oauth.controllers.main import OAuthLogin +_logger = logging.getLogger(__name__) + class OpenIDLogin(OAuthLogin): def list_providers(self): providers = super(OpenIDLogin, self).list_providers() for provider in providers: - if provider.get("flow") == "id_token": - provider["nonce"] = uuid.uuid1().hex # TODO Better nonce + flow = provider.get("flow") + if flow in ("id_token", "id_token_code"): params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) - params.pop("response_type") - params.update( - dict(response_type="id_token token", nonce=provider["nonce"]) - ) + params["nonce"] = uuid.uuid1().hex # TODO Better nonce + if flow == "id_token": + # https://openid.net/specs/openid-connect-core-1_0.html + # #ImplicitAuthRequest + params["response_type"] = "id_token token" + elif flow == "id_token_code": + # https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + params["response_type"] = "code" if provider.get("scope"): + if "openid" not in provider["scope"].split(): + _logger.error("openid connect scope must contain 'openid'") params["scope"] = provider["scope"] provider["auth_link"] = "{}?{}".format( provider["auth_endpoint"], werkzeug.url_encode(params) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 38699aec8a..40df2f4d64 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -18,7 +18,11 @@ class AuthOauthProvider(models.Model): _inherit = "auth.oauth.provider" flow = fields.Selection( - [("access_token", "OAuth2"), ("id_token", "OpenID Connect (implicit flow")], + [ + ("access_token", "OAuth2"), + ("id_token", "OpenID Connect (implicit flow)"), + ("id_token_code", "OpenID Connect (authorization code flow)"), + ], string="Auth Flow", required=True, default="access_token", @@ -35,10 +39,11 @@ class AuthOauthProvider(models.Model): help="For OpenID Connect this should be the location for public keys " ) + client_secret = fields.Char() + token_endpoint = fields.Char(string="Token URL") + @tools.ormcache("self.validation_endpoint", "kid") def _get_key(self, kid): - if self.flow != "id_token": - return False r = requests.get(self.validation_endpoint) r.raise_for_status() response = r.json() diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index 571c115618..f0074ac23e 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -2,8 +2,11 @@ # Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import requests + from odoo import api, models from odoo.exceptions import AccessDenied +from odoo.http import request class ResUsers(models.Model): @@ -12,16 +15,37 @@ class ResUsers(models.Model): @api.model def auth_oauth(self, provider, params): oauth_provider = self.env["auth.oauth.provider"].browse(provider) - if oauth_provider.flow != "id_token": + if oauth_provider.flow not in ("id_token", "id_token_code"): return super(ResUsers, self).auth_oauth(provider, params) - access_token = params.get('access_token') - id_token = params.get('id_token') + if oauth_provider.flow == "id_token": + # https://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse + access_token = params.get("access_token") + id_token = params.get("id_token") + elif oauth_provider.flow == "id_token_code": + # https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse + code = params.get("code") + # https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest + response = requests.post( + oauth_provider.token_endpoint, + data=dict( + grant_type="authorization_code", + code=code, + redirect_uri=request.httprequest.url_root + "auth_oauth/signin", + ), + auth=(oauth_provider.client_id, oauth_provider.client_secret), + ) + response.raise_for_status() + response_json = response.json() + # https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse + access_token = response_json.get("access_token") + id_token = response_json.get("id_token") validation = oauth_provider._parse_id_token(id_token, access_token) # required check - if not validation.get('user_id'): + if not validation.get("user_id"): raise AccessDenied() # retrieve and sign in user + params["access_token"] = access_token login = self._auth_oauth_signin(provider, validation, params) if not login: raise AccessDenied() diff --git a/auth_oidc/views/auth_oauth_provider.xml b/auth_oidc/views/auth_oauth_provider.xml index bf7b20ddc4..a86880ba2a 100644 --- a/auth_oidc/views/auth_oauth_provider.xml +++ b/auth_oidc/views/auth_oauth_provider.xml @@ -12,6 +12,12 @@ placeholder="e.g from:to upn:email sub:user_id" /> + + + + + + From bc5ce0f09016350ed79e991e907fafeb2ad3560f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 02:06:27 +0200 Subject: [PATCH 10/46] auth_oidc: separate field for jwks uri --- auth_oidc/models/auth_oauth_provider.py | 12 ++++-------- auth_oidc/views/auth_oauth_provider.xml | 3 ++- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 40df2f4d64..d8d0b94a01 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -27,24 +27,20 @@ class AuthOauthProvider(models.Model): required=True, default="access_token", ) - token_map = fields.Char( help="Some Oauth providers don't map keys in their responses " "exactly as required. It is important to ensure user_id and " "email at least are mapped. For OpenID Connect user_id is " "the sub key in the standard." ) - - validation_endpoint = fields.Char( - help="For OpenID Connect this should be the location for public keys " - ) - client_secret = fields.Char() + validation_endpoint = fields.Char(required=False) token_endpoint = fields.Char(string="Token URL") + jwks_uri = fields.Char(string="JWKS URL") - @tools.ormcache("self.validation_endpoint", "kid") + @tools.ormcache("self.jwks_uri", "kid") def _get_key(self, kid): - r = requests.get(self.validation_endpoint) + r = requests.get(self.jwks_uri) r.raise_for_status() response = r.json() for key in response["keys"]: diff --git a/auth_oidc/views/auth_oauth_provider.xml b/auth_oidc/views/auth_oauth_provider.xml index a86880ba2a..90c931b417 100644 --- a/auth_oidc/views/auth_oauth_provider.xml +++ b/auth_oidc/views/auth_oauth_provider.xml @@ -15,8 +15,9 @@ - + + From 5517ff235f420a3cd2258166afe1738c8bd2e069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 11:11:01 +0200 Subject: [PATCH 11/46] auth_oidc: additional error logging --- auth_oidc/models/res_users.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index f0074ac23e..b495ed87d2 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -2,12 +2,16 @@ # Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import logging + import requests from odoo import api, models from odoo.exceptions import AccessDenied from odoo.http import request +_logger = logging.getLogger(__name__) + class ResUsers(models.Model): _inherit = "res.users" @@ -40,9 +44,16 @@ def auth_oauth(self, provider, params): # https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse access_token = response_json.get("access_token") id_token = response_json.get("id_token") + if not access_token: + _logger.error("No access_token in response.") + raise AccessDenied() + if not id_token: + _logger.error("No id_token in response.") + raise AccessDenied() validation = oauth_provider._parse_id_token(id_token, access_token) # required check if not validation.get("user_id"): + _logger.error("user_id claim not found in id_token (after mapping).") raise AccessDenied() # retrieve and sign in user params["access_token"] = access_token From d0ec070dbc819faed0a51bdf4680c069b976c66f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 11:14:05 +0200 Subject: [PATCH 12/46] auth_oidc: add author and maintainer --- auth_oidc/__manifest__.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index 78b15bf229..a2ff5cae5e 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -1,14 +1,21 @@ # Copyright 2016 ICTSTUDIO +# Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "Authentication OpenID Connect", "version": "13.0.1.0.0", "license": "AGPL-3", - "author": "ICTSTUDIO, André Schenkels, Odoo Community Association (OCA)", + "author": ( + "ICTSTUDIO, André Schenkels, " + "ACSONE SA/NV, " + "Odoo Community Association (OCA)" + ), + "maintainers": ["sbidoul"], "website": "https://github.com/OCA/server-auth", "summary": "Allow users to login through OpenID Connect Provider", "external_dependencies": {"python": ["python-jose"]}, "depends": ["auth_oauth"], "data": ["views/auth_oauth_provider.xml"], + "demo": ["demo/local_keycloak.xml"], } From 1f074ec99998be816688934a7732839c850c529f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 11:22:49 +0200 Subject: [PATCH 13/46] auth_oidc: improve docs, mention implicit flow is not recommended --- auth_oidc/models/auth_oauth_provider.py | 12 ++++++++---- auth_oidc/readme/CONFIGURE.rst | 18 ++++++++++++++---- auth_oidc/readme/CONTRIBUTORS.rst | 1 + auth_oidc/readme/DESCRIPTION.rst | 9 ++++----- auth_oidc/readme/HISTORY.rst | 5 +++++ auth_oidc/readme/ROADMAP.rst | 1 - 6 files changed, 32 insertions(+), 14 deletions(-) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index d8d0b94a01..43a0aa8781 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -20,8 +20,8 @@ class AuthOauthProvider(models.Model): flow = fields.Selection( [ ("access_token", "OAuth2"), - ("id_token", "OpenID Connect (implicit flow)"), ("id_token_code", "OpenID Connect (authorization code flow)"), + ("id_token", "OpenID Connect (implicit flow, not recommended)"), ], string="Auth Flow", required=True, @@ -33,10 +33,14 @@ class AuthOauthProvider(models.Model): "email at least are mapped. For OpenID Connect user_id is " "the sub key in the standard." ) - client_secret = fields.Char() + client_secret = fields.Char( + help="Required for OpenID Connect authorization code flow." + ) validation_endpoint = fields.Char(required=False) - token_endpoint = fields.Char(string="Token URL") - jwks_uri = fields.Char(string="JWKS URL") + token_endpoint = fields.Char( + string="Token URL", help="Required for OpenID Connect authorization code flow." + ) + jwks_uri = fields.Char(string="JWKS URL", help="Required for OpenID Connect.") @tools.ormcache("self.jwks_uri", "kid") def _get_key(self, kid): diff --git a/auth_oidc/readme/CONFIGURE.rst b/auth_oidc/readme/CONFIGURE.rst index 4993f4aea6..74ef8ad38d 100644 --- a/auth_oidc/readme/CONFIGURE.rst +++ b/auth_oidc/readme/CONFIGURE.rst @@ -1,6 +1,10 @@ Setup for Microsoft Azure ~~~~~~~~~~~~~~~~~~~~~~~~~ +Example configuration with OpenID Connect implicit flow. +This configuration is not recommended because it exposes the access token +to the client, and in logs. + # configure a new web application in Azure with OpenID and implicit flow (see the `provider documentation `_) @@ -25,22 +29,28 @@ Setup for Microsoft Azure Setup for Keycloak ~~~~~~~~~~~~~~~~~~ +Example configuration with OpenID Connect authorization code flow. + In Keycloak: # configure a new Client -# make sure Implicit Flow is Enabled. +# make sure Authorization Code Flow is Enabled. +# configure the client Access Type as "confidential" and take note of the client secret in the Credentials tab # configure the redirect url to be "/auth_oauth/signin" In Odoo, create a new Oauth Provider with the following parameters: * Provider name: Keycloak (or any name you like that identify your keycloak provider) -* Auth Flow: OpenID Connect +* Auth Flow: OpenID Connect (authorization code flow) * Client ID: the same Client ID you entered when configuring the client in Keycloak +* Client Secret: found in keycloak on the client Credentials tab * Allowed: yes * Body: the link text to appear on the login page, such as Login with Keycloak +* Scope: openid email * Authentication URL: The "authorization_endpoint" URL found in the OpenID Endpoint Configuration of your Keycloak realm -* Scope: email -* Validation URL: The "jwks_uri" URL found in the +* Token URL: The "token_endpoint" URL found in the + OpenID Endpoint Configuration of your Keycloak realm +* JWKS URL: The "jwks_uri" URL found in the OpenID Endpoint Configuration of your Keycloak realm diff --git a/auth_oidc/readme/CONTRIBUTORS.rst b/auth_oidc/readme/CONTRIBUTORS.rst index 8785f72c06..d721552d86 100644 --- a/auth_oidc/readme/CONTRIBUTORS.rst +++ b/auth_oidc/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Alexandre Fayolle +* Stéphane Bidoul diff --git a/auth_oidc/readme/DESCRIPTION.rst b/auth_oidc/readme/DESCRIPTION.rst index 540c7c2c95..ae89dd9d73 100644 --- a/auth_oidc/readme/DESCRIPTION.rst +++ b/auth_oidc/readme/DESCRIPTION.rst @@ -1,6 +1,5 @@ -This module allows users to login through an OpenID Connect provider. +This module allows users to login through an OpenID Connect provider using the +authorization code flow or implicit flow. -This includes: - -- Keycloak with ClientID and secret + Implicit Flow -- Microsoft Azure +Note the implicit flow is not recommended because it exposes access tokens to +the browser and in http logs. diff --git a/auth_oidc/readme/HISTORY.rst b/auth_oidc/readme/HISTORY.rst index 5aee5128a7..f831ce0e6d 100644 --- a/auth_oidc/readme/HISTORY.rst +++ b/auth_oidc/readme/HISTORY.rst @@ -1,3 +1,8 @@ +13.0.1.0.0 2020-04-10 +~~~~~~~~~~~~~~~~~~~~~ + +* Odoo 13 migration, add authorization code flow. + 10.0.1.0.0 2018-10-05 ~~~~~~~~~~~~~~~~~~~~~ diff --git a/auth_oidc/readme/ROADMAP.rst b/auth_oidc/readme/ROADMAP.rst index c99e29f2c9..6a95f19054 100644 --- a/auth_oidc/readme/ROADMAP.rst +++ b/auth_oidc/readme/ROADMAP.rst @@ -1,3 +1,2 @@ - * When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link * When doing a logout an extra option to also logout at the SSO provider. From 68dcb6ebd8c2b31a1fab3880345b6ec1283ce7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 11:30:10 +0200 Subject: [PATCH 14/46] auth_oidc: better nonce --- auth_oidc/controllers/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index c4d4a610f7..9673cac151 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -3,7 +3,7 @@ # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import logging -import uuid +import secrets import werkzeug.utils @@ -19,7 +19,7 @@ def list_providers(self): flow = provider.get("flow") if flow in ("id_token", "id_token_code"): params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) - params["nonce"] = uuid.uuid1().hex # TODO Better nonce + params["nonce"] = secrets.token_urlsafe() if flow == "id_token": # https://openid.net/specs/openid-connect-core-1_0.html # #ImplicitAuthRequest From 16a4fd73c8154893e59ab598daeef4ce5386d4a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 12:01:44 +0200 Subject: [PATCH 15/46] auth_oidc: make client secret optional This is not a recommended scenario, but this prepares the code for using PKCE --- auth_oidc/models/res_users.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index b495ed87d2..43ab713887 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -30,14 +30,18 @@ def auth_oauth(self, provider, params): # https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse code = params.get("code") # https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest + auth = None + if oauth_provider.client_secret: + auth = (oauth_provider.client_id, oauth_provider.client_secret) response = requests.post( oauth_provider.token_endpoint, data=dict( + client_id=oauth_provider.client_id, grant_type="authorization_code", code=code, redirect_uri=request.httprequest.url_root + "auth_oauth/signin", ), - auth=(oauth_provider.client_id, oauth_provider.client_secret), + auth=auth, ) response.raise_for_status() response_json = response.json() From 0590e0b11ba36ea4d6a0b60fd8f926a491964813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sat, 10 Apr 2021 13:12:02 +0200 Subject: [PATCH 16/46] auth_oidc: add PKCE support --- auth_oidc/controllers/main.py | 13 +++++++++++++ auth_oidc/models/auth_oauth_provider.py | 6 +++++- auth_oidc/models/res_users.py | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index 9673cac151..dbc7dac69c 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -2,6 +2,8 @@ # Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import base64 +import hashlib import logging import secrets @@ -19,7 +21,9 @@ def list_providers(self): flow = provider.get("flow") if flow in ("id_token", "id_token_code"): params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) + # nonce params["nonce"] = secrets.token_urlsafe() + # response_type if flow == "id_token": # https://openid.net/specs/openid-connect-core-1_0.html # #ImplicitAuthRequest @@ -27,10 +31,19 @@ def list_providers(self): elif flow == "id_token_code": # https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest params["response_type"] = "code" + # PKCE (https://tools.ietf.org/html/rfc7636) + code_verifier = provider["code_verifier"] + code_challenge = base64.urlsafe_b64encode( + hashlib.sha256(code_verifier.encode("ascii")).digest() + ).rstrip(b"=") + params["code_challenge"] = code_challenge + params["code_challenge_method"] = "S256" + # scope if provider.get("scope"): if "openid" not in provider["scope"].split(): _logger.error("openid connect scope must contain 'openid'") params["scope"] = provider["scope"] + # auth link that the user will click provider["auth_link"] = "{}?{}".format( provider["auth_endpoint"], werkzeug.url_encode(params) ) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 43a0aa8781..06f0dae545 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -3,6 +3,7 @@ # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) import logging +import secrets import requests @@ -34,7 +35,10 @@ class AuthOauthProvider(models.Model): "the sub key in the standard." ) client_secret = fields.Char( - help="Required for OpenID Connect authorization code flow." + help="Used in OpenID Connect authorization code flow for confidential clients.", + ) + code_verifier = fields.Char( + default=lambda self: secrets.token_urlsafe(32), help="Used for PKCE." ) validation_endpoint = fields.Char(required=False) token_endpoint = fields.Char( diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index 43ab713887..8a0328c26a 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -39,6 +39,7 @@ def auth_oauth(self, provider, params): client_id=oauth_provider.client_id, grant_type="authorization_code", code=code, + code_verifier=oauth_provider.code_verifier, # PKCE redirect_uri=request.httprequest.url_root + "auth_oauth/signin", ), auth=auth, From b7bbd3c328aa2e132561b2c7029b15691789c129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Sun, 11 Apr 2021 14:44:16 +0200 Subject: [PATCH 17/46] auth_oidc: demo data and basic test --- auth_oidc/demo/local_keycloak.xml | 20 + auth_oidc/tests/__init__.py | 1 + auth_oidc/tests/keycloak/keycloak-config.json | 1997 +++++++++++++++++ auth_oidc/tests/keycloak/keycloak.sh | 10 + auth_oidc/tests/test_auth_oidc_auth_code.py | 52 + 5 files changed, 2080 insertions(+) create mode 100644 auth_oidc/demo/local_keycloak.xml create mode 100644 auth_oidc/tests/__init__.py create mode 100644 auth_oidc/tests/keycloak/keycloak-config.json create mode 100755 auth_oidc/tests/keycloak/keycloak.sh create mode 100644 auth_oidc/tests/test_auth_oidc_auth_code.py diff --git a/auth_oidc/demo/local_keycloak.xml b/auth_oidc/demo/local_keycloak.xml new file mode 100644 index 0000000000..919754db99 --- /dev/null +++ b/auth_oidc/demo/local_keycloak.xml @@ -0,0 +1,20 @@ + + + keycloak:8080 on localhost + id_token_code + auth_oidc-test + preferred_username:user_id + keycloak:8080 on localhost + + openid email + http://localhost:8080/auth/realms/master/protocol/openid-connect/auth + http://localhost:8080/auth/realms/master/protocol/openid-connect/token + http://localhost:8080/auth/realms/master/protocol/openid-connect/certs + + diff --git a/auth_oidc/tests/__init__.py b/auth_oidc/tests/__init__.py new file mode 100644 index 0000000000..e603d993b8 --- /dev/null +++ b/auth_oidc/tests/__init__.py @@ -0,0 +1 @@ +from . import test_auth_oidc_auth_code diff --git a/auth_oidc/tests/keycloak/keycloak-config.json b/auth_oidc/tests/keycloak/keycloak-config.json new file mode 100644 index 0000000000..5a0456b55c --- /dev/null +++ b/auth_oidc/tests/keycloak/keycloak-config.json @@ -0,0 +1,1997 @@ +{ + "id": "master", + "realm": "master", + "displayName": "Keycloak", + "displayNameHtml": "
Keycloak
", + "notBefore": 0, + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 60, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "enabled": true, + "sslRequired": "external", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "roles": { + "realm": [ + { + "id": "39f27ebe-139e-435b-840a-beb824d5d355", + "name": "admin", + "description": "${role_admin}", + "composite": true, + "composites": { + "realm": ["create-realm"], + "client": { + "master-realm": [ + "create-client", + "view-realm", + "view-events", + "manage-clients", + "query-clients", + "view-identity-providers", + "impersonation", + "manage-events", + "query-realms", + "query-groups", + "manage-authorization", + "query-users", + "view-authorization", + "manage-identity-providers", + "manage-users", + "view-clients", + "view-users", + "manage-realm" + ] + } + }, + "clientRole": false, + "containerId": "master", + "attributes": {} + }, + { + "id": "3fd38fac-f708-4783-b8e9-4e47963fc4bf", + "name": "offline_access", + "description": "${role_offline-access}", + "composite": false, + "clientRole": false, + "containerId": "master", + "attributes": {} + }, + { + "id": "4ac4a81b-0a30-41db-94ce-dbd621c331d2", + "name": "uma_authorization", + "description": "${role_uma_authorization}", + "composite": false, + "clientRole": false, + "containerId": "master", + "attributes": {} + }, + { + "id": "20b16986-2361-454c-af0b-81f403152ef8", + "name": "create-realm", + "description": "${role_create-realm}", + "composite": false, + "clientRole": false, + "containerId": "master", + "attributes": {} + } + ], + "client": { + "auth_oidc-test": [], + "security-admin-console": [], + "admin-cli": [], + "account-console": [], + "broker": [ + { + "id": "5fa108a0-2e5e-4e2e-8ee3-1317592517f8", + "name": "read-token", + "description": "${role_read-token}", + "composite": false, + "clientRole": true, + "containerId": "d1dd5ade-20bf-4d53-a371-a33f10bc1087", + "attributes": {} + } + ], + "master-realm": [ + { + "id": "0d062a1c-5165-4ea5-b550-55d02ca86226", + "name": "create-client", + "description": "${role_create-client}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "f5795bcb-ab2d-4a74-b954-51f335c21198", + "name": "view-realm", + "description": "${role_view-realm}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "1c0e3231-db03-4b03-961f-32308318f4f1", + "name": "view-events", + "description": "${role_view-events}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "ed9949d1-b11d-4742-b259-ee260f62f111", + "name": "manage-clients", + "description": "${role_manage-clients}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "b29b494a-9cd4-4410-8d16-207a3bb2e528", + "name": "query-clients", + "description": "${role_query-clients}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "8afdd284-070b-4d6f-9d21-d9917d8827af", + "name": "view-identity-providers", + "description": "${role_view-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "b5807416-f8f3-41ae-a29e-298ec3aae028", + "name": "impersonation", + "description": "${role_impersonation}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "6400b20c-a72b-4228-87d1-01b0d1315026", + "name": "manage-events", + "description": "${role_manage-events}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "6c3b14c3-0797-4e39-b5fa-b07d0e073e0e", + "name": "query-realms", + "description": "${role_query-realms}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "3fbf2279-9661-4cfc-b381-c7e4a8c459dc", + "name": "query-groups", + "description": "${role_query-groups}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "1b9e5572-34d5-4284-897c-0471544cf813", + "name": "manage-authorization", + "description": "${role_manage-authorization}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "a226e0fa-aa45-490d-9d64-78e88c3152cb", + "name": "query-users", + "description": "${role_query-users}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "d78736d4-250c-4012-a8ad-55b5c718a57a", + "name": "manage-identity-providers", + "description": "${role_manage-identity-providers}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "68e9559f-d467-46c3-ae48-d67c74582ca8", + "name": "view-authorization", + "description": "${role_view-authorization}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "908beec6-d8d1-441a-a5ad-f45d39df6b43", + "name": "manage-users", + "description": "${role_manage-users}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "5c5d56a2-e2ea-4b4f-9bb1-f40e18082932", + "name": "view-clients", + "description": "${role_view-clients}", + "composite": true, + "composites": { + "client": { + "master-realm": ["query-clients"] + } + }, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "561ec0f4-bd97-4c41-a825-918002afb307", + "name": "view-users", + "description": "${role_view-users}", + "composite": true, + "composites": { + "client": { + "master-realm": ["query-groups", "query-users"] + } + }, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + }, + { + "id": "8ae350ac-19cd-431b-80fb-aee88316219e", + "name": "manage-realm", + "description": "${role_manage-realm}", + "composite": false, + "clientRole": true, + "containerId": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "attributes": {} + } + ], + "account": [ + { + "id": "0a6ac4dd-afdc-4b7b-b16a-ef2ca3b8e396", + "name": "delete-account", + "description": "${role_delete-account}", + "composite": false, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "1a30b2d0-9d49-4a09-9769-ea7f3142e715", + "name": "manage-consent", + "description": "${role_manage-consent}", + "composite": true, + "composites": { + "client": { + "account": ["view-consent"] + } + }, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "e065f4ba-d97c-4219-b56e-edbe945e14bf", + "name": "view-consent", + "description": "${role_view-consent}", + "composite": false, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "6e5b9a43-0f82-4669-a821-a1d449e4a2be", + "name": "view-applications", + "description": "${role_view-applications}", + "composite": false, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "c386c8c5-bdee-4d52-b124-94379799d5d9", + "name": "manage-account-links", + "description": "${role_manage-account-links}", + "composite": false, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "bcff49f4-7f83-4ec6-9a51-271fc9cbb302", + "name": "view-profile", + "description": "${role_view-profile}", + "composite": false, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + }, + { + "id": "4e36a4bf-80ab-404b-854e-7d593e9248ca", + "name": "manage-account", + "description": "${role_manage-account}", + "composite": true, + "composites": { + "client": { + "account": ["manage-account-links"] + } + }, + "clientRole": true, + "containerId": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "attributes": {} + } + ] + } + }, + "groups": [], + "defaultRoles": ["offline_access", "uma_authorization"], + "requiredCredentials": ["password"], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpSupportedApplications": ["FreeOTP", "Google Authenticator"], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": ["ES256"], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": ["ES256"], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": [ + { + "id": "ad01a4d9-c919-4bc6-8b48-b3e3bcbb4149", + "createdTimestamp": 1618140941731, + "username": "admin", + "enabled": true, + "totp": false, + "emailVerified": false, + "credentials": [ + { + "id": "596b17bb-199c-4a23-9c48-4620c0ecfd7a", + "type": "password", + "createdDate": 1618140941876, + "secretData": "{\"value\":\"PXx46hQETQuXQRUl9FvzEJdZtoL57qsad1dFQyOLzj/pNEmwldN54oxQh5p+QB0rNNJPI9ZiaAfZS90ZzJa6pQ==\",\"salt\":\"kiFQwyPm53MgwAByqTw5qQ==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["offline_access", "admin", "uma_authorization"], + "clientRoles": { + "account": ["view-profile", "manage-account"] + }, + "notBefore": 0, + "groups": [] + }, + { + "id": "3752dfb8-d3b5-4597-b83c-fed005d2671c", + "createdTimestamp": 1618141153912, + "username": "demo", + "enabled": true, + "totp": false, + "emailVerified": false, + "credentials": [ + { + "id": "4e5b5a38-3fcb-4703-8b9c-075164dde145", + "type": "password", + "createdDate": 1618141311783, + "secretData": "{\"value\":\"upShAwzTaS89elSkEgK0Phs+XUP3Ya1pOUYtE8k4JmZEJnXWjdOy9brn4cpLKwjF6pZ3glxkJgjdLmDeWm9WwQ==\",\"salt\":\"RnaXCbRf4bw1lZmQX43cMg==\",\"additionalParameters\":{}}", + "credentialData": "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } + ], + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": ["offline_access", "uma_authorization"], + "clientRoles": { + "account": ["view-profile", "manage-account"] + }, + "notBefore": 0, + "groups": [] + } + ], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": ["offline_access"] + } + ], + "clientScopeMappings": { + "account": [ + { + "client": "account-console", + "roles": ["manage-account"] + } + ] + }, + "clients": [ + { + "id": "45ef915f-ca72-4bcc-a79b-d2b83ca4be2f", + "clientId": "account", + "name": "${client_account}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/master/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "055c06d1-ffcc-4762-b8eb-e9814a6995df", + "defaultRoles": ["view-profile", "manage-account"], + "redirectUris": ["/realms/master/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "4308a071-7dbf-4d08-a987-b7dc2f42b86e", + "clientId": "account-console", + "name": "${client_account-console}", + "rootUrl": "${authBaseUrl}", + "baseUrl": "/realms/master/account/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "ec31839f-7ffb-400d-9373-26be6706e619", + "redirectUris": ["/realms/master/account/*"], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "e36bba2d-7a07-4c83-a40e-14b4a8316ae9", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ], + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "0915d9fc-102f-4033-b37e-832b89fee932", + "clientId": "admin-cli", + "name": "${client_admin-cli}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "175303e4-f2d4-4ae9-8fb0-27a1337d3208", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": false, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "8bf21eb5-63da-4da5-8c12-7a5bafda1bf5", + "clientId": "auth_oidc-test", + "rootUrl": "http://localhost:8069", + "adminUrl": "http://localhost:8069", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "20c0ad33-0200-43cd-9bd1-5dd1b22918e3", + "redirectUris": ["http://localhost:8069/*"], + "webOrigins": ["http://localhost:8069"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": true, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "saml.assertion.signature": "false", + "saml.force.post.binding": "false", + "saml.multivalued.roles": "false", + "saml.encrypt": "false", + "backchannel.logout.revoke.offline.tokens": "false", + "saml.server.signature": "false", + "saml.server.signature.keyinfo.ext": "false", + "exclude.session.state.from.auth.response": "false", + "backchannel.logout.session.required": "true", + "client_credentials.use_refresh_token": "false", + "saml_force_name_id_format": "false", + "saml.client.signature": "false", + "tls.client.certificate.bound.access.tokens": "false", + "saml.authnstatement": "false", + "display.on.consent.screen": "false", + "saml.onetimeuse.condition": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "d1dd5ade-20bf-4d53-a371-a33f10bc1087", + "clientId": "broker", + "name": "${client_broker}", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "52aab659-5f2d-445a-a93e-6ab04de9db42", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "4e03d0f9-4c56-42c8-958c-337f6eede3ac", + "clientId": "master-realm", + "name": "master Realm", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "15ca5d76-d964-4761-85d1-8343748481ab", + "redirectUris": [], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": true, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": false, + "frontchannelLogout": false, + "attributes": {}, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": 0, + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + }, + { + "id": "983b74a1-e7a0-4bc4-8481-0eb8ca4f12e0", + "clientId": "security-admin-console", + "name": "${client_security-admin-console}", + "rootUrl": "${authAdminUrl}", + "baseUrl": "/admin/master/console/", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "121beec3-2388-475b-b989-1ff85d25b4fd", + "redirectUris": ["/admin/master/console/*"], + "webOrigins": ["+"], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": false, + "serviceAccountsEnabled": false, + "publicClient": true, + "frontchannelLogout": false, + "protocol": "openid-connect", + "attributes": { + "pkce.code.challenge.method": "S256" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": false, + "nodeReRegistrationTimeout": 0, + "protocolMappers": [ + { + "id": "21a0f25a-a2b7-415e-95a7-23886f00c83b", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": ["web-origins", "role_list", "roles", "profile", "email"], + "optionalClientScopes": ["address", "phone", "offline_access", "microprofile-jwt"] + } + ], + "clientScopes": [ + { + "id": "07bff9f2-498f-4f07-9fb9-019152141a0f", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "46333a31-1e88-4191-8d25-2f4d975af4db", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "8c30d3a8-af56-407a-a622-df16e7c2b04b", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "db1117e1-6174-4934-99d0-ff51f319c6f5", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "c1a7d21e-7f45-4559-b314-913cbb560967", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "3d9f47ed-d2f0-474c-baa4-75e0e6619385", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "e7c49c89-b513-4512-9410-bc0f39d4b4e5", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + }, + { + "id": "7b65fe0b-2974-445f-a7cd-ccec5141f560", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "a9ed1a18-0e05-4eeb-a247-c84296cfa653", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "52f62f87-f914-4771-b79c-46a387797be7", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d8c485dc-7b8a-4469-8868-5bb73a6abafa", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "id": "b1b21cd7-06f7-4469-b11e-22bf9de1a3ce", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "311bf186-6cb2-4bdd-9da5-8ac30ff8b296", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "eda5d01f-6841-441e-b7d0-8fa48365a501", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "e131db35-e08b-4805-b11b-59a90650ee2a", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "6e311016-71e1-450a-8011-6f6ce9f7e365", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "e0fc1d75-2b19-4fc8-8ea3-778c96b47321", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "a94d4acf-0a46-44b0-a739-ed329dfa9f41", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "57d4c1ad-4507-4545-8efc-5f83c0dc0be6", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "0b42528f-d116-4217-9fbe-fe469c80914d", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "f698a281-a28c-4b3e-ad73-524c556d1cc5", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "50b2c4f9-b4a3-470a-a8e4-af7384bd9536", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "id": "12a21276-1f49-4b34-8bf7-df4dd7929ebf", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "String" + } + }, + { + "id": "4abce20a-c47d-430a-85c0-f65cb9ae7aa0", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "fa7c99b6-9cdf-4f8f-96af-45df5f2497e2", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "983d7499-e23c-4971-9501-d404b405b484", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "f42b6bcb-4bac-44a8-8073-c3194b56029c", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "6acc2950-2bbe-49d8-b266-42e3c518f46f", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "8b0f3195-f1c2-4188-8045-5d48ba0aeb30", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "437e71e0-7728-4763-82af-29fd14b1fa12", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "d255020d-b385-417d-bf27-b3a8c5911579", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "5adb2a9c-5dbb-430c-89c0-9b464677245f", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "667e731f-4375-489f-bf03-566a7292719b", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "359211aa-1a6d-4441-a9bb-610acc6350db", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "6646f9a5-0a1e-4bcc-b1d1-35b035b5aa55", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": ["jboss-logging"], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "070f65ee-8d8f-4fff-ae32-a27016e7bf5c", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "81bc44f5-9bbd-4325-9f63-fc332e30e0e7", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-usermodel-property-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "saml-user-attribute-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "f7d673f4-10ef-486d-a168-925b139abbc5", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "c103a051-9371-4021-8a34-8196a78c3638", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": ["true"] + } + }, + { + "id": "a71eaf13-3e55-4aa6-9cf8-c1b74198d63a", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": ["true"], + "client-uris-must-match": ["true"] + } + }, + { + "id": "fb7bdacb-46bc-4266-b217-a1705e87f957", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-property-mapper", + "oidc-full-name-mapper", + "oidc-usermodel-property-mapper", + "oidc-address-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "ec4f12ef-2fef-42b2-9cb8-9f251d8c3344", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "99778abc-51f8-4480-a778-59ef73a60f56", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": ["200"] + } + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "052d41cb-6938-40f0-8872-8ab171ec27e9", + "name": "fallback-HS256", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "kid": ["489fe44c-d70f-42f7-9a19-e3dc2a0df076"], + "secret": [ + "6dqvUhGU5rhuMOKNuOI4U7nPTcA9jeJJLpmoewnkw_PdFDSjy73iQkPt5hw_8qU34IIFGOM-LkJJ8VWihvwEwQ" + ], + "priority": ["-100"], + "algorithm": ["HS256"] + } + }, + { + "id": "53901ca8-2f9d-4f2e-804f-756204b9c1c5", + "name": "fallback-RS256", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "privateKey": [ + "MIIEpAIBAAKCAQEAgYNR3Pgh/f1+DUcMBc9T6uT1MwC4oTthGbtJhmqQiawSWzUO8icSM4hFjiN2zqsKx7ofWmP3+ZRTq6fSEref+0tRRWafTq6LtDySa4DilqnQ/WBznnXML9hmsPBW3gNiZAKYbwvb/YE36L+a4nWcEc13jcXgMqLXUD7K/3YIYNT/S7xGgNKYfBmbTfS0A08ZITtyakaGYwLK6zwLYVAeUj1hZjVcz1926Zhhu4YznD6qMgCmBwlSD9lc6v0/RUNjo1NKSU0LXAeUEk1ynxFKJ+cUikHuvsIQvuXY5Sj+Y2tcWFpU51J01com69kdyYeelQv1n41yOB/U4bGmbhUctwIDAQABAoIBAH+RgwwdmRXeH9AiQBRk8Gq5lU/kkPe3TmCTGsv8oVwKEpamP4+Drqj1vFVSV08gKOEsUn+tYm8CjBvTlNd86WcT+/xZJefRg6hH1Y1wiUAQCtvYqmnV7Abgp933Dglm2f5alB0lWE5ufkySlpQjdlQOx4js9HXL8juHblqMv5noJNaDQSDh4UxtET+fVT8pvCL/MImG4C6BtULLDXLdH4pIvn0OIS788Xpc87uc3dSIfVxL1Oa0U1Qrxma5P8p9imremKLdA4iOzopyVsLo0uP2PrSLWD04I9kSwO0MHNDzbkMJiWXX5a7afzRY4g+zQL2STYsbD4B9KQnIpsQNrVkCgYEA4LbUejeexUBsjs3Whkn1BlUvxvDf2Vtudws1XSNmd5lneitfseDXCcH6p1TLHn0xoOmJDFGjLPEYFhqL0I5IyZP6zfiCJL88zFWVXlY8NsAoQeqvgnu5wHpIXEXCaJAAksWy/dmUZwTUyfIIxnLUQMPpJ1stu35e8DyNO4VadPMCgYEAk4teFNWkFBZmjSBUyo8Tw4TmJuCIRVH4FSaspUeVNhToKI3e5R/duz8rvqBj4tul5lyWq9FmcDaawE94jIa17XQnj/O767G72lHRuIlI+qftIca3r4/kDvy730yAOWl/1Su4SrX3t7WSBHIG2j7HMYIsj5xgBUvnbRQUtxByui0CgYEAsLe3YyHoj3D5rlg708HHmqJVf1sgfxvDRIUhA0z6oSWX1eDUUdvi4H6XMw6g6ipEZCokJ/bvn0E+0usvduTeYwAn5eD/4AwwsPTBEb45fkkhn60DN1c7nh3MWBxYJcjRWpt1BuMcLOQEv4fC1OWq+//VlKjEz0UzPjQwUVWu7HcCgYAo6uqhfootY/T2yHObZUiG3ZFyUKyaBNx3CS2x/IMd53hm3slk44x7hE5eZF6vKFj+5MiIR99P2WTbVm7JEgbcHm1mV6LS/4xoRG6T7cbGdNGnn1OLpa0Klv6HM9EPmvlvpdtLJOHZGcqv3uuVlPlq+n3fKe/bKCy7LGl+R1p51QKBgQCEmPS9A9y6YF7zRo8u7vUmJzGktdrSw65zYZVMzXY9A7uKU/OfpZ+papKDr1D9ApFgi5Ip1imirR7K9m4GImowZOTe/E6dT6nmrUtWUkaS4ghhwZ9Gh6kAOWoBYRB/Z4XIzJoSiet+PJ3p8SLhM7nETj7IDaeQgNudSK8/ohNPWQ==" + ], + "certificate": [ + "MIICmzCCAYMCBgF4wLeSOTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwNDExMTEzNDE5WhcNMzEwNDExMTEzNTU5WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCBg1Hc+CH9/X4NRwwFz1Pq5PUzALihO2EZu0mGapCJrBJbNQ7yJxIziEWOI3bOqwrHuh9aY/f5lFOrp9ISt5/7S1FFZp9Orou0PJJrgOKWqdD9YHOedcwv2Gaw8FbeA2JkAphvC9v9gTfov5ridZwRzXeNxeAyotdQPsr/dghg1P9LvEaA0ph8GZtN9LQDTxkhO3JqRoZjAsrrPAthUB5SPWFmNVzPX3bpmGG7hjOcPqoyAKYHCVIP2Vzq/T9FQ2OjU0pJTQtcB5QSTXKfEUon5xSKQe6+whC+5djlKP5ja1xYWlTnUnTVyibr2R3Jh56VC/WfjXI4H9ThsaZuFRy3AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHEq+7bncqOh0RJJj+6fSHsIlkRGOeX6djVIKi1/eAJCD61Et3MHKh4kbu4U6phNlnhW5IFYinchGXe1uoG18fWkUS6QJoxHIDLR+tub7NSMraYxK85VgyLHCHaaGX7Bz+sIM628th4LlQd/M2zL45rqlMvB1XLxsMpi9Pb0Zc7qWwrvE5Jfi99UDAi6ZV3OojR6YC79HVHyOVmBIdLrVtn5mQYKJ5tF5F8xSs4ng96IO8Sn8pbUuYG8SlEz6KMmGH1sczlPE/3kAdm9IF+fXpYywuhsRNJyDBVDGpcqHTW+UW+V5TWa/ucZ6cpr1dQP5/FpcHylSWoXJpCk01PXl/M=" + ], + "priority": ["-100"], + "algorithm": ["RS256"] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "815c3100-241b-4298-8039-54253c2c7e70", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "e6a85d70-dc13-4705-9ba3-a980d548a430", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "basic-auth-otp", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "96b14b20-7305-4018-8e75-86b572e3ace0", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "a51abcaa-7495-4526-997f-55cd82f152e2", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "54fa7607-f4e5-4fe7-a8c3-4fad6ee376b8", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-otp-form", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "31de3d9e-2f81-43cc-a4ad-1112cd4e9d71", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Account verification options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "f499a904-41cc-41c1-bb43-688672d1533b", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-otp", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "7cee4451-5b17-4742-8736-8fce85639409", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 20, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "dafe7404-bce7-4ab6-9e35-fd1aecb93545", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d34ac568-3793-4864-8df8-733fa2cd3554", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "auth-spnego", + "requirement": "DISABLED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "identity-provider-redirector", + "requirement": "ALTERNATIVE", + "priority": 25, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "ALTERNATIVE", + "priority": 30, + "flowAlias": "forms", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "15a43900-948a-487d-a97a-444502dce766", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "requirement": "ALTERNATIVE", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-jwt", + "requirement": "ALTERNATIVE", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-secret-jwt", + "requirement": "ALTERNATIVE", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "client-x509", + "requirement": "ALTERNATIVE", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "1865917e-f56d-453c-bb66-578e1955d199", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "direct-grant-validate-password", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 30, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "3e151380-0869-4bdb-b9d1-65a7bcfcb6ab", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "79369bf2-9434-4b93-94d9-74a08a361701", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "User creation or linking", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "322d8eb9-a9cc-422a-a6a2-065b3b863967", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 20, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "0cf68f4f-332e-4f7d-a7a9-907189914191", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "REQUIRED", + "priority": 20, + "flowAlias": "Authentication Options", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d412906e-1aad-4707-ac40-95406aeed8d0", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "requirement": "REQUIRED", + "priority": 10, + "flowAlias": "registration form", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "d11ddacf-4fe8-4614-a9f1-fe5dcf621330", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-profile-action", + "requirement": "REQUIRED", + "priority": 40, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-password-action", + "requirement": "REQUIRED", + "priority": 50, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "registration-recaptcha-action", + "requirement": "DISABLED", + "priority": 60, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + }, + { + "id": "48093169-4a24-499b-aad7-b87dcd32269d", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-credential-email", + "requirement": "REQUIRED", + "priority": 20, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "authenticator": "reset-password", + "requirement": "REQUIRED", + "priority": 30, + "userSetupAllowed": false, + "autheticatorFlow": false + }, + { + "requirement": "CONDITIONAL", + "priority": 40, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false, + "autheticatorFlow": true + } + ] + }, + { + "id": "f0a1d44c-2bab-408c-91b4-bb730bd65bc4", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "requirement": "REQUIRED", + "priority": 10, + "userSetupAllowed": false, + "autheticatorFlow": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "173229da-6a52-4d54-8e88-cba503234cb4", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "a72c2a36-a48a-440d-b52f-831c385287fc", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "terms_and_conditions", + "name": "Terms and Conditions", + "providerId": "terms_and_conditions", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": {}, + "keycloakVersion": "12.0.4", + "userManagedAccessAllowed": false +} diff --git a/auth_oidc/tests/keycloak/keycloak.sh b/auth_oidc/tests/keycloak/keycloak.sh new file mode 100755 index 0000000000..7c81d562e7 --- /dev/null +++ b/auth_oidc/tests/keycloak/keycloak.sh @@ -0,0 +1,10 @@ +#!/bin/sh +set -x +$(which docker || which podman) run --rm \ + -v $(dirname $0)/keycloak-config.json:/tmp/keycloak-config.json \ + -p 8080:8080 \ + quay.io/keycloak/keycloak:12.0.4 \ + -Dkeycloak.migration.action=import \ + -Dkeycloak.migration.provider=singleFile \ + -Dkeycloak.migration.file=/tmp/keycloak-config.json \ + -Dkeycloak.migration.strategy=OVERWRITE_EXISTING diff --git a/auth_oidc/tests/test_auth_oidc_auth_code.py b/auth_oidc/tests/test_auth_oidc_auth_code.py new file mode 100644 index 0000000000..623fa4b2cf --- /dev/null +++ b/auth_oidc/tests/test_auth_oidc_auth_code.py @@ -0,0 +1,52 @@ +# Copyright 2021 ACSONE SA/NV +# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import contextlib +from urllib.parse import parse_qs, urlparse + +import odoo +from odoo.tests import common + +from odoo.addons.auth_oidc.controllers.main import OpenIDLogin +from odoo.addons.website.tools import MockRequest as _MockRequest + +BASE_URL = "http://localhost:%s" % odoo.tools.config["http_port"] + + +@contextlib.contextmanager +def MockRequest(env): + with _MockRequest(env) as request: + request.httprequest.url_root = BASE_URL + "/" + request.params = {} + yield request + + +class TestAuthOIDCAuthorizationCodeFlow(common.HttpCase): + def setUp(self): + super().setUp() + # search our test provider and bind the demo user to it + self.provider_rec = self.env["auth.oauth.provider"].search( + [("client_id", "=", "auth_oidc-test")] + ) + self.assertEqual(len(self.provider_rec), 1) + + def test_auth_link(self): + """Test that the authentication link is correct.""" + # disable existing providers except our test provider + self.env["auth.oauth.provider"].search( + [("client_id", "!=", "auth_oidc-test")] + ).write(dict(enabled=False)) + with MockRequest(self.env): + providers = OpenIDLogin().list_providers() + self.assertEqual(len(providers), 1) + auth_link = providers[0]["auth_link"] + assert auth_link.startswith(self.provider_rec.auth_endpoint) + params = parse_qs(urlparse(auth_link).query) + self.assertEqual(params["response_type"], ["code"]) + self.assertEqual(params["client_id"], [self.provider_rec.client_id]) + self.assertEqual(params["scope"], ["openid email"]) + self.assertTrue(params["code_challenge"]) + self.assertEqual(params["code_challenge_method"], ["S256"]) + self.assertTrue(params["nonce"]) + self.assertTrue(params["state"]) + self.assertEqual(params["redirect_uri"], [BASE_URL + "/auth_oauth/signin"]) From 952d6a7d17675a3bd73bfeab5540ba6fa8c4ff4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 25 Jun 2021 16:03:09 +0200 Subject: [PATCH 18/46] auth_oidc: slightly more robust parsing of claim mapping --- auth_oidc/models/auth_oauth_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 06f0dae545..6a40e87ed6 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -59,7 +59,7 @@ def _get_key(self, kid): def _map_token_values(self, res): if self.token_map: for pair in self.token_map.split(" "): - from_key, to_key = pair.split(":") + from_key, to_key = [k.strip() for k in pair.split(":", 1)] if to_key not in res: res[to_key] = res.get(from_key, "") return res From 677f17398e4225c5c64e8c0a698260030323d645 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Bidoul?= Date: Fri, 25 Jun 2021 16:03:22 +0200 Subject: [PATCH 19/46] auth_oidc: split long method --- auth_oidc/models/res_users.py | 61 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index 8a0328c26a..c487504e2a 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -16,39 +16,46 @@ class ResUsers(models.Model): _inherit = "res.users" + def _auth_oauth_get_tokens_implicit_flow(self, oauth_provider, params): + # https://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse + return params.get("access_token"), params.get("id_token") + + def _auth_oauth_get_tokens_auth_code_flow(self, oauth_provider, params): + # https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse + code = params.get("code") + # https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest + auth = None + if oauth_provider.client_secret: + auth = (oauth_provider.client_id, oauth_provider.client_secret) + response = requests.post( + oauth_provider.token_endpoint, + data=dict( + client_id=oauth_provider.client_id, + grant_type="authorization_code", + code=code, + code_verifier=oauth_provider.code_verifier, # PKCE + redirect_uri=request.httprequest.url_root + "auth_oauth/signin", + ), + auth=auth, + ) + response.raise_for_status() + response_json = response.json() + # https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse + return response_json.get("access_token"), response_json.get("id_token") + @api.model def auth_oauth(self, provider, params): oauth_provider = self.env["auth.oauth.provider"].browse(provider) - if oauth_provider.flow not in ("id_token", "id_token_code"): - return super(ResUsers, self).auth_oauth(provider, params) - if oauth_provider.flow == "id_token": - # https://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthResponse - access_token = params.get("access_token") - id_token = params.get("id_token") + access_token, id_token = self._auth_oauth_get_tokens_implicit_flow( + oauth_provider, params + ) elif oauth_provider.flow == "id_token_code": - # https://openid.net/specs/openid-connect-core-1_0.html#AuthResponse - code = params.get("code") - # https://openid.net/specs/openid-connect-core-1_0.html#TokenRequest - auth = None - if oauth_provider.client_secret: - auth = (oauth_provider.client_id, oauth_provider.client_secret) - response = requests.post( - oauth_provider.token_endpoint, - data=dict( - client_id=oauth_provider.client_id, - grant_type="authorization_code", - code=code, - code_verifier=oauth_provider.code_verifier, # PKCE - redirect_uri=request.httprequest.url_root + "auth_oauth/signin", - ), - auth=auth, + access_token, id_token = self._auth_oauth_get_tokens_auth_code_flow( + oauth_provider, params ) - response.raise_for_status() - response_json = response.json() - # https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse - access_token = response_json.get("access_token") - id_token = response_json.get("id_token") + else: + return super(ResUsers, self).auth_oauth(provider, params) if not access_token: _logger.error("No access_token in response.") raise AccessDenied() From e028d28fa462c156973c2b8b1d4ae54320db913a Mon Sep 17 00:00:00 2001 From: oca-travis Date: Fri, 25 Jun 2021 14:19:32 +0000 Subject: [PATCH 20/46] [UPD] Update auth_oidc.pot --- auth_oidc/README.rst | 143 ++++++- auth_oidc/__manifest__.py | 2 +- auth_oidc/i18n/auth_oidc.pot | 113 +++++ auth_oidc/static/description/index.html | 526 ++++++++++++++++++++++++ 4 files changed, 772 insertions(+), 12 deletions(-) create mode 100644 auth_oidc/i18n/auth_oidc.pot create mode 100644 auth_oidc/static/description/index.html diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst index 2bcb185e11..932ffea7b0 100644 --- a/auth_oidc/README.rst +++ b/auth_oidc/README.rst @@ -1,21 +1,57 @@ -================================= -Authentication via OpenID Connect -================================= +============================= +Authentication OpenID Connect +============================= -This module allows users to login through an OpenID Connect provider. +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -This includes: +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github + :target: https://github.com/OCA/server-auth/tree/13.0/auth_oidc + :alt: OCA/server-auth +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-auth-13-0/server-auth-13-0-auth_oidc + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/251/13.0 + :alt: Try me on Runbot -- Keycloak with ClientID and secret + Implicit Flow -- Microsoft Azure +|badge1| |badge2| |badge3| |badge4| |badge5| +This module allows users to login through an OpenID Connect provider using the +authorization code flow or implicit flow. -Usage -===== +Note the implicit flow is not recommended because it exposes access tokens to +the browser and in http logs. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module depends on the `python-jose `__ +library, not to be confused with ``jose`` which is also available on PyPI. + +Configuration +============= Setup for Microsoft Azure ~~~~~~~~~~~~~~~~~~~~~~~~~ +Example configuration with OpenID Connect implicit flow. +This configuration is not recommended because it exposes the access token +to the client, and in logs. + # configure a new web application in Azure with OpenID and implicit flow (see the `provider documentation `_) @@ -40,8 +76,65 @@ Setup for Microsoft Azure Setup for Keycloak ~~~~~~~~~~~~~~~~~~ -write me... +Example configuration with OpenID Connect authorization code flow. + +In Keycloak: + +# configure a new Client +# make sure Authorization Code Flow is Enabled. +# configure the client Access Type as "confidential" and take note of the client secret in the Credentials tab +# configure the redirect url to be "/auth_oauth/signin" + +In Odoo, create a new Oauth Provider with the following parameters: + +* Provider name: Keycloak (or any name you like that identify your keycloak + provider) +* Auth Flow: OpenID Connect (authorization code flow) +* Client ID: the same Client ID you entered when configuring the client in Keycloak +* Client Secret: found in keycloak on the client Credentials tab +* Allowed: yes +* Body: the link text to appear on the login page, such as Login with Keycloak +* Scope: openid email +* Authentication URL: The "authorization_endpoint" URL found in the + OpenID Endpoint Configuration of your Keycloak realm +* Token URL: The "token_endpoint" URL found in the + OpenID Endpoint Configuration of your Keycloak realm +* JWKS URL: The "jwks_uri" URL found in the + OpenID Endpoint Configuration of your Keycloak realm + +Usage +===== + +On the login page, click on the authentication provider you configured. +Known issues / Roadmap +====================== + +* When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link +* When doing a logout an extra option to also logout at the SSO provider. + +Changelog +========= + +13.0.1.0.0 2020-04-10 +~~~~~~~~~~~~~~~~~~~~~ + +* Odoo 13 migration, add authorization code flow. + +10.0.1.0.0 2018-10-05 +~~~~~~~~~~~~~~~~~~~~~ + +* Initial implementation + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= @@ -49,9 +142,37 @@ Credits Authors ~~~~~~~ -* ICTSTUDIO, André Schenkels +* ICTSTUDIO +* André Schenkels +* ACSONE SA/NV Contributors ~~~~~~~~~~~~ * Alexandre Fayolle +* Stéphane Bidoul + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-sbidoul| image:: https://github.com/sbidoul.png?size=40px + :target: https://github.com/sbidoul + :alt: sbidoul + +Current `maintainer `__: + +|maintainer-sbidoul| + +This module is part of the `OCA/server-auth `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index a2ff5cae5e..b30efbb0ca 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "13.0.1.0.0", + "version": "13.0.1.0.1", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, " diff --git a/auth_oidc/i18n/auth_oidc.pot b/auth_oidc/i18n/auth_oidc.pot new file mode 100644 index 0000000000..4150f1a674 --- /dev/null +++ b/auth_oidc/i18n/auth_oidc.pot @@ -0,0 +1,113 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * auth_oidc +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 13.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__flow +msgid "Auth Flow" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__client_secret +msgid "Client Secret" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__code_verifier +msgid "Code Verifier" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__jwks_uri +msgid "JWKS URL" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields.selection,name:auth_oidc.selection__auth_oauth_provider__flow__access_token +msgid "OAuth2" +msgstr "" + +#. module: auth_oidc +#: model:ir.model,name:auth_oidc.model_auth_oauth_provider +msgid "OAuth2 provider" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields.selection,name:auth_oidc.selection__auth_oauth_provider__flow__id_token_code +msgid "OpenID Connect (authorization code flow)" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields.selection,name:auth_oidc.selection__auth_oauth_provider__flow__id_token +msgid "OpenID Connect (implicit flow, not recommended)" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,help:auth_oidc.field_auth_oauth_provider__token_endpoint +msgid "Required for OpenID Connect authorization code flow." +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,help:auth_oidc.field_auth_oauth_provider__jwks_uri +msgid "Required for OpenID Connect." +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,help:auth_oidc.field_auth_oauth_provider__token_map +msgid "" +"Some Oauth providers don't map keys in their responses exactly as required." +" It is important to ensure user_id and email at least are mapped. For " +"OpenID Connect user_id is the sub key in the standard." +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__token_map +msgid "Token Map" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__token_endpoint +msgid "Token URL" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,help:auth_oidc.field_auth_oauth_provider__code_verifier +msgid "Used for PKCE." +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,help:auth_oidc.field_auth_oauth_provider__client_secret +msgid "" +"Used in OpenID Connect authorization code flow for confidential clients." +msgstr "" + +#. module: auth_oidc +#: model:ir.model,name:auth_oidc.model_res_users +msgid "Users" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__validation_endpoint +msgid "Validation URL" +msgstr "" + +#. module: auth_oidc +#: model_terms:ir.ui.view,arch_db:auth_oidc.view_oidc_provider_form +msgid "e.g from:to upn:email sub:user_id" +msgstr "" + +#. module: auth_oidc +#: model:auth.oauth.provider,body:auth_oidc.local_keycloak +msgid "keycloak:8080 on localhost" +msgstr "" diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html new file mode 100644 index 0000000000..a820198809 --- /dev/null +++ b/auth_oidc/static/description/index.html @@ -0,0 +1,526 @@ + + + + + + +Authentication OpenID Connect + + + +
+

Authentication OpenID Connect

+ + +

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

+

This module allows users to login through an OpenID Connect provider using the +authorization code flow or implicit flow.

+

Note the implicit flow is not recommended because it exposes access tokens to +the browser and in http logs.

+

Table of contents

+ +
+

Installation

+

This module depends on the python-jose +library, not to be confused with jose which is also available on PyPI.

+
+
+

Configuration

+
+

Setup for Microsoft Azure

+

Example configuration with OpenID Connect implicit flow. +This configuration is not recommended because it exposes the access token +to the client, and in logs.

+
+
# configure a new web application in Azure with OpenID and implicit flow (see
+
the provider documentation)
+
# in this application the redirect url must be be “<url of your
+
server>/auth_oauth/signin” and of course this URL should be reachable from +Azure
+
# create a new authentication provider in Odoo with the following
+
parameters (see the portal documentation +for more information):
+
+
    +
  • Provider Name: Azure
  • +
  • Auth Flow: OpenID Connect
  • +
  • Client ID: use the value of the OAuth2 autorization endoing (v2) from the Azure Endpoints list
  • +
  • Body: Azure SSO
  • +
  • Authentication URL: use the value of “OAuth2 autorization endpoint (v2)” from the Azure endpoints list
  • +
  • Scope: openid email
  • +
  • Validation URL: use the value of “OAuth2 token endpoint (v2)” from the Azure endpoints list
  • +
  • Allowed: yes
  • +
+
+
+

Setup for Keycloak

+

Example configuration with OpenID Connect authorization code flow.

+

In Keycloak:

+

# configure a new Client +# make sure Authorization Code Flow is Enabled. +# configure the client Access Type as “confidential” and take note of the client secret in the Credentials tab +# configure the redirect url to be “<url of your server>/auth_oauth/signin”

+

In Odoo, create a new Oauth Provider with the following parameters:

+
    +
  • Provider name: Keycloak (or any name you like that identify your keycloak +provider)
  • +
  • Auth Flow: OpenID Connect (authorization code flow)
  • +
  • Client ID: the same Client ID you entered when configuring the client in Keycloak
  • +
  • Client Secret: found in keycloak on the client Credentials tab
  • +
  • Allowed: yes
  • +
  • Body: the link text to appear on the login page, such as Login with Keycloak
  • +
  • Scope: openid email
  • +
  • Authentication URL: The “authorization_endpoint” URL found in the +OpenID Endpoint Configuration of your Keycloak realm
  • +
  • Token URL: The “token_endpoint” URL found in the +OpenID Endpoint Configuration of your Keycloak realm
  • +
  • JWKS URL: The “jwks_uri” URL found in the +OpenID Endpoint Configuration of your Keycloak realm
  • +
+
+
+
+

Usage

+

On the login page, click on the authentication provider you configured.

+
+
+

Known issues / Roadmap

+
    +
  • When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link
  • +
  • When doing a logout an extra option to also logout at the SSO provider.
  • +
+
+
+

Changelog

+
+

13.0.1.0.0 2020-04-10

+
    +
  • Odoo 13 migration, add authorization code flow.
  • +
+
+
+

10.0.1.0.0 2018-10-05

+
    +
  • Initial implementation
  • +
+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ICTSTUDIO
  • +
  • André Schenkels
  • +
  • ACSONE SA/NV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

sbidoul

+

This module is part of the OCA/server-auth project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + From f6ae2a48d303fc6b11be7d5d07f33310a9c6a48f Mon Sep 17 00:00:00 2001 From: Chafique Date: Fri, 10 Dec 2021 16:07:12 +0100 Subject: [PATCH 21/46] [MIG] auth_oidc: Migration to 14.0 --- auth_oidc/README.rst | 15 +++-- auth_oidc/__manifest__.py | 2 +- auth_oidc/i18n/auth_oidc.pot | 20 +++++- auth_oidc/readme/HISTORY.rst | 5 ++ auth_oidc/static/description/index.html | 71 +++++++++++---------- auth_oidc/tests/test_auth_oidc_auth_code.py | 3 +- 6 files changed, 76 insertions(+), 40 deletions(-) diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst index 932ffea7b0..04ab9a1a2e 100644 --- a/auth_oidc/README.rst +++ b/auth_oidc/README.rst @@ -14,13 +14,13 @@ Authentication OpenID Connect :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github - :target: https://github.com/OCA/server-auth/tree/13.0/auth_oidc + :target: https://github.com/OCA/server-auth/tree/14.0/auth_oidc :alt: OCA/server-auth .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-auth-13-0/server-auth-13-0-auth_oidc + :target: https://translation.odoo-community.org/projects/server-auth-14-0/server-auth-14-0-auth_oidc :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/251/13.0 + :target: https://runbot.odoo-community.org/runbot/251/14.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -116,6 +116,11 @@ Known issues / Roadmap Changelog ========= +14.0.1.0.0 2021-12-10 +~~~~~~~~~~~~~~~~~~~~~ + +* Odoo 14 migration + 13.0.1.0.0 2020-04-10 ~~~~~~~~~~~~~~~~~~~~~ @@ -132,7 +137,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -173,6 +178,6 @@ Current `maintainer `__: |maintainer-sbidoul| -This module is part of the `OCA/server-auth `_ project on GitHub. +This module is part of the `OCA/server-auth `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index b30efbb0ca..ebf94f0bbb 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "13.0.1.0.1", + "version": "14.0.1.0.1", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, " diff --git a/auth_oidc/i18n/auth_oidc.pot b/auth_oidc/i18n/auth_oidc.pot index 4150f1a674..a687a98816 100644 --- a/auth_oidc/i18n/auth_oidc.pot +++ b/auth_oidc/i18n/auth_oidc.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 13.0\n" +"Project-Id-Version: Odoo Server 14.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -28,11 +28,29 @@ msgstr "" msgid "Code Verifier" msgstr "" +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__display_name +#: model:ir.model.fields,field_description:auth_oidc.field_res_users__display_name +msgid "Display Name" +msgstr "" + +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__id +#: model:ir.model.fields,field_description:auth_oidc.field_res_users__id +msgid "ID" +msgstr "" + #. module: auth_oidc #: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__jwks_uri msgid "JWKS URL" msgstr "" +#. module: auth_oidc +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider____last_update +#: model:ir.model.fields,field_description:auth_oidc.field_res_users____last_update +msgid "Last Modified on" +msgstr "" + #. module: auth_oidc #: model:ir.model.fields.selection,name:auth_oidc.selection__auth_oauth_provider__flow__access_token msgid "OAuth2" diff --git a/auth_oidc/readme/HISTORY.rst b/auth_oidc/readme/HISTORY.rst index f831ce0e6d..33b336582e 100644 --- a/auth_oidc/readme/HISTORY.rst +++ b/auth_oidc/readme/HISTORY.rst @@ -1,3 +1,8 @@ +14.0.1.0.0 2021-12-10 +~~~~~~~~~~~~~~~~~~~~~ + +* Odoo 14 migration + 13.0.1.0.0 2020-04-10 ~~~~~~~~~~~~~~~~~~~~~ diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html index a820198809..c744fd4f07 100644 --- a/auth_oidc/static/description/index.html +++ b/auth_oidc/static/description/index.html @@ -367,7 +367,7 @@

Authentication OpenID Connect

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

This module allows users to login through an OpenID Connect provider using the authorization code flow or implicit flow.

Note the implicit flow is not recommended because it exposes access tokens to @@ -375,37 +375,38 @@

Authentication OpenID Connect

Table of contents

-

Installation

+

Installation

This module depends on the python-jose library, not to be confused with jose which is also available on PyPI.

-

Configuration

+

Configuration

-

Setup for Microsoft Azure

+

Setup for Microsoft Azure

Example configuration with OpenID Connect implicit flow. This configuration is not recommended because it exposes the access token to the client, and in logs.

@@ -431,7 +432,7 @@

Setup for Microsoft Azure

-

Setup for Keycloak

+

Setup for Keycloak

Example configuration with OpenID Connect authorization code flow.

In Keycloak:

# configure a new Client @@ -458,43 +459,49 @@

Setup for Keycloak

-

Usage

+

Usage

On the login page, click on the authentication provider you configured.

-

Known issues / Roadmap

+

Known issues / Roadmap

  • When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link
  • When doing a logout an extra option to also logout at the SSO provider.
-

Changelog

+

Changelog

-

13.0.1.0.0 2020-04-10

+

14.0.1.0.0 2021-12-10

    -
  • Odoo 13 migration, add authorization code flow.
  • +
  • Odoo 14 migration
-

10.0.1.0.0 2018-10-05

+

13.0.1.0.0 2020-04-10

+
    +
  • Odoo 13 migration, add authorization code flow.
  • +
+
+
+

10.0.1.0.0 2018-10-05

  • Initial implementation
-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • ICTSTUDIO
  • André Schenkels
  • @@ -502,14 +509,14 @@

    Authors

-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association

OCA, or the Odoo Community Association, is a nonprofit organization whose @@ -517,7 +524,7 @@

Maintainers

promote its widespread use.

Current maintainer:

sbidoul

-

This module is part of the OCA/server-auth project on GitHub.

+

This module is part of the OCA/server-auth project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

diff --git a/auth_oidc/tests/test_auth_oidc_auth_code.py b/auth_oidc/tests/test_auth_oidc_auth_code.py index 623fa4b2cf..f608d02dde 100644 --- a/auth_oidc/tests/test_auth_oidc_auth_code.py +++ b/auth_oidc/tests/test_auth_oidc_auth_code.py @@ -7,9 +7,10 @@ import odoo from odoo.tests import common -from odoo.addons.auth_oidc.controllers.main import OpenIDLogin from odoo.addons.website.tools import MockRequest as _MockRequest +from ..controllers.main import OpenIDLogin + BASE_URL = "http://localhost:%s" % odoo.tools.config["http_port"] From 0bb0e1ec31d9320f34c3d53468d95840c37fabef Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Mon, 14 Mar 2022 10:43:33 +0100 Subject: [PATCH 22/46] [FIX] auth_oidc: Fix werkzeug deprecated warning for url_encode, url decode --- auth_oidc/__manifest__.py | 2 +- auth_oidc/controllers/main.py | 6 +++--- auth_oidc/i18n/auth_oidc.pot | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index ebf94f0bbb..dcb25ec54f 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "14.0.1.0.1", + "version": "14.0.1.0.2", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, " diff --git a/auth_oidc/controllers/main.py b/auth_oidc/controllers/main.py index dbc7dac69c..0c0861e6da 100644 --- a/auth_oidc/controllers/main.py +++ b/auth_oidc/controllers/main.py @@ -7,7 +7,7 @@ import logging import secrets -import werkzeug.utils +from werkzeug.urls import url_decode, url_encode from odoo.addons.auth_oauth.controllers.main import OAuthLogin @@ -20,7 +20,7 @@ def list_providers(self): for provider in providers: flow = provider.get("flow") if flow in ("id_token", "id_token_code"): - params = werkzeug.url_decode(provider["auth_link"].split("?")[-1]) + params = url_decode(provider["auth_link"].split("?")[-1]) # nonce params["nonce"] = secrets.token_urlsafe() # response_type @@ -45,6 +45,6 @@ def list_providers(self): params["scope"] = provider["scope"] # auth link that the user will click provider["auth_link"] = "{}?{}".format( - provider["auth_endpoint"], werkzeug.url_encode(params) + provider["auth_endpoint"], url_encode(params) ) return providers diff --git a/auth_oidc/i18n/auth_oidc.pot b/auth_oidc/i18n/auth_oidc.pot index a687a98816..256f1ca81b 100644 --- a/auth_oidc/i18n/auth_oidc.pot +++ b/auth_oidc/i18n/auth_oidc.pot @@ -111,13 +111,13 @@ msgid "" msgstr "" #. module: auth_oidc -#: model:ir.model,name:auth_oidc.model_res_users -msgid "Users" +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__validation_endpoint +msgid "UserInfo URL" msgstr "" #. module: auth_oidc -#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__validation_endpoint -msgid "Validation URL" +#: model:ir.model,name:auth_oidc.model_res_users +msgid "Users" msgstr "" #. module: auth_oidc From dc8f4510d62601580d243dfe191ab5b4c44cb060 Mon Sep 17 00:00:00 2001 From: Karl Southern Date: Thu, 7 Jul 2022 12:07:09 +0100 Subject: [PATCH 23/46] [MIG] auth_oidc: Migration to 15.0 --- auth_oidc/README.rst | 10 +++++----- auth_oidc/__manifest__.py | 2 +- auth_oidc/i18n/auth_oidc.pot | 20 +------------------- auth_oidc/static/description/index.html | 6 +++--- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst index 04ab9a1a2e..3c6265151c 100644 --- a/auth_oidc/README.rst +++ b/auth_oidc/README.rst @@ -14,13 +14,13 @@ Authentication OpenID Connect :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github - :target: https://github.com/OCA/server-auth/tree/14.0/auth_oidc + :target: https://github.com/OCA/server-auth/tree/15.0/auth_oidc :alt: OCA/server-auth .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-auth-14-0/server-auth-14-0-auth_oidc + :target: https://translation.odoo-community.org/projects/server-auth-15-0/server-auth-15-0-auth_oidc :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/251/14.0 + :target: https://runbot.odoo-community.org/runbot/251/15.0 :alt: Try me on Runbot |badge1| |badge2| |badge3| |badge4| |badge5| @@ -137,7 +137,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -178,6 +178,6 @@ Current `maintainer `__: |maintainer-sbidoul| -This module is part of the `OCA/server-auth `_ project on GitHub. +This module is part of the `OCA/server-auth `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index dcb25ec54f..cdbf492ec0 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "14.0.1.0.2", + "version": "15.0.1.0.0", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, " diff --git a/auth_oidc/i18n/auth_oidc.pot b/auth_oidc/i18n/auth_oidc.pot index 256f1ca81b..a6146631d4 100644 --- a/auth_oidc/i18n/auth_oidc.pot +++ b/auth_oidc/i18n/auth_oidc.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 14.0\n" +"Project-Id-Version: Odoo Server 15.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -28,29 +28,11 @@ msgstr "" msgid "Code Verifier" msgstr "" -#. module: auth_oidc -#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__display_name -#: model:ir.model.fields,field_description:auth_oidc.field_res_users__display_name -msgid "Display Name" -msgstr "" - -#. module: auth_oidc -#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__id -#: model:ir.model.fields,field_description:auth_oidc.field_res_users__id -msgid "ID" -msgstr "" - #. module: auth_oidc #: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__jwks_uri msgid "JWKS URL" msgstr "" -#. module: auth_oidc -#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider____last_update -#: model:ir.model.fields,field_description:auth_oidc.field_res_users____last_update -msgid "Last Modified on" -msgstr "" - #. module: auth_oidc #: model:ir.model.fields.selection,name:auth_oidc.selection__auth_oauth_provider__flow__access_token msgid "OAuth2" diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html index c744fd4f07..737081829c 100644 --- a/auth_oidc/static/description/index.html +++ b/auth_oidc/static/description/index.html @@ -367,7 +367,7 @@

Authentication OpenID Connect

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runbot

This module allows users to login through an OpenID Connect provider using the authorization code flow or implicit flow.

Note the implicit flow is not recommended because it exposes access tokens to @@ -495,7 +495,7 @@

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed -feedback.

+feedback.

Do not contact contributors directly about support or help with technical issues.

@@ -524,7 +524,7 @@

Maintainers

promote its widespread use.

Current maintainer:

sbidoul

-

This module is part of the OCA/server-auth project on GitHub.

+

This module is part of the OCA/server-auth project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

From fe89a44ebadc3eda37259716077091cb6699e964 Mon Sep 17 00:00:00 2001 From: djaen Date: Fri, 27 Jan 2023 17:54:08 +0100 Subject: [PATCH 24/46] [16.0][MIG] auth_oidc: Migration to 16.0 --- auth_oidc/README.rst | 24 ++++--- auth_oidc/__manifest__.py | 2 +- auth_oidc/i18n/auth_oidc.pot | 10 +-- auth_oidc/models/auth_oauth_provider.py | 2 +- auth_oidc/models/res_users.py | 1 + auth_oidc/readme/CONTRIBUTORS.rst | 1 + auth_oidc/static/description/index.html | 87 +++++++++++++------------ 7 files changed, 68 insertions(+), 59 deletions(-) diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst index 3c6265151c..6cafbe6c07 100644 --- a/auth_oidc/README.rst +++ b/auth_oidc/README.rst @@ -2,10 +2,13 @@ Authentication OpenID Connect ============================= -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:0e77943e35a7d7c6fb3b6f9e5753d5870e6023f5614e17f7bc0c32522086c49a + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -14,16 +17,16 @@ Authentication OpenID Connect :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--auth-lightgray.png?logo=github - :target: https://github.com/OCA/server-auth/tree/15.0/auth_oidc + :target: https://github.com/OCA/server-auth/tree/16.0/auth_oidc :alt: OCA/server-auth .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/server-auth-15-0/server-auth-15-0-auth_oidc + :target: https://translation.odoo-community.org/projects/server-auth-16-0/server-auth-16-0-auth_oidc :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/251/15.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-auth&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module allows users to login through an OpenID Connect provider using the authorization code flow or implicit flow. @@ -136,8 +139,8 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed -`feedback `_. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -156,6 +159,7 @@ Contributors * Alexandre Fayolle * Stéphane Bidoul +* David Jaen Maintainers ~~~~~~~~~~~ @@ -178,6 +182,6 @@ Current `maintainer `__: |maintainer-sbidoul| -This module is part of the `OCA/server-auth `_ project on GitHub. +This module is part of the `OCA/server-auth `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index cdbf492ec0..845d7a27db 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "15.0.1.0.0", + "version": "16.0.1.0.0", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, " diff --git a/auth_oidc/i18n/auth_oidc.pot b/auth_oidc/i18n/auth_oidc.pot index a6146631d4..0dc7d5e576 100644 --- a/auth_oidc/i18n/auth_oidc.pot +++ b/auth_oidc/i18n/auth_oidc.pot @@ -4,7 +4,7 @@ # msgid "" msgstr "" -"Project-Id-Version: Odoo Server 15.0\n" +"Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" "Last-Translator: \n" "Language-Team: \n" @@ -93,13 +93,13 @@ msgid "" msgstr "" #. module: auth_oidc -#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__validation_endpoint -msgid "UserInfo URL" +#: model:ir.model,name:auth_oidc.model_res_users +msgid "User" msgstr "" #. module: auth_oidc -#: model:ir.model,name:auth_oidc.model_res_users -msgid "Users" +#: model:ir.model.fields,field_description:auth_oidc.field_auth_oauth_provider__validation_endpoint +msgid "UserInfo URL" msgstr "" #. module: auth_oidc diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 6a40e87ed6..b969fa3e8d 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -48,7 +48,7 @@ class AuthOauthProvider(models.Model): @tools.ormcache("self.jwks_uri", "kid") def _get_key(self, kid): - r = requests.get(self.jwks_uri) + r = requests.get(self.jwks_uri, timeout=10) r.raise_for_status() response = r.json() for key in response["keys"]: diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index c487504e2a..a1be73ec88 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -37,6 +37,7 @@ def _auth_oauth_get_tokens_auth_code_flow(self, oauth_provider, params): redirect_uri=request.httprequest.url_root + "auth_oauth/signin", ), auth=auth, + timeout=10, ) response.raise_for_status() response_json = response.json() diff --git a/auth_oidc/readme/CONTRIBUTORS.rst b/auth_oidc/readme/CONTRIBUTORS.rst index d721552d86..303011adb2 100644 --- a/auth_oidc/readme/CONTRIBUTORS.rst +++ b/auth_oidc/readme/CONTRIBUTORS.rst @@ -1,2 +1,3 @@ * Alexandre Fayolle * Stéphane Bidoul +* David Jaen diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html index 737081829c..384f6ddcff 100644 --- a/auth_oidc/static/description/index.html +++ b/auth_oidc/static/description/index.html @@ -1,20 +1,20 @@ - + - + Authentication OpenID Connect -
-

Authentication OpenID Connect

+
+ + +Odoo Community Association + +
+

Authentication OpenID Connect

-

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/server-auth Translate me on Weblate Try me on Runboat

This module allows users to login through an OpenID Connect provider using the authorization code flow or implicit flow.

Note the implicit flow is not recommended because it exposes access @@ -408,15 +413,15 @@

Authentication OpenID Connect

-

Installation

+

Installation

This module depends on the python-jose library, not to be confused with jose which is also available on PyPI.

-

Configuration

+

Configuration

-

Setup for Microsoft Azure

+

Setup for Microsoft Azure

Example configuration with OpenID Connect authorization code flow.

  1. configure a new web application in Azure with OpenID and code flow @@ -430,8 +435,8 @@

    Setup for Microsoft Azure

    for more information):
-

image

-

image1

+

image

+

image1

Single tenant provider limits the access to user of your tenant, while Multitenants allow access for all AzureAD users, so user of foreign companies can use their AzureAD login without an guest account.

@@ -449,10 +454,10 @@

Setup for Microsoft Azure

Allowed: yes
  • replace {tenant_id} in urls with your Azure tenant id
  • -

    image2

    +

    image2

    -

    Setup for Keycloak

    +

    Setup for Keycloak

    Example configuration with OpenID Connect authorization code flow.

    In Keycloak:

      @@ -485,11 +490,11 @@

      Setup for Keycloak

    -

    Usage

    +

    Usage

    On the login page, click on the authentication provider you configured.

    -

    Known issues / Roadmap

    +

    Known issues / Roadmap

    • When going to the login screen, check for a existing token and do a direct login without the clicking on the SSO link
    • @@ -498,80 +503,80 @@

      Known issues / Roadmap

    -

    Changelog

    +

    Changelog

    -

    16.0.1.1.0 2024-02-28

    +

    16.0.1.1.0 2024-02-28

    • Forward port OpenID Connect fixes from 15.0 to 16.0
    -

    16.0.1.0.1 2023-10-09

    +

    16.0.1.0.1 2023-10-09

    • Add AzureAD code flow provider
    -

    13.0.1.0.0 2020-04-10

    +

    13.0.1.0.0 2020-04-10

    • Odoo 13 migration, add authorization code flow.
    -

    10.0.1.0.0 2018-10-05

    +

    10.0.1.0.0 2018-10-05

    • Initial implementation
    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

    +feedback.

    Do not contact contributors directly about support or help with technical issues.

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • ICTSTUDIO
    • André Schenkels
    • @@ -579,7 +584,7 @@

      Authors

    -

    Contributors

    +

    Contributors

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association @@ -598,10 +603,11 @@

    Maintainers

    promote its widespread use.

    Current maintainer:

    sbidoul

    -

    This module is part of the OCA/server-auth project on GitHub.

    +

    This module is part of the OCA/server-auth project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    +
    diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..13c8fbb434 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +# generated from manifests external_dependencies +python-jose From 554b50d864d7b684d3053a25b2e21a80c5e96acd Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Wed, 1 Oct 2025 13:19:28 +0200 Subject: [PATCH 46/46] [MIG] auth_oidc: Migration to 19.0 --- auth_oidc/README.rst | 5 ++ auth_oidc/__manifest__.py | 2 +- auth_oidc/demo/local_keycloak.xml | 2 +- auth_oidc/readme/HISTORY.md | 4 + auth_oidc/static/description/index.html | 85 +++++++++++---------- auth_oidc/tests/test_auth_oidc_auth_code.py | 25 ++++-- test-requirements.txt | 1 + 7 files changed, 78 insertions(+), 46 deletions(-) create mode 100644 test-requirements.txt diff --git a/auth_oidc/README.rst b/auth_oidc/README.rst index ba90958edc..c042a56932 100644 --- a/auth_oidc/README.rst +++ b/auth_oidc/README.rst @@ -147,6 +147,11 @@ Known issues / Roadmap Changelog ========= +19.0.1.0.0 2025-10-01 +--------------------- + +- Odoo 19 migration + 18.0.1.0.0 2024-10-09 --------------------- diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index 707cc792df..0671c99136 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Authentication OpenID Connect", - "version": "18.0.1.0.0", + "version": "19.0.1.0.0", "license": "AGPL-3", "author": ( "ICTSTUDIO, André Schenkels, ACSONE SA/NV, Odoo Community Association (OCA)" diff --git a/auth_oidc/demo/local_keycloak.xml b/auth_oidc/demo/local_keycloak.xml index 919754db99..fe1ebe1ba8 100644 --- a/auth_oidc/demo/local_keycloak.xml +++ b/auth_oidc/demo/local_keycloak.xml @@ -2,7 +2,7 @@ keycloak:8080 on localhost id_token_code - auth_oidc-test + auth_oidc-keycloak preferred_username:user_id keycloak:8080 on localhost diff --git a/auth_oidc/readme/HISTORY.md b/auth_oidc/readme/HISTORY.md index 48ad92f7f1..cff42d2e3c 100644 --- a/auth_oidc/readme/HISTORY.md +++ b/auth_oidc/readme/HISTORY.md @@ -1,3 +1,7 @@ +## 19.0.1.0.0 2025-10-01 + +- Odoo 19 migration + ## 18.0.1.0.0 2024-10-09 - Odoo 18 migration diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html index 3fee687da0..f1550a6839 100644 --- a/auth_oidc/static/description/index.html +++ b/auth_oidc/static/description/index.html @@ -391,23 +391,24 @@

    Authentication OpenID Connect

  • Usage
  • Known issues / Roadmap
  • Changelog
  • -
  • Bug Tracker
  • -
  • Credits @@ -505,68 +506,74 @@

    Known issues / Roadmap

    Changelog

    -

    18.0.1.0.0 2024-10-09

    +

    19.0.1.0.0 2025-10-01

      -
    • Odoo 18 migration
    • +
    • Odoo 19 migration
    -

    17.0.1.0.0 2024-03-20

    +

    18.0.1.0.0 2024-10-09

      -
    • Odoo 17 migration
    • +
    • Odoo 18 migration
    -

    16.0.1.1.0 2024-02-28

    +

    17.0.1.0.0 2024-03-20

      -
    • Forward port OpenID Connect fixes from 15.0 to 16.0
    • +
    • Odoo 17 migration
    -

    16.0.1.0.2 2023-11-16

    +

    16.0.1.1.0 2024-02-28

      -
    • Readme link updates
    • +
    • Forward port OpenID Connect fixes from 15.0 to 16.0
    -

    16.0.1.0.1 2023-10-09

    +

    16.0.1.0.2 2023-11-16

      -
    • Add AzureAD code flow provider
    • +
    • Readme link updates
    -

    16.0.1.0.0 2023-01-27

    +

    16.0.1.0.1 2023-10-09

      -
    • Odoo 16 migration
    • +
    • Add AzureAD code flow provider
    -

    15.0.1.0.0 2023-01-06

    +

    16.0.1.0.0 2023-01-27

      -
    • Odoo 15 migration
    • +
    • Odoo 16 migration
    -

    14.0.1.0.0 2021-12-10

    +

    15.0.1.0.0 2023-01-06

      -
    • Odoo 14 migration
    • +
    • Odoo 15 migration
    -

    13.0.1.0.0 2020-04-10

    +

    14.0.1.0.0 2021-12-10

      -
    • Odoo 13 migration, add authorization code flow.
    • +
    • Odoo 14 migration
    -

    10.0.1.0.0 2018-10-05

    +

    13.0.1.0.0 2020-04-10

    +
      +
    • Odoo 13 migration, add authorization code flow.
    • +
    +
    +
    +

    10.0.1.0.0 2018-10-05

    • Initial implementation
    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -574,9 +581,9 @@

    Bug Tracker

    Do not contact contributors directly about support or help with technical issues.

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • ICTSTUDIO
    • André Schenkels
    • @@ -584,7 +591,7 @@

      Authors

    -

    Contributors

    +

    Contributors

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    Odoo Community Association diff --git a/auth_oidc/tests/test_auth_oidc_auth_code.py b/auth_oidc/tests/test_auth_oidc_auth_code.py index 27da38c779..40d2170b5f 100644 --- a/auth_oidc/tests/test_auth_oidc_auth_code.py +++ b/auth_oidc/tests/test_auth_oidc_auth_code.py @@ -17,7 +17,7 @@ from odoo.exceptions import AccessDenied from odoo.tests import common -from odoo.addons.website.tools import MockRequest as _MockRequest +from odoo.addons.http_routing.tests.common import MockRequest as _MockRequest from ..controllers.main import OpenIDLogin @@ -70,9 +70,19 @@ def _generate_key(): def setUp(self): super().setUp() - # search our test provider and bind the demo user to it - self.provider_rec = self.env["auth.oauth.provider"].search( - [("client_id", "=", "auth_oidc-test")] + # set up our test provider to bind the test user to it + self.provider_rec = self.env["auth.oauth.provider"].create( + { + "name": "OAuth Provider for TestAuthOIDCAuthorizationCodeFlow", + "client_id": "auth_oidc-test", + "enabled": True, + "body": "Config of an oauth provider for tests", + "scope": "openid email", + "flow": "id_token_code", + "auth_endpoint": "http://localhost:8080/auth/realms/master/protocol/openid-connect/auth", + "token_endpoint": "http://localhost:8080/auth/realms/master/protocol/openid-connect/token", + "jwks_uri": "http://localhost:8080/auth/realms/master/protocol/openid-connect/certs", + } ) self.assertEqual(len(self.provider_rec), 1) @@ -98,7 +108,12 @@ def test_auth_link(self): self.assertEqual(params["redirect_uri"], [BASE_URL + "/auth_oauth/signin"]) def _prepare_login_test_user(self): - user = self.env.ref("base.user_demo") + user = self.env["res.users"].create( + { + "login": "auth_oidc_test_user", + "name": "Auth OIDC Test User", + } + ) user.write({"oauth_provider_id": self.provider_rec.id, "oauth_uid": user.login}) return user diff --git a/test-requirements.txt b/test-requirements.txt new file mode 100644 index 0000000000..2cb24f43db --- /dev/null +++ b/test-requirements.txt @@ -0,0 +1 @@ +responses