def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ def _raise_unauthorized(): raise JsonError(401, description='Unauthorized') token = request.headers.get('Authorization') if token is None: _raise_unauthorized() token = token.split() if len(token) != 2: _raise_unauthorized() name, assertion = token if name.lower() != 'browser-id': resp = JsonError(401, description='Unsupported') resp.www_authenticate = ('Browser-ID', {}) raise resp try: verifier = get_verifier() assertion = verifier.verify(assertion) except BrowserIDError: _raise_unauthorized() # everything sounds good, add the assertion to the list of validated fields # and continue request.validated['assertion'] = assertion
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browserid': resp = _unauthorized(description='Unsupported') resp.www_authenticate = ('BrowserID', {}) raise resp try: verifier = get_verifier() with metrics_timer('tokenserver.assertion.verify', request): assertion = verifier.verify(assertion) except browserid.errors.Error as e: # Convert CamelCase to under_scores for reporting. error_type = e.__class__.__name__ error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() request.metrics['token.assertion.verify_failure'] = 1 request.metrics['token.assertion.%s' % error_type] = 1 # Log a full traceback for errors that are not a simple # "your assertion was bad and we dont trust it". if not isinstance(e, browserid.errors.TrustError): logger.exception("Unexpected verification error") # Report an appropriate error code. if isinstance(e, browserid.errors.ConnectionError): raise json_error(503, description="Resource is not available") if isinstance(e, browserid.errors.ExpiredSignatureError): raise _unauthorized("invalid-timestamp") raise _unauthorized("invalid-credentials") # everything sounds good, add the assertion to the list of validated fields # and continue request.metrics['token.assertion.verify_success'] = 1 request.validated['assertion'] = assertion # Include a unique FxA identifier in the logs, but obfuscate # it for privacy purposes. id_key = request.registry.settings.get("fxa.metrics_uid_secret_key") if id_key: email = request.validated['assertion']['email'] request.metrics['uid'] = fxa_metrics_uid(email, id_key)
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ metlog = request.registry['metlog'] def _unauthorized(): return json_error(401, description='Unauthorized') token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browser-id': resp = json_error(401, description='Unsupported') resp.www_authenticate = ('Browser-ID', {}) raise resp def _handle_exception(error_type): # convert CamelCase to camel_case error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() metlog.incr('token.assertion.verify_failure') metlog.incr('token.assertion.%s' % error_type) if error_type == "connection_error": raise json_error(503, description="Resource is not available") else: raise _unauthorized() try: verifier = get_verifier() assertion = verifier.verify(assertion) except ClientCatchedError as e: _handle_exception(e.error_type) except BrowserIDError as e: _handle_exception(e.__class__.__name__) # everything sounds good, add the assertion to the list of validated fields # and continue metlog.incr('token.assertion.verify_success') request.validated['assertion'] = assertion
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ metlog = request.registry['metlog'] token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browserid': resp = _unauthorized(description='Unsupported') resp.www_authenticate = ('BrowserID', {}) raise resp def _handle_exception(error_type): # convert CamelCase to camel_case error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() metlog.incr('token.assertion.verify_failure') metlog.incr('token.assertion.%s' % error_type) if error_type == "connection_error": raise json_error(503, description="Resource is not available") if error_type == "expired_signature_error": raise _unauthorized("invalid-timestamp") else: raise _unauthorized("invalid-credentials") try: verifier = get_verifier() with time_backend_operation(request, 'assertion.verify'): assertion = verifier.verify(assertion) except ClientCatchedError as e: _handle_exception(e.error_type) except BrowserIDError as e: _handle_exception(e.__class__.__name__) # everything sounds good, add the assertion to the list of validated fields # and continue metlog.incr('token.assertion.verify_success') request.validated['assertion'] = assertion
def mock_verifier(self, response=None, exc=None): def mock_verify_method(assertion): if exc is not None: raise exc if response is not None: return response return {"status": "okay", "email": get_assertion_info(assertion)["principal"]["email"]} verifier = get_verifier(self.config.registry) orig_verify_method = verifier.__dict__.get("verify", None) verifier.__dict__["verify"] = mock_verify_method try: yield None finally: if orig_verify_method is None: del verifier.__dict__["verify"] else: verifier.__dict__["verify"] = orig_verify_method
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browserid': resp = _unauthorized(description='Unsupported') resp.www_authenticate = ('BrowserID', {}) raise resp def _handle_exception(error_type): # convert CamelCase to camel_case error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() request.metrics['token.assertion.verify_failure'] = 1 request.metrics['token.assertion.%s' % error_type] = 1 if error_type == "connection_error": raise json_error(503, description="Resource is not available") if error_type == "expired_signature_error": raise _unauthorized("invalid-timestamp") else: raise _unauthorized("invalid-credentials") try: verifier = get_verifier() with metrics_timer('tokenserver.assertion.verify', request): assertion = verifier.verify(assertion) except BrowserIDError as e: _handle_exception(e.__class__.__name__) # everything sounds good, add the assertion to the list of validated fields # and continue request.metrics['token.assertion.verify_success'] = 1 request.validated['assertion'] = assertion
def mock_verifier(self, response=None, exc=None): def mock_verify_method(assertion): if exc is not None: raise exc if response is not None: return response return { "status": "okay", "email": get_assertion_info(assertion)["principal"]["email"], } verifier = get_verifier(self.config.registry) orig_verify_method = verifier.__dict__.get("verify", None) verifier.__dict__["verify"] = mock_verify_method try: yield None finally: if orig_verify_method is None: del verifier.__dict__["verify"] else: verifier.__dict__["verify"] = orig_verify_method
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browserid': resp = _unauthorized(description='Unsupported') resp.www_authenticate = ('BrowserID', {}) raise resp try: verifier = get_verifier() with metrics_timer('tokenserver.assertion.verify', request): assertion = verifier.verify(assertion) except browserid.errors.Error as e: # Convert CamelCase to under_scores for reporting. error_type = e.__class__.__name__ error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() request.metrics['token.assertion.verify_failure'] = 1 request.metrics['token.assertion.%s' % error_type] = 1 # Log a full traceback for errors that are not a simple # "your assertion was bad and we dont trust it". if not isinstance(e, browserid.errors.TrustError): logger.exception("Unexpected verification error") # Report an appropriate error code. if isinstance(e, browserid.errors.ConnectionError): raise json_error(503, description="Resource is not available") if isinstance(e, browserid.errors.ExpiredSignatureError): raise _unauthorized("invalid-timestamp") raise _unauthorized("invalid-credentials") # everything sounds good, add the assertion to the list of validated fields # and continue request.metrics['token.assertion.verify_success'] = 1 request.validated['assertion'] = assertion id_key = request.registry.settings.get("fxa.metrics_uid_secret_key") if id_key is None: id_key = 'insecure' email = assertion['email'] fxa_uid_full = fxa_metrics_hash(email, id_key) # "legacy" key used by heka active_counts.lua request.metrics['uid'] = fxa_uid_full request.metrics['email'] = email # "new" keys use shorter values fxa_uid = fxa_uid_full[:32] request.validated['fxa_uid'] = fxa_uid request.metrics['fxa_uid'] = fxa_uid try: device = assertion['idpClaims']['fxa-deviceId'] if device is None: device = 'none' except KeyError: device = 'none' device_id = fxa_metrics_hash(fxa_uid + device, id_key)[:32] request.validated['device_id'] = device_id request.metrics['device_id'] = device_id
def valid_assertion(request): """Validate that the assertion given in the request is correct. If not, add errors in the response so that the client can know what happened. """ token = request.headers.get('Authorization') if token is None: raise _unauthorized() token = token.split() if len(token) != 2: raise _unauthorized() name, assertion = token if name.lower() != 'browserid': resp = _unauthorized(description='Unsupported') resp.www_authenticate = ('BrowserID', {}) raise resp try: verifier = get_verifier() with metrics_timer('tokenserver.assertion.verify', request): assertion = verifier.verify(assertion) except browserid.errors.Error as e: # Convert CamelCase to under_scores for reporting. error_type = e.__class__.__name__ error_type = re.sub('(?<=.)([A-Z])', r'_\1', error_type).lower() request.metrics['token.assertion.verify_failure'] = 1 request.metrics['token.assertion.%s' % error_type] = 1 # Log a full traceback for errors that are not a simple # "your assertion was bad and we dont trust it". if not isinstance(e, browserid.errors.TrustError): logger.exception("Unexpected verification error") # Report an appropriate error code. if isinstance(e, browserid.errors.ConnectionError): raise json_error(503, description="Resource is not available") if isinstance(e, browserid.errors.ExpiredSignatureError): raise _unauthorized("invalid-timestamp") raise _unauthorized("invalid-credentials") # FxA sign-in confirmation introduced the notion of unverified tokens. # The default value is True to preserve backwards compatibility. try: tokenVerified = assertion['idpClaims']['fxa-tokenVerified'] except KeyError: tokenVerified = True if not tokenVerified: raise _unauthorized("invalid-credentials") # everything sounds good, add the assertion to the list of validated fields # and continue request.metrics['token.assertion.verify_success'] = 1 request.validated['assertion'] = assertion id_key = request.registry.settings.get("fxa.metrics_uid_secret_key") if id_key is None: id_key = 'insecure' email = assertion['email'] fxa_uid_full = fxa_metrics_hash(email, id_key) # "legacy" key used by heka active_counts.lua request.metrics['uid'] = fxa_uid_full request.metrics['email'] = email # "new" keys use shorter values fxa_uid = fxa_uid_full[:32] request.validated['fxa_uid'] = fxa_uid request.metrics['fxa_uid'] = fxa_uid try: device = assertion['idpClaims']['fxa-deviceId'] if device is None: device = 'none' except KeyError: device = 'none' device_id = fxa_metrics_hash(fxa_uid + device, id_key)[:32] request.validated['device_id'] = device_id request.metrics['device_id'] = device_id