예제 #1
0
def valid_authorization(request, **kwargs):
    """Validate that the Authorization on the request is correct.

    If not, add errors in the response so that the client can know what
    happened.
    """
    authz = request.headers.get('Authorization')
    if authz is None:
        raise _unauthorized()

    authz = authz.split(None, 1)
    if len(authz) != 2:
        raise _unauthorized()
    name, token = authz

    if name.lower() == 'browserid':
        _validate_browserid_assertion(request, token)
    elif name.lower() == 'bearer':
        _validate_oauth_token(request, token)
    else:
        resp = _unauthorized(description='Unsupported')
        resp.www_authenticate = ('BrowserID', {})
        raise resp

    authorization = request.validated['authorization']
    email = authorization['email']
    request.validated['fxa_uid'] = email.split("@", 1)[0]

    # For metrics purposes we expose a "anonymized" version of the
    # FxA uid which as been hmaced with a server-side secret key.
    # We call this the "metrics uid" and it correlates to identifiers
    # used in other systems, such as client-side telemetry.
    #
    # For legacy reasons the active_counts.lua script expects a longer
    # id stored in the key "uid", while other scripts can accept a
    # shorter id stored in the key "metrics_uid".
    request.metrics['email'] = email
    id_key = request.registry.settings.get("fxa.metrics_uid_secret_key")
    if id_key is None:
        id_key = 'insecure'
    hashed_fxa_uid_full = fxa_metrics_hash(email, id_key)
    hashed_fxa_uid = hashed_fxa_uid_full[:32]
    request.metrics['uid'] = hashed_fxa_uid_full
    request.metrics['metrics_uid'] = hashed_fxa_uid

    # Similarly, we expose an "anonymized" device-id for metrics purposes
    # where available.
    try:
        device = authorization['idpClaims']['fxa-deviceId']
        if device is None:
            device = 'none'
    except KeyError:
        device = 'none'
    hashed_device_id = fxa_metrics_hash(hashed_fxa_uid + device, id_key)[:32]
    request.metrics['metrics_device_id'] = hashed_device_id

    # We also pass the metrics id back to the client so it
    # can include that in its own metrics events.
    request.validated['hashed_fxa_uid'] = hashed_fxa_uid
    request.validated['hashed_device_id'] = hashed_device_id
예제 #2
0
def valid_authorization(request, **kwargs):
    """Validate that the Authorization on the request is correct.

    If not, add errors in the response so that the client can know what
    happened.
    """
    authz = request.headers.get('Authorization')
    if authz is None:
        raise _unauthorized()

    authz = authz.split(None, 1)
    if len(authz) != 2:
        raise _unauthorized()
    name, token = authz

    if name.lower() == 'browserid':
        _validate_browserid_assertion(request, token)
    elif name.lower() == 'bearer':
        _validate_oauth_token(request, token)
    else:
        resp = _unauthorized(description='Unsupported')
        resp.www_authenticate = ('BrowserID', {})
        raise resp

    authorization = request.validated['authorization']
    email = authorization['email']
    request.validated['fxa_uid'] = email.split("@", 1)[0]

    # For metrics purposes we expose a "anonymized" version of the
    # FxA uid which as been hmaced with a server-side secret key.
    # We call this the "metrics uid" and it correlates to identifiers
    # used in other systems, such as client-side telemetry.
    #
    # For legacy reasons the active_counts.lua script expects a longer
    # id stored in the key "uid", while other scripts can accept a
    # shorter id stored in the key "metrics_uid".
    request.metrics['email'] = email
    id_key = request.registry.settings.get("fxa.metrics_uid_secret_key")
    if id_key is None:
        id_key = 'insecure'
    hashed_fxa_uid_full = fxa_metrics_hash(email, id_key)
    hashed_fxa_uid = hashed_fxa_uid_full[:32]
    request.metrics['uid'] = hashed_fxa_uid_full
    request.metrics['metrics_uid'] = hashed_fxa_uid

    # Similarly, we expose an "anonymized" device-id for metrics purposes
    # where available.
    try:
        device = authorization['idpClaims']['fxa-deviceId']
        if device is None:
            device = 'none'
    except KeyError:
        device = 'none'
    hashed_device_id = fxa_metrics_hash(hashed_fxa_uid + device, id_key)[:32]
    request.metrics['metrics_device_id'] = hashed_device_id

    # We also pass the metrics id back to the client so it
    # can include that in its own metrics events.
    request.validated['hashed_fxa_uid'] = hashed_fxa_uid
    request.validated['hashed_device_id'] = hashed_device_id
예제 #3
0
def _valid_browserid_assertion(request, assertion):
    try:
        verifier = get_browserid_verifier(request.registry)
        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['authorization'] = 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
예제 #4
0
def _valid_oauth_token(request, token):
    try:
        verifier = get_oauth_verifier(request.registry)
        with metrics_timer('tokenserver.oauth.verify', request):
            token = verifier.verify(token)
    except (fxa.errors.Error, ConnectionError) as e:
        request.metrics['token.oauth.verify_failure'] = 1
        if isinstance(e, fxa.errors.InProtocolError):
            request.metrics['token.oauth.errno.%s' % e.errno] = 1
        # Log a full traceback for errors that are not a simple
        # "your token was bad and we dont trust it".
        if not isinstance(e, fxa.errors.TrustError):
            logger.exception("Unexpected verification error")
        # Report an appropriate error code.
        if isinstance(e, ConnectionError):
            request.metrics['token.oauth.connection_error'] = 1
            raise json_error(503, description="Resource is not available")
        raise _unauthorized("invalid-credentials")

    request.metrics['token.oauth.verify_success'] = 1
    request.validated['authorization'] = token

    # OAuth clients should send the scoped-key kid in lieu of X-Client-State.
    # A future enhancement might allow us to learn this from the OAuth
    # verification response rather than requiring a separate header.
    kid = request.headers.get('X-KeyID')
    if kid:
        try:
            # The kid combines a timestamp and a hash of the key material,
            # so we can decode it into equivalent information to what we
            # get out of a BrowserID assertion.
            generation, client_state = kid.split("-", 1)
            generation = int(generation)
            idpClaims = request.validated['authorization']['idpClaims']
            idpClaims['fxa-generation'] = generation
            client_state = browserid.utils.decode_bytes(client_state)
            client_state = client_state.encode('hex')
            if not 1 <= len(client_state) <= 32:
                raise json_error(400, location='header', name='X-Client-State',
                                 description='Invalid client state value')
            # Sanity-check in case the client sent *both* headers.
            # If they don't match, the client is definitely confused.
            if 'X-Client-State' in request.headers:
                if request.headers['X-Client-State'] != client_state:
                    raise _unauthorized("invalid-client-state")
            request.validated['client-state'] = client_state
        except (IndexError, ValueError):
            raise _unauthorized("invalid-credentials")

    id_key = request.registry.settings.get("fxa.metrics_uid_secret_key")
    if id_key is None:
        id_key = 'insecure'
    email = token['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

    # There's currently no notion of a "device id" in OAuth.
    # In future we might be able to use e.g. the refresh token
    # or some derivative of it here.
    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
예제 #5
0
파일: views.py 프로젝트: eXfio/tokenserver
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
예제 #6
0
def valid_authorization(request, **kwargs):
    """Validate that the Authorization on the request is correct and valid.

    If authorization is valid, this validator populates user information into
    `request.validated` as follows:

      * `authorization`: a dict of information about the user:
        * `email`: user id in the form "{userid}@{issuer}"
        * `idpClaims`: a dict of optional extra claims from the IdP:
          * `fxa-generation`: timestamp at which user credentials last changed
          * `fxa-keysChangedAt`: timestamp at which user keys last changed
      * `fxa_uid`: the userid component of `authorization.email`
      * `client_state`: hash of the user's key material, as a hex string
      * `hashed_fxa_uid`: hmaced `fxa_uid`, to use for metrics
      * `hashed_device_id`: hmaced device identifier, to use for metrics

    If authorization is not valid, this validator adds errors in the response
    so that the client can know what happened.
    """
    authz = request.headers.get('Authorization')
    if authz is None:
        raise _unauthorized()

    authz = authz.split(None, 1)
    if len(authz) != 2:
        raise _unauthorized()
    name, token = authz

    if name.lower() == 'browserid':
        _validate_browserid_assertion(request, token)
    elif name.lower() == 'bearer':
        _validate_oauth_token(request, token)
    else:
        resp = _unauthorized(description='Unsupported')
        resp.www_authenticate = ('BrowserID', {})
        raise resp

    authorization = request.validated['authorization']
    email = authorization['email']
    request.validated['fxa_uid'] = email.split("@", 1)[0]

    # For metrics purposes we expose a "anonymized" version of the
    # FxA uid which as been hmaced with a server-side secret key.
    # We call this the "metrics uid" and it correlates to identifiers
    # used in other systems, such as client-side telemetry.
    #
    # For legacy reasons the active_counts.lua script expects a longer
    # id stored in the key "uid", while other scripts can accept a
    # shorter id stored in the key "metrics_uid".
    request.metrics['email'] = email
    id_key = request.registry.settings.get("fxa.metrics_uid_secret_key")
    if id_key is None:
        id_key = 'insecure'
    hashed_fxa_uid_full = fxa_metrics_hash(email, id_key)
    hashed_fxa_uid = hashed_fxa_uid_full[:32]
    request.metrics['uid'] = hashed_fxa_uid_full
    request.metrics['metrics_uid'] = hashed_fxa_uid

    # Similarly, we expose an "anonymized" device-id for metrics purposes
    # where available.
    try:
        device = authorization['idpClaims']['fxa-deviceId']
        if device is None:
            device = 'none'
    except KeyError:
        device = 'none'
    hashed_device_id = fxa_metrics_hash(hashed_fxa_uid + device, id_key)[:32]
    request.metrics['metrics_device_id'] = hashed_device_id

    # We also pass the metrics id back to the client so it
    # can include that in its own metrics events.
    request.validated['hashed_fxa_uid'] = hashed_fxa_uid
    request.validated['hashed_device_id'] = hashed_device_id