def prepareForStep(self, configurationAttributes, requestParameters, step): identity = CdiUtil.bean(Identity) if step == 1: return True elif step == 2: print "Fido2. Prepare for step 2" session = CdiUtil.bean(SessionIdService).getSessionId() if session == None: print "Fido2. Prepare for step 2. Failed to determine session_id" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if user == None: print "Fido2. Prepare for step 2. Failed to determine user name" return False userName = user.getUserId() metaDataConfiguration = self.getMetaDataConfiguration() assertionResponse = None attestationResponse = None # Check if user have registered devices userService = CdiUtil.bean(UserService) countFido2Devices = userService.countFidoAndFido2Devices(userName, self.fido2_domain) if countFido2Devices > 0: print "Fido2. Prepare for step 2. Call Fido2 endpoint in order to start assertion flow" try: assertionService = Fido2ClientFactory.instance().createAssertionService(metaDataConfiguration) assertionRequest = json.dumps({'username': userName}, separators=(',', ':')) assertionResponse = assertionService.authenticate(assertionRequest).readEntity(java.lang.String) except ClientResponseFailure, ex: print "Fido2. Prepare for step 2. Failed to start assertion flow. Exception:", sys.exc_info()[1] return False else: print "Fido2. Prepare for step 2. Call Fido2 endpoint in order to start attestation flow" try: attestationService = Fido2ClientFactory.instance().createAttestationService(metaDataConfiguration) attestationRequest = json.dumps({'username': userName, 'displayName': userName, 'attestation' : 'direct'}, separators=(',', ':')) attestationResponse = attestationService.register(attestationRequest).readEntity(java.lang.String) except ClientResponseFailure, ex: print "Fido2. Prepare for step 2. Failed to start attestation flow. Exception:", sys.exc_info()[1] return False
def getMetaDataConfiguration(self): if self.metaDataConfiguration != None: return self.metaDataConfiguration self.metaDataLoaderLock.lock() # Make sure that another thread not loaded configuration already if self.metaDataConfiguration != None: return self.metaDataConfiguration try: print "Fido2. Initialization. Downloading Fido2 metadata" self.fido2_server_metadata_uri = self.fido2_server_uri + "/.well-known/fido2-configuration" #self.fido2_server_metadata_uri = self.fido2_server_uri + "/fido2/restv1/fido2/configuration" metaDataConfigurationService = Fido2ClientFactory.instance().createMetaDataConfigurationService(self.fido2_server_metadata_uri) max_attempts = 10 for attempt in range(1, max_attempts + 1): try: self.metaDataConfiguration = metaDataConfigurationService.getMetadataConfiguration().readEntity(java.lang.String) return self.metaDataConfiguration 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
def authenticateFido2(self, userId, requestParameters): if REMOTE_DEBUG: pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) userService = CdiUtil.bean(UserService) authenticationService = CdiUtil.bean(AuthenticationService) identity = CdiUtil.bean(Identity) session = identity.getSessionId() tokenResponse = ServerUtil.getFirstValue(requestParameters, "fido2Authentication") print("%s. Authenticate. Got fido2 authentication response: %s" % (self.name, tokenResponse)) metaDataConfiguration = self.getFidoMetaDataConfiguration() assertionService = Fido2ClientFactory.instance( ).createAssertionService(metaDataConfiguration) assertionStatus = assertionService.verify(tokenResponse) authenticationStatusEntity = assertionStatus.readEntity( java.lang.String) if assertionStatus.getStatus() != Response.Status.OK.getStatusCode(): print( "%s. Authenticate. Got invalid authentication status from Fido2 server" % self.name) return False return authenticationService.authenticate( identity.getWorkingParameter("userId"))
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 "Fido2. Authenticate for step 1" if authenticationService.getAuthenticatedUser() != None: return True 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 "Fido2. Authenticate for step 2" token_response = ServerUtil.getFirstValue(requestParameters, "tokenResponse") if token_response == None: print "Fido2. Authenticate for step 2. tokenResponse is empty" return False auth_method = ServerUtil.getFirstValue(requestParameters, "authMethod") if auth_method == None: print "Fido2. Authenticate for step 2. authMethod is empty" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "Fido2. Prepare for step 2. Failed to determine user name" return False if (auth_method == 'authenticate'): print "Fido2. Prepare for step 2. Call Fido2 in order to finish authentication flow" assertionService = Fido2ClientFactory.instance( ).createAssertionService(self.metaDataConfiguration) assertionStatus = assertionService.verify(token_response) authenticationStatusEntity = assertionStatus.readEntity( java.lang.String) if (assertionStatus.getStatus() != Response.Status.OK.getStatusCode()): print "Fido2. Authenticate for step 2. Get invalid authentication status from Fido2 server" return False return True elif (auth_method == 'enroll'): print "Fido2. Prepare for step 2. Call Fido2 in order to finish registration flow" attestationService = Fido2ClientFactory.instance( ).createAttestationService(self.metaDataConfiguration) attestationStatus = attestationService.verify(token_response) if (attestationStatus.getStatus() != Response.Status.OK.getStatusCode()): print "Fido2. Authenticate for step 2. Get invalid registration status from Fido2 server" return False return True else: print "Fido2. Prepare for step 2. Authentication 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 "Fido2. Prepare for step 2" session_id = CdiUtil.bean( SessionIdService).getSessionIdFromCookie() if StringHelper.isEmpty(session_id): print "Fido2. Prepare for step 2. Failed to determine session_id" return False authenticationService = CdiUtil.bean(AuthenticationService) user = authenticationService.getAuthenticatedUser() if (user == None): print "Fido2. Prepare for step 2. Failed to determine user name" return False userName = user.getUserId() metaDataConfiguration = self.getMetaDataConfiguration() # Check if user have registered devices registrationPersistenceService = CdiUtil.bean( RegistrationPersistenceService) assertionResponse = None attestationResponse = None userFido2Devices = registrationPersistenceService.findAllRegisteredByUsername( userName) if (userFido2Devices.size() > 0): print "Fido2. Prepare for step 2. Call Fido2 endpoint in order to start assertion flow" try: assertionService = Fido2ClientFactory.instance( ).createAssertionService(metaDataConfiguration) assertionRequest = json.dumps({'username': userName}, separators=(',', ':')) assertionResponse = assertionService.authenticate( assertionRequest).readEntity(java.lang.String) except ClientResponseFailure, ex: print "Fido2. Prepare for step 2. Failed to start assertion flow. Exception:", sys.exc_info( )[1] return False else: print "Fido2. Prepare for step 2. Call Fido2 endpoint in order to start attestation flow" try: attestationService = Fido2ClientFactory.instance( ).createAttestationService(metaDataConfiguration) attestationRequest = json.dumps( { 'username': userName, 'displayName': userName }, separators=(',', ':')) attestationResponse = attestationService.register( attestationRequest).readEntity(java.lang.String) except ClientResponseFailure, ex: print "Fido2. Prepare for step 2. Failed to start attestation flow. Exception:", sys.exc_info( )[1] return False
def prepareForStep(self, configurationAttributes, requestParameters, step): if REMOTE_DEBUG: pydevd.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True) # Inject dependencies identity = CdiUtil.bean(Identity) facesResources = CdiUtil.bean(FacesResources) facesService = CdiUtil.bean(FacesService) userService = CdiUtil.bean(UserService) session = identity.getSessionId() sessionAttributes = session.getSessionAttributes() externalContext = facesResources.getFacesContext().getExternalContext() uiLocales = sessionAttributes.get(AuthorizeRequestParam.UI_LOCALES) rpConfig = self.getRPConfig(session) clientUri = self.getClientUri(session) externalContext.addResponseHeader( "Content-Security-Policy", "default-src 'self' https://www.canada.ca; font-src 'self' https://fonts.gstatic.com https://use.fontawesome.com https://www.canada.ca; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline' https://use.fontawesome.com https://fonts.googleapis.com https://www.canada.ca; script-src 'self' 'unsafe-inline' https://www.canada.ca https://ajax.googleapis.com; connect-src 'self' https://*.fjgc-gccf.gc.ca" ) if step == 1: httpRequest = externalContext.getRequest() # Bookmark detection #if httpRequest.getHeader("referer") is None: # if StringHelper.isNotEmpty(clientUri): # facesService.redirectToExternalURL(clientUri) # return True # else: # print("%s: prepareForStep. clientUri is missing for client %s" % (self.name, self.getClient(session).getClientName())) # return False # forceAuthn workaround prompt2 = httpRequest.getParameter("prompt2") if prompt2 == "login": identity.setWorkingParameter("forceAuthn", True) # step could actually be 2, or 3 if uiLocales is not None: if len(self.providers) > 1: step = self.STEP_CHOOSER else: step = self.STEP_1FA if identity.getWorkingParameter("abort"): # Back button workaround # Obtain the client URI of the current client from the client configuration if len(self.providers ) == 1: # Pass through, so send them back to the client if StringHelper.isNotEmpty(clientUri): facesService.redirectToExternalURL(clientUri) return True else: print( "%s: prepareForStep. clientUri is missing for client %s" % (self.name, self.getClient(session).getClientName())) return False else: # reset the chooser identity.setWorkingParameter("provider", None) if step == self.STEP_CHOOSER: # Prepare for chooser page customization. for param in ["layout", "chooser", "content"]: identity.setWorkingParameter(param, rpConfig[param]) elif step in {self.STEP_1FA, self.STEP_COLLECT, self.STEP_2FA}: # Passport passportOptions = { "ui_locales": uiLocales, "exp": int(time.time()) + 60 } if step in {self.STEP_1FA, self.STEP_COLLECT}: provider = identity.getWorkingParameter("provider") if provider is None and len( self.providers ) == 1: # Only one provider. Direct Pass-through provider = next(iter(self.providers)) identity.setWorkingParameter("provider", provider) if step == self.STEP_1FA: # Coordinate single-sign-on (SSO) maxAge = (sessionAttributes.get(AuthorizeRequestParam.MAX_AGE) or self.getClient(session).getDefaultMaxAge()) if (identity.getWorkingParameter("forceAuthn") or ("GCCF" in self.passport.getProvider(provider)["options"] and maxAge < 1200) ): # 1200 is 20 minutes, the SSO timeout on GCKey and CBS passportOptions["forceAuthn"] = "true" elif step == self.STEP_COLLECT: collect = rpConfig.get("collect") if collect is not None: passportOptions["allowCreate"] = rpConfig.get( "allowCreate") or "false" passportOptions["spNameQualifier"] = collect else: # This should never happen print( "%s. prepareForStep: collection entityID is missing" % self.name) return False elif step == self.STEP_2FA: provider = rpConfig.get("mfaProvider") if provider is None: print("%s: prepareForStep. mfaProvider is missing!" % self.name) return False mfaId = identity.getWorkingParameter("mfaId") if mfaId is None: print("%s: prepareForStep. mfaId is missing!" % self.name) return False else: passportOptions["login_hint"] = mfaId # The following parameters are redundant, but currently required by the 2ndFaaS passportOptions[ "redirect_uri"] = self.passport.getProvider( provider)["callbackUrl"] passportOptions["response_type"] = "code" passportOptions["scope"] = "openid profile" # Set the abort flag to handle back button identity.setWorkingParameter("abort", True) # Send the request to passport passportRequest = self.passport.createRequest( provider, passportOptions) facesService.redirectToExternalURL(passportRequest) elif step in {self.STEP_FIDO_REGISTER, self.STEP_FIDO_AUTH}: userId = identity.getWorkingParameter("userId") metaDataConfiguration = self.getFidoMetaDataConfiguration() if step == self.STEP_FIDO_REGISTER: try: attestationService = Fido2ClientFactory.instance( ).createAttestationService(metaDataConfiguration) attestationRequest = json.dumps( { 'username': userId, 'displayName': userId, 'attestation': 'direct', 'timeout': 120000, 'userVerification': 'discouraged' }, separators=(',', ':')) attestationResponse = attestationService.register( attestationRequest).readEntity(java.lang.String) except ClientErrorException as ex: print( "%s. Prepare for step. Failed to start FIDO2 attestation flow. Exception:" % self.name, sys.exc_info()[1]) return False identity.setWorkingParameter( "fido2_attestation_request", ServerUtil.asJson(attestationResponse)) print(ServerUtil.asJson(attestationResponse)) elif step == self.STEP_FIDO_AUTH: userId = identity.getWorkingParameter("userId") metaDataConfiguration = self.getFidoMetaDataConfiguration() fidoDeviceCount = userService.countFidoAndFido2Devices( userId, self.fido2_domain) try: assertionService = Fido2ClientFactory.instance( ).createAssertionService(metaDataConfiguration) assertionRequest = json.dumps( { 'username': userId, 'timeout': 120000, 'userVerification': 'discouraged' }, separators=(',', ':')) assertionResponse = assertionService.authenticate( assertionRequest).readEntity(java.lang.String) except ClientErrorException as ex: print( "%s. Prepare for step. Failed to start FIDO2 assertion flow. Exception:" % self.name, sys.exc_info()[1]) return False identity.setWorkingParameter( "fido2_assertion_request", ServerUtil.asJson(assertionResponse)) return True