By: Jan Mashat user 11 Jun 2019 at 9:20 a.m. CDT

8 Responses
Jan Mashat gravatar
This morning I refreshed my gluu homepage (/identity/home) - but the administration items in the sidebar were gone and then I noticed that - to my surprise - I was logged in as a different user. It may have been coincidence, but the user I was logged in as happens to be the first entry we have in the user listing (/identity/person/personInventory.htm) We have U2F enabled as per https://gluu.org/docs/ce/authn-guide/U2F/ so it's impossible for me to have "accidentally" logged in as the other user. I didn't investigate further at the time, but when I reloaded the homepage one hour later, I was logged in as the correct user. Checking the logs, I noticed the following in `/opt/gluu-server-3.1.6/opt/gluu/jetty/identity/logs/oxtrust.log` ``` 2019-06-11 05:05:54,950 INFO [qtp804611486-447063] [org.gluu.oxtrust.action.Authenticator] (Authenticator.java:116) - Authenticating user 'null' 2019-06-11 05:05:54,975 INFO [qtp804611486-447063] [org.gluu.oxtrust.action.Authenticator] (Authenticator.java:131) - User 'null' authenticated successfully 2019-06-11 05:05:55,060 INFO [qtp804611486-449599] [org.gluu.oxtrust.ldap.service.OrganizationService] (OrganizationService.java:231) - Starting App version 3.1.6.Final 2019-06-11 05:05:57,838 INFO [qtp804611486-450725] [org.gluu.oxtrust.ldap.service.OrganizationService] (OrganizationService.java:231) - Starting App version 3.1.6.Final ``` Those are the only 2 entries containing `null` in our identity/logs folder, but I did find 761 matches for `Authentication success for User: 'null'` in oxauth/logs going as far back as April 15th (we installed gluu on April 11th)...for example in `/opt/gluu-server-3.1.6/opt/gluu/jetty/oxauth/logs/oxauth.log` (sensitive data truncated) ``` 2019-06-11 07:01:14,927 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:533) - Attempting to redirect user: SessionUser: SessionState {dn='oxAuthSessionId=ebfd4dad-...-8c02299eea12,ou=session,o=@!6D74.52B0.0D98...0CFB,o=gluu', id='ebfd4dad-...-8c02299eea12', lastUsedAt=Tue Jun 11 07:01:14 UTC 2019, userDn='inum=@!6D74.52B0.0D98...3244.0241.D1B3,ou=people,o=@!6D74.52B0.0D98...0CFB,o=gluu', authenticationTime=Tue Jun 11 07:01:14 UTC 2019, state=authenticated, sessionState='02cb...ab5e.0fdb700e-...-f35ffdf38b02', permissionGranted=null, isJwt=false, jwt=null, permissionGrantedMap=org.xdi.oxauth.model.common.SessionIdAccessMap@10b988f5, involvedClients=null, sessionAttributes={session_custom_state=approved, auth_external_attributes=null, opbs=b8aea6fc-...-b74f34a76ede, response_type=code, oxpush2_u2f_device_enroll=false, oxpush2_u2f_device_id=15...56, nonce=5193b337-...-050a288b951d, client_id=@!6D74.52B0.0D98...EDE0, auth_step_passed_1=true, auth_step=2, acr=u2f_opt, remote_ip=1.0.0.1, 10.4.4.4, auth_user=jan, scope=openid profile email user_name, acr_values=u2f_opt, oxpush2_u2f_device_user_inum=@!6D74.52B0.0D98...3244.0241.D1B3, redirect_uri=https://idp...com/identity/authentication/getauthcode, state=ccf8237b-...-c16fe80f14b8, oxpush2_u2f_device_one_step=false}, persisted=true} 2019-06-11 07:01:14,930 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:541) - Attempting to redirect user: User: org.xdi.oxauth.model.common.User@368b2e08 2019-06-11 07:01:14,931 INFO [qtp804611486-115363] [org.xdi.oxauth.auth.Authenticator] (Authenticator.java:409) - Authentication success for User: 'null' ``` We do have a `u2f_opt` custom script to allow bypassing U2F - but that was added in mid-May (so a month after the `null` messages started appearing in oxauth/logs): ``` # oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. # Copyright (c) 2016, Gluu # # Author: Yuriy Movchan # import java import sys from javax.ws.rs.core import Response from org.jboss.resteasy.client import ClientResponseFailure from org.jboss.resteasy.client.exception import ResteasyClientException from org.xdi.model.custom.script.type.auth import PersonAuthenticationType from org.xdi.oxauth.client.fido.u2f import FidoU2fClientFactory from org.xdi.oxauth.model.config import Constants from org.xdi.oxauth.security import Identity from org.xdi.oxauth.service import UserService, AuthenticationService, SessionIdService from org.xdi.oxauth.service.fido.u2f import DeviceRegistrationService from org.xdi.oxauth.util import ServerUtil from org.xdi.service.cdi.util import CdiUtil from org.xdi.util import StringHelper class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis def checkNo2FA(self, user): roles = user.getAttribute("role", True) if roles is not None: if isinstance(roles, unicode): if roles == "no2fa": return True else: for i in range(roles.length()): if roles.getString(i) == "no2fa": return True return False def init(self, configurationAttributes): print "U2F. Initialization" print "U2F. Initialization. Downloading U2F metadata" u2f_server_uri = configurationAttributes.get("u2f_server_uri").getValue2() u2f_server_metadata_uri = u2f_server_uri + "/.well-known/fido-u2f-configuration" metaDataConfigurationService = FidoU2fClientFactory.instance().createMetaDataConfigurationService( u2f_server_metadata_uri) max_attempts = 20 for attempt in range(1, max_attempts + 1): try: self.metaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration() break except ClientResponseFailure, ex: # Detect if last try or we still get Service Unavailable HTTP error if (attempt == max_attempts) or ( ex.getResponse().getResponseStatus() != Response.Status.SERVICE_UNAVAILABLE): raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt except ResteasyClientException, ex: # Detect if last try or we still get Service Unavailable HTTP error if attempt == max_attempts: raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt print "U2F. Initialized successfully" return True def destroy(self, configurationAttributes): print "U2F. Destroy" print "U2F. Destroyed successfully" return True def getApiVersion(self): return 1 def isValidAuthenticationMethod(self, usageType, configurationAttributes): return True def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): return None def authenticate(self, configurationAttributes, requestParameters, step): authenticationService = CdiUtil.bean(AuthenticationService) identity = CdiUtil.bean(Identity) credentials = identity.getCredentials() user_name = credentials.getUsername() if step == 1: print "U2F. Authenticate for step 1" user_password = credentials.getPassword() logged_in = False if StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password): userService = CdiUtil.bean(UserService) logged_in = authenticationService.authenticate(user_name, user_password) if not logged_in: return False if self.checkNo2FA(authenticationService.getAuthenticatedUser()): identity.setWorkingParameter("no2fa", True) return True elif step == 2: print "U2F. Authenticate for step 2" token_response = ServerUtil.getFirstValue(requestParameters, "tokenResponse") if token_response is None: print "U2F. Authenticate for step 2. tokenResponse is empty" return False auth_method = ServerUtil.getFirstValue(requestParameters, "authMethod") if auth_method is None: print "U2F. Authenticate for step 2. authMethod is empty" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if user is None: print "U2F. Prepare for step 2. Failed to determine user name" return False if auth_method == 'authenticate': print "U2F. Prepare for step 2. Call FIDO U2F in order to finish authentication workflow" authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService( self.metaDataConfiguration) authenticationStatus = authenticationRequestService.finishAuthentication(user.getUserId(), token_response) if authenticationStatus.getStatus() != Constants.RESULT_SUCCESS: print "U2F. Authenticate for step 2. Get invalid authentication status from FIDO U2F server" return False return True elif auth_method == 'enroll': print "U2F. Prepare for step 2. Call FIDO U2F in order to finish registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService( self.metaDataConfiguration) registrationStatus = registrationRequestService.finishRegistration(user.getUserId(), token_response) if (registrationStatus.getStatus() != Constants.RESULT_SUCCESS): print "U2F. Authenticate for step 2. Get invalid registration status from FIDO U2F server" return False return True else: print "U2F. Prepare for step 2. Authenticatiod method is invalid" return False else: return False def prepareForStep(self, configurationAttributes, requestParameters, step): identity = CdiUtil.bean(Identity) if step == 1: return True elif step == 2: print "U2F. Prepare for step 2" session_id = CdiUtil.bean(SessionIdService).getSessionIdFromCookie() if StringHelper.isEmpty(session_id): print "U2F. Prepare for step 2. Failed to determine session_id" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if user is None: print "U2F. Prepare for step 2. Failed to determine user name" return False u2f_application_id = configurationAttributes.get("u2f_application_id").getValue2() # Check if user have registered devices deviceRegistrationService = CdiUtil.bean(DeviceRegistrationService) userInum = user.getAttribute("inum") registrationRequest = None authenticationRequest = None deviceRegistrations = deviceRegistrationService.findUserDeviceRegistrations(userInum, u2f_application_id) if deviceRegistrations.size() > 0: print "U2F. Prepare for step 2. Call FIDO U2F in order to start authentication workflow" try: authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService( self.metaDataConfiguration) authenticationRequest = authenticationRequestService.startAuthentication(user.getUserId(), None, u2f_application_id, session_id) except ClientResponseFailure, ex: if ex.getResponse().getResponseStatus() != Response.Status.NOT_FOUND: print "U2F. Prepare for step 2. Failed to start authentication workflow. Exception:", \ sys.exc_info()[1] return False else: print "U2F. Prepare for step 2. Call FIDO U2F in order to start registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService( self.metaDataConfiguration) registrationRequest = registrationRequestService.startRegistration(user.getUserId(), u2f_application_id, session_id) identity.setWorkingParameter("fido_u2f_authentication_request", ServerUtil.asJson(authenticationRequest)) identity.setWorkingParameter("fido_u2f_registration_request", ServerUtil.asJson(registrationRequest)) return True elif step == 3: print "U2F. Prepare for step 3" return True else: return False def getExtraParametersForStep(self, configurationAttributes, step): return None def getCountAuthenticationSteps(self, configurationAttributes): identity = CdiUtil.bean(Identity) if identity.isSetWorkingParameter("no2fa"): return 1 return 2 def getPageForStep(self, configurationAttributes, step): if step == 2: return "/auth/u2f/login.xhtml" return "" def logout(self, configurationAttributes, requestParameters): return True ``` Thanks in advance for any help!

By William Lowe staff 11 Jun 2019 at 9:30 a.m. CDT

William Lowe gravatar
It sounds like [this issue](https://gluu.org/docs/ce/upgrade/patches/#oxtrust-unauthorized-access), which we released a patch for a few weeks ago. Thanks, Will

By Jan Mashat user 11 Jun 2019 at 12:45 p.m. CDT

Jan Mashat gravatar
Hi Will, Regarding the issue you've linked and the issue I've experienced... The end result seems to be the same: > created an unauthorized session for the first user on that list They may share the same underlying cause: > without meaningful session context However there are 2 major differences between the issues: 1. I didn't navigate to a `finishlogin` page, I simply reloaded `identity/home` 2. I was already logged into my own account when I temporarily saw the other user's profile, and then 1 hour later when I reloaded again I was back in my own profile. Is it possible that Gluu was restarting at the moment I reloaded the page, and therefore couldn't retrieve the session context? Thanks, Jan

By Aliaksandr Samuseu staff 11 Jun 2019 at 1:54 p.m. CDT

Aliaksandr Samuseu gravatar
Hi, Jan. Could you check what authentication methods you have set as default methods at "Configuration -> Manage authentication -> Default Authentication Method" page? And, if possible, could you provide a timeline of changes done to settings on that page, starting from April (the moment when you believe those logs mentioning "null" user started to appear)? >We do have a u2f_opt custom script to allow bypassing U2F - but that was added in mid-May (so a month after the null messages started appearing in oxauth/logs) Can I assume you also have a default u2f custom script employed there as well? If it's the case, was it employed at this instance back in April? Was it the script which was included in your Gluu Server after installation, or did you get it from Github? Could you share it as well here?

By Aliaksandr Samuseu staff 11 Jun 2019 at 2:38 p.m. CDT

Aliaksandr Samuseu gravatar
Jan, could you please provide a more complete version of this trace? ``` 2019-06-11 07:01:14,927 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:533) - Attempting to redirect user: SessionUser: SessionState {dn='oxAuthSessionId=ebfd4dad-...-8c02299eea12,ou=session,o=@!6D74.52B0.0D98...0CFB,o=gluu', id='ebfd4dad-...-8c02299eea12', lastUsedAt=Tue Jun 11 07:01:14 UTC 2019, userDn='inum=@!6D74.52B0.0D98...3244.0241.D1B3,ou=people,o=@!6D74.52B0.0D98...0CFB,o=gluu', authenticationTime=Tue Jun 11 07:01:14 UTC 2019, state=authenticated, sessionState='02cb...ab5e.0fdb700e-...-f35ffdf38b02', permissionGranted=null, isJwt=false, jwt=null, permissionGrantedMap=org.xdi.oxauth.model.common.SessionIdAccessMap@10b988f5, involvedClients=null, sessionAttributes={session_custom_state=approved, auth_external_attributes=null, opbs=b8aea6fc-...-b74f34a76ede, response_type=code, oxpush2_u2f_device_enroll=false, oxpush2_u2f_device_id=15...56, nonce=5193b337-...-050a288b951d, client_id=@!6D74.52B0.0D98...EDE0, auth_step_passed_1=true, auth_step=2, acr=u2f_opt, remote_ip=1.0.0.1, 10.4.4.4, auth_user=jan, scope=openid profile email user_name, acr_values=u2f_opt, oxpush2_u2f_device_user_inum=@!6D74.52B0.0D98...3244.0241.D1B3, redirect_uri=https://idp...com/identity/authentication/getauthcode, state=ccf8237b-...-c16fe80f14b8, oxpush2_u2f_device_one_step=false}, persisted=true} 2019-06-11 07:01:14,930 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:541) - Attempting to redirect user: User: org.xdi.oxauth.model.common.User@368b2e08 2019-06-11 07:01:14,931 INFO [qtp804611486-115363] [org.xdi.oxauth.auth.Authenticator] (Authenticator.java:409) - Authentication success for User: 'null' ``` I mean, a bigger section of the log showing entries preceding and following it. We lack context to understand the flow of execution as of now.

By Jan Mashat user 12 Jun 2019 at 7:58 a.m. CDT

Jan Mashat gravatar
Hi Aliaksandr, Here is a more complete version of the trace (the preceding/following entries have a 2 minute time gap): ``` 2019-06-11 07:01:04,865 ERROR [qtp804611486-115401] [org.xdi.oxauth.auth.Authenticator] (Authenticator.java:608) - Failed to get attributes from session 2019-06-11 07:01:14,927 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:533) - Attempting to redirect user: SessionUser: SessionState {dn='oxAuthSessionId=ebfd4dad-...-8c02299eea12,ou=session,o=@!6D74.52B0.0D98...0CFB,o=gluu', id='ebfd4dad-...-8c02299eea12', lastUsedAt=Tue Jun 11 07:01:14 UTC 2019, userDn='inum=@!6D74.52B0.0D98...3244.0241.D1B3,ou=people,o=@!6D74.52B0.0D98...0CFB,o=gluu', authenticationTime=Tue Jun 11 07:01:14 UTC 2019, state=authenticated, sessionState='02cb...ab5e.0fdb700e-...-f35ffdf38b02', permissionGranted=null, isJwt=false, jwt=null, permissionGrantedMap=org.xdi.oxauth.model.common.SessionIdAccessMap@10b988f5, involvedClients=null, sessionAttributes={session_custom_state=approved, auth_external_attributes=null, opbs=b8aea6fc-...-b74f34a76ede, response_type=code, oxpush2_u2f_device_enroll=false, oxpush2_u2f_device_id=15...56, nonce=5193b337-...-050a288b951d, client_id=@!6D74.52B0.0D98...EDE0, auth_step_passed_1=true, auth_step=2, acr=u2f_opt, remote_ip=1.0.0.1, 10.4.4.4, auth_user=jan, scope=openid profile email user_name, acr_values=u2f_opt, oxpush2_u2f_device_user_inum=@!6D74.52B0.0D98...3244.0241.D1B3, redirect_uri=https://idp...com/identity/authentication/getauthcode, state=ccf8237b-...-c16fe80f14b8, oxpush2_u2f_device_one_step=false}, persisted=true} 2019-06-11 07:01:14,930 INFO [qtp804611486-115363] [org.xdi.oxauth.service.AuthenticationService] (AuthenticationService.java:541) - Attempting to redirect user: User: org.xdi.oxauth.model.common.User@368b2e08 2019-06-11 07:01:14,931 INFO [qtp804611486-115363] [org.xdi.oxauth.auth.Authenticator] (Authenticator.java:409) - Authentication success for User: 'null' 2019-06-11 07:01:15,085 INFO [qtp804611486-115448] [org.xdi.oxauth.auth.Authenticator] (Authenticator.java:262) - Authentication success for Client: '@!6D74.52B0.0D98...EDE0' ``` The default authentication methods are both set to `u2f_opt` (both `u2f_opt` and `u2f` are Enabled under Manage Custom Scripts) - and it has been this way since mid-May. Until then (from mid-June to mid-May) we were only using the default `u2f` for the authentication methods: ``` # oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. # Copyright (c) 2016, Gluu # # Author: Yuriy Movchan # import java import sys from javax.ws.rs.core import Response from org.jboss.resteasy.client import ClientResponseFailure from org.jboss.resteasy.client.exception import ResteasyClientException from org.xdi.model.custom.script.type.auth import PersonAuthenticationType from org.xdi.oxauth.client.fido.u2f import FidoU2fClientFactory from org.xdi.oxauth.model.config import Constants from org.xdi.oxauth.security import Identity from org.xdi.oxauth.service import UserService, AuthenticationService, SessionIdService from org.xdi.oxauth.service.fido.u2f import DeviceRegistrationService from org.xdi.oxauth.util import ServerUtil from org.xdi.service.cdi.util import CdiUtil from org.xdi.util import StringHelper class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis def init(self, configurationAttributes): print "U2F. Initialization" print "U2F. Initialization. Downloading U2F metadata" u2f_server_uri = configurationAttributes.get("u2f_server_uri").getValue2() u2f_server_metadata_uri = u2f_server_uri + "/.well-known/fido-u2f-configuration" metaDataConfigurationService = FidoU2fClientFactory.instance().createMetaDataConfigurationService(u2f_server_metadata_uri) max_attempts = 20 for attempt in range(1, max_attempts + 1): try: self.metaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration() break except ClientResponseFailure, ex: # Detect if last try or we still get Service Unavailable HTTP error if (attempt == max_attempts) or (ex.getResponse().getResponseStatus() != Response.Status.SERVICE_UNAVAILABLE): raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt except ResteasyClientException, ex: # Detect if last try or we still get Service Unavailable HTTP error if attempt == max_attempts: raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt print "U2F. Initialized successfully" return True def destroy(self, configurationAttributes): print "U2F. Destroy" print "U2F. Destroyed successfully" return True def getApiVersion(self): return 1 def isValidAuthenticationMethod(self, usageType, configurationAttributes): return True def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): return None def authenticate(self, configurationAttributes, requestParameters, step): authenticationService = CdiUtil.bean(AuthenticationService) identity = CdiUtil.bean(Identity) credentials = identity.getCredentials() user_name = credentials.getUsername() if (step == 1): print "U2F. Authenticate for step 1" user_password = credentials.getPassword() logged_in = False if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)): userService = CdiUtil.bean(UserService) logged_in = authenticationService.authenticate(user_name, user_password) if (not logged_in): return False return True elif (step == 2): print "U2F. Authenticate for step 2" token_response = ServerUtil.getFirstValue(requestParameters, "tokenResponse") if token_response == None: print "U2F. Authenticate for step 2. tokenResponse is empty" return False auth_method = ServerUtil.getFirstValue(requestParameters, "authMethod") if auth_method == None: print "U2F. Authenticate for step 2. authMethod is empty" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "U2F. Prepare for step 2. Failed to determine user name" return False if (auth_method == 'authenticate'): print "U2F. Prepare for step 2. Call FIDO U2F in order to finish authentication workflow" authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService(self.metaDataConfiguration) authenticationStatus = authenticationRequestService.finishAuthentication(user.getUserId(), token_response) if (authenticationStatus.getStatus() != Constants.RESULT_SUCCESS): print "U2F. Authenticate for step 2. Get invalid authentication status from FIDO U2F server" return False return True elif (auth_method == 'enroll'): print "U2F. Prepare for step 2. Call FIDO U2F in order to finish registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService(self.metaDataConfiguration) registrationStatus = registrationRequestService.finishRegistration(user.getUserId(), token_response) if (registrationStatus.getStatus() != Constants.RESULT_SUCCESS): print "U2F. Authenticate for step 2. Get invalid registration status from FIDO U2F server" return False return True else: print "U2F. Prepare for step 2. Authenticatiod method is invalid" return False return False else: return False def prepareForStep(self, configurationAttributes, requestParameters, step): identity = CdiUtil.bean(Identity) if (step == 1): return True elif (step == 2): print "U2F. Prepare for step 2" session_id = CdiUtil.bean(SessionIdService).getSessionIdFromCookie() if StringHelper.isEmpty(session_id): print "U2F. Prepare for step 2. Failed to determine session_id" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "U2F. Prepare for step 2. Failed to determine user name" return False u2f_application_id = configurationAttributes.get("u2f_application_id").getValue2() # Check if user have registered devices deviceRegistrationService = CdiUtil.bean(DeviceRegistrationService) userInum = user.getAttribute("inum") registrationRequest = None authenticationRequest = None deviceRegistrations = deviceRegistrationService.findUserDeviceRegistrations(userInum, u2f_application_id) if (deviceRegistrations.size() > 0): print "U2F. Prepare for step 2. Call FIDO U2F in order to start authentication workflow" try: authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService(self.metaDataConfiguration) authenticationRequest = authenticationRequestService.startAuthentication(user.getUserId(), None, u2f_application_id, session_id) except ClientResponseFailure, ex: if (ex.getResponse().getResponseStatus() != Response.Status.NOT_FOUND): print "U2F. Prepare for step 2. Failed to start authentication workflow. Exception:", sys.exc_info()[1] return False else: print "U2F. Prepare for step 2. Call FIDO U2F in order to start registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService(self.metaDataConfiguration) registrationRequest = registrationRequestService.startRegistration(user.getUserId(), u2f_application_id, session_id) identity.setWorkingParameter("fido_u2f_authentication_request", ServerUtil.asJson(authenticationRequest)) identity.setWorkingParameter("fido_u2f_registration_request", ServerUtil.asJson(registrationRequest)) return True elif (step == 3): print "U2F. Prepare for step 3" return True else: return False def getExtraParametersForStep(self, configurationAttributes, step): return None def getCountAuthenticationSteps(self, configurationAttributes): return 2 def getPageForStep(self, configurationAttributes, step): if (step == 2): return "/auth/u2f/login.xhtml" return "" def logout(self, configurationAttributes, requestParameters): return True ``` One thing I didn't know when pasting in the `u2f_opt` code yesterday is that it had already been slightly modified by a colleague as he tried to prevent the issue from re-occurring. The original `u2f_opt` code that was in place since mid-June up until the issue occurred yesterday morning is: ``` # oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. # Copyright (c) 2016, Gluu # # Author: Yuriy Movchan # import java import sys from javax.ws.rs.core import Response from org.jboss.resteasy.client import ClientResponseFailure from org.jboss.resteasy.client.exception import ResteasyClientException from org.xdi.model.custom.script.type.auth import PersonAuthenticationType from org.xdi.oxauth.client.fido.u2f import FidoU2fClientFactory from org.xdi.oxauth.model.config import Constants from org.xdi.oxauth.security import Identity from org.xdi.oxauth.service import UserService, AuthenticationService, SessionIdService from org.xdi.oxauth.service.fido.u2f import DeviceRegistrationService from org.xdi.oxauth.util import ServerUtil from org.xdi.service.cdi.util import CdiUtil from org.xdi.util import StringHelper class PersonAuthentication(PersonAuthenticationType): def __init__(self, currentTimeMillis): self.currentTimeMillis = currentTimeMillis def checkNo2FA(self, user): roles = user.getAttribute("role", True) if roles is not None: if isinstance(roles, unicode): if roles == "no2fa": return True else: for i in range(roles.length()): if roles.getString(i) == "no2fa": return True return False def init(self, configurationAttributes): print "U2F. Initialization" print "U2F. Initialization. Downloading U2F metadata" u2f_server_uri = configurationAttributes.get("u2f_server_uri").getValue2() u2f_server_metadata_uri = u2f_server_uri + "/.well-known/fido-u2f-configuration" metaDataConfigurationService = FidoU2fClientFactory.instance().createMetaDataConfigurationService( u2f_server_metadata_uri) max_attempts = 20 for attempt in range(1, max_attempts + 1): try: self.metaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration() break except ClientResponseFailure, ex: # Detect if last try or we still get Service Unavailable HTTP error if (attempt == max_attempts) or ( ex.getResponse().getResponseStatus() != Response.Status.SERVICE_UNAVAILABLE): raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt except ResteasyClientException, ex: # Detect if last try or we still get Service Unavailable HTTP error if attempt == max_attempts: raise ex java.lang.Thread.sleep(3000) print "Attempting to load metadata: %d" % attempt print "U2F. Initialized successfully" return True def destroy(self, configurationAttributes): print "U2F. Destroy" print "U2F. Destroyed successfully" return True def getApiVersion(self): return 1 def isValidAuthenticationMethod(self, usageType, configurationAttributes): return True def getAlternativeAuthenticationMethod(self, usageType, configurationAttributes): return None def authenticate(self, configurationAttributes, requestParameters, step): authenticationService = CdiUtil.bean(AuthenticationService) identity = CdiUtil.bean(Identity) credentials = identity.getCredentials() user_name = credentials.getUsername() if (step == 1): print "U2F. Authenticate for step 1" user_password = credentials.getPassword() logged_in = False if (StringHelper.isNotEmptyString(user_name) and StringHelper.isNotEmptyString(user_password)): userService = CdiUtil.bean(UserService) logged_in = authenticationService.authenticate(user_name, user_password) if (not logged_in): return False if self.checkNo2FA(authenticationService.getAuthenticatedUser()): identity.setWorkingParameter("no2fa", True) return True elif (step == 2): print "U2F. Authenticate for step 2" token_response = ServerUtil.getFirstValue(requestParameters, "tokenResponse") if token_response == None: print "U2F. Authenticate for step 2. tokenResponse is empty" return False auth_method = ServerUtil.getFirstValue(requestParameters, "authMethod") if auth_method == None: print "U2F. Authenticate for step 2. authMethod is empty" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "U2F. Prepare for step 2. Failed to determine user name" return False if (auth_method == 'authenticate'): print "U2F. Prepare for step 2. Call FIDO U2F in order to finish authentication workflow" authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService( self.metaDataConfiguration) authenticationStatus = authenticationRequestService.finishAuthentication(user.getUserId(), token_response) if (authenticationStatus.getStatus() != Constants.RESULT_SUCCESS): print "U2F. Authenticate for step 2. Get invalid authentication status from FIDO U2F server" return False return True elif (auth_method == 'enroll'): print "U2F. Prepare for step 2. Call FIDO U2F in order to finish registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService( self.metaDataConfiguration) registrationStatus = registrationRequestService.finishRegistration(user.getUserId(), token_response) if (registrationStatus.getStatus() != Constants.RESULT_SUCCESS): print "U2F. Authenticate for step 2. Get invalid registration status from FIDO U2F server" return False return True else: print "U2F. Prepare for step 2. Authenticatiod method is invalid" return False return False else: return False def prepareForStep(self, configurationAttributes, requestParameters, step): identity = CdiUtil.bean(Identity) if (step == 1): return True elif (step == 2): print "U2F. Prepare for step 2" session_id = CdiUtil.bean(SessionIdService).getSessionIdFromCookie() if StringHelper.isEmpty(session_id): print "U2F. Prepare for step 2. Failed to determine session_id" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "U2F. Prepare for step 2. Failed to determine user name" return False u2f_application_id = configurationAttributes.get("u2f_application_id").getValue2() # Check if user have registered devices deviceRegistrationService = CdiUtil.bean(DeviceRegistrationService) userInum = user.getAttribute("inum") registrationRequest = None authenticationRequest = None deviceRegistrations = deviceRegistrationService.findUserDeviceRegistrations(userInum, u2f_application_id) if (deviceRegistrations.size() > 0): print "U2F. Prepare for step 2. Call FIDO U2F in order to start authentication workflow" try: authenticationRequestService = FidoU2fClientFactory.instance().createAuthenticationRequestService( self.metaDataConfiguration) authenticationRequest = authenticationRequestService.startAuthentication(user.getUserId(), None, u2f_application_id, session_id) except ClientResponseFailure, ex: if (ex.getResponse().getResponseStatus() != Response.Status.NOT_FOUND): print "U2F. Prepare for step 2. Failed to start authentication workflow. Exception:", \ sys.exc_info()[1] return False else: print "U2F. Prepare for step 2. Call FIDO U2F in order to start registration workflow" registrationRequestService = FidoU2fClientFactory.instance().createRegistrationRequestService( self.metaDataConfiguration) registrationRequest = registrationRequestService.startRegistration(user.getUserId(), u2f_application_id, session_id) identity.setWorkingParameter("fido_u2f_authentication_request", ServerUtil.asJson(authenticationRequest)) identity.setWorkingParameter("fido_u2f_registration_request", ServerUtil.asJson(registrationRequest)) return True elif (step == 3): print "U2F. Prepare for step 3" return True else: return False def getExtraParametersForStep(self, configurationAttributes, step): return None def getCountAuthenticationSteps(self, configurationAttributes): identity = CdiUtil.bean(Identity) if identity.isSetWorkingParameter("no2fa"): return 1 return 2 def getPageForStep(self, configurationAttributes, step): if (step == 2): return "/auth/u2f/login.xhtml" return "" def logout(self, configurationAttributes, requestParameters): return True ```

By Aliaksandr Samuseu staff 12 Jun 2019 at 10:08 a.m. CDT

Aliaksandr Samuseu gravatar
Thank you for all the details submitted so far, Jan. Could you elaborate on this? >One thing I didn't know when pasting in the u2f_opt code yesterday is that it had already been slightly modified by a colleague as he tried to prevent the issue from re-occurring. If you try to modify the script to prevent it, may I assume you've figured out the cause of it already? Could you share your findings with us?

By Tamas Demeter-Haludka user 13 Jun 2019 at 6 a.m. CDT

Tamas Demeter-Haludka gravatar
What I did in the code was to change the `variable == None` parts to `variable is None` in the conditions. I had no idea what the problem could be, I opened the file in IDEA, and this fix was suggested, so I applied it.

By Jan Mashat user 13 Jun 2019 at 8:21 a.m. CDT

Jan Mashat gravatar
I can confirm that `User 'null' authenticated successfully` showed up again in identity/logs a few hours after Tamas' changes - so I don't think his changes had any effect - but the message only showed up on June 11th (at the time I experienced the issue and 5 more times that day) and hasn't appeared since. `Authentication success for User: 'null'` in oxauth/logs is a different story, as that started appearing on April 15th (as I mentioned in my original post) and continues to this day.