diff --git a/config_app/js/core-config-setup/config-setup-tool.html b/config_app/js/core-config-setup/config-setup-tool.html index 9780f85de..f043fe9fc 100644 --- a/config_app/js/core-config-setup/config-setup-tool.html +++ b/config_app/js/core-config-setup/config-setup-tool.html @@ -1201,6 +1201,32 @@ + + Verified E-mail Address Claim (optional): + + + +
+ If specified, the claim in the User Information JWT that contains the verified e-mail address for the user. +
+ + + + Preferred Username Claim (optional): + + + +
+ If specified, the claim in the User Information JWT that contains the preferred username for the user. +
+ + Binding Field: diff --git a/endpoints/oauth/login.py b/endpoints/oauth/login.py index 2a0ade3af..3987b2fea 100644 --- a/endpoints/oauth/login.py +++ b/endpoints/oauth/login.py @@ -182,12 +182,12 @@ def _register_service(login_service): @oauthlogin_csrf_protect def callback_func(): # Check for a callback error. - error = request.args.get('error', None) + error = request.values.get('error', None) if error: return _render_ologin_error(login_service.service_name(), error) # Exchange the OAuth code for login information. - code = request.args.get('code') + code = request.values.get('code') try: lid, lusername, lemail = login_service.exchange_code_for_login(app.config, client, code, '') except OAuthLoginException as ole: @@ -217,12 +217,12 @@ def _register_service(login_service): @oauthlogin_csrf_protect def attach_func(): # Check for a callback error. - error = request.args.get('error', None) + error = request.values.get('error', None) if error: return _render_ologin_error(login_service.service_name(), error) # Exchange the OAuth code for login information. - code = request.args.get('code') + code = request.values.get('code') try: lid, lusername, _ = login_service.exchange_code_for_login(app.config, client, code, '/attach') except OAuthLoginException as ole: @@ -258,12 +258,12 @@ def _register_service(login_service): @oauthlogin_csrf_protect def cli_token_func(): # Check for a callback error. - error = request.args.get('error', None) + error = request.values.get('error', None) if error: return _render_ologin_error(login_service.service_name(), error) # Exchange the OAuth code for the ID token. - code = request.args.get('code') + code = request.values.get('code') try: idtoken, _ = login_service.exchange_code_for_tokens(app.config, client, code, '/cli') except OAuthLoginException as ole: @@ -281,17 +281,17 @@ def _register_service(login_service): oauthlogin.add_url_rule('/%s/callback' % login_service.service_id(), '%s_oauth_callback' % login_service.service_id(), callback_func, - methods=['GET']) + methods=['GET', 'POST']) oauthlogin.add_url_rule('/%s/callback/attach' % login_service.service_id(), '%s_oauth_attach' % login_service.service_id(), attach_func, - methods=['GET']) + methods=['GET', 'POST']) oauthlogin.add_url_rule('/%s/callback/cli' % login_service.service_id(), '%s_oauth_cli' % login_service.service_id(), cli_token_func, - methods=['GET']) + methods=['GET', 'POST']) # Register the routes for each of the login services. for current_service in oauth_login.services: diff --git a/oauth/oidc.py b/oauth/oidc.py index 892929b29..6abe6496c 100644 --- a/oauth/oidc.py +++ b/oauth/oidc.py @@ -167,13 +167,32 @@ class OIDCLoginService(OAuthService): raise OAuthLoginException('Mismatch in `sub` returned by OIDC user info endpoint') # Check if we have a verified email address. - email_address = user_info.get('email') if user_info.get('email_verified') else None + if self.config.get('VERIFIED_EMAIL_CLAIM_NAME'): + email_address = user_info.get(self.config['VERIFIED_EMAIL_CLAIM_NAME']) + else: + email_address = user_info.get('email') if user_info.get('email_verified') else None + + logger.debug('Found e-mail address `%s` for sub `%s`', email_address, user_info['sub']) if self._mailing: if email_address is None: raise OAuthLoginException('A verified email address is required to login with this service') # Check for a preferred username. - lusername = user_info.get('preferred_username') or user_info.get('sub') + if self.config.get('PREFERRED_USERNAME_CLAIM_NAME'): + lusername = user_info.get(self.config['PREFERRED_USERNAME_CLAIM_NAME']) + else: + lusername = user_info.get('preferred_username') + if lusername is None: + # Note: Active Directory provides `unique_name` and `upn`. + # https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-id-and-access-tokens + lusername = user_info.get('unique_name', user_info.get('upn')) + + if lusername is None: + lusername = user_info['sub'] + + if lusername.find('@') >= 0: + lusername = lusername[0:lusername.find('@')] + return decoded_id_token['sub'], lusername, email_address @property diff --git a/oauth/test/test_oidc.py b/oauth/test/test_oidc.py index 1309060e3..3ab184593 100644 --- a/oauth/test/test_oidc.py +++ b/oauth/test/test_oidc.py @@ -51,7 +51,7 @@ def email_verified(request): def userinfo_supported(request): return request.param -@pytest.fixture(params=["someusername", None]) +@pytest.fixture(params=["someusername", "foo@bar.com", None]) def preferred_username(request): return request.param @@ -334,6 +334,9 @@ def test_exchange_code_validcode(oidc_service, discovery_handler, app_config, ht assert lemail is None if preferred_username is not None: + if preferred_username.find('@') >= 0: + preferred_username = preferred_username[0:preferred_username.find('@')] + assert lusername == preferred_username else: assert lusername == lid