Ejemplo n.º 1
0
 def _decode_unverified(self, token):
     try:
         header = jwt.decode_header(token)
         payload = jwt.decode(token, verify=False)
         return header, payload
     except ValueError as error:
         raise self._invalid_token_error(str(error), cause=error)
Ejemplo n.º 2
0
def receive_messages_handler():
    # Verify that the request originates from the application.
    if (request.args.get('token', '') !=
            current_app.config['PUBSUB_VERIFICATION_TOKEN']):
        return 'Invalid request', 400

    # Verify that the push request originates from Cloud Pub/Sub.
    try:
        # Get the Cloud Pub/Sub-generated JWT in the "Authorization" header.
        bearer_token = request.headers.get('Authorization')
        token = bearer_token.split(' ')[1]
        TOKENS.append(token)

        header = jwt.decode_header(token)
        HEADERS.append(header)

        # Verify and decode the JWT. Underneath it checks the signature against
        # Google's public certs at https://www.googleapis.com/oauth2/v1/certs.
        # It also checks the token expiration time.
        claim = id_token.verify_oauth2_token(token, requests.Request())
        CLAIMS.append(claim)

        # Check the audience field in the claim. It was specified in
        # `--push-auth-token-audience` when you created the subscription.
        assert claim['aud'] == 'example.com'
    except Exception as e:
        return 'Invalid token: {}\n'.format(e), 400

    envelope = json.loads(request.data.decode('utf-8'))
    payload = base64.b64decode(envelope['message']['data'])
    MESSAGES.append(payload)
    # Returning any 2xx status indicates successful receipt of the message.
    return 'OK', 200
Ejemplo n.º 3
0
def verify_custom_token(custom_token, expected_claims, tenant_id=None):
    assert isinstance(custom_token, bytes)
    expected_email = MOCK_SERVICE_ACCOUNT_EMAIL
    if _is_emulated():
        expected_email = _token_gen.AUTH_EMULATOR_EMAIL
        token = jwt.decode(custom_token, verify=False)
    else:
        token = google.oauth2.id_token.verify_token(
            custom_token,
            testutils.MockRequest(200, MOCK_PUBLIC_CERTS),
            _token_gen.FIREBASE_AUDIENCE)
    assert token['uid'] == MOCK_UID
    assert token['iss'] == expected_email
    assert token['sub'] == expected_email
    if tenant_id is None:
        assert 'tenant_id' not in token
    else:
        assert token['tenant_id'] == tenant_id

    header = jwt.decode_header(custom_token)
    assert header.get('typ') == 'JWT'
    assert header.get('alg') == 'RS256'
    if expected_claims:
        for key, value in expected_claims.items():
            assert value == token['claims'][key]
Ejemplo n.º 4
0
def test_encode_extra_headers(signer):
    encoded = jwt.encode(signer, {}, header={'extra': 'value'})
    header = jwt.decode_header(encoded)
    assert header == {
        'typ': 'JWT',
        'alg': 'RS256',
        'kid': signer.key_id,
        'extra': 'value'
    }
Ejemplo n.º 5
0
def test_encode_extra_headers(signer):
    encoded = jwt.encode(signer, {}, header={"extra": "value"})
    header = jwt.decode_header(encoded)
    assert header == {
        "typ": "JWT",
        "alg": "RS256",
        "kid": signer.key_id,
        "extra": "value",
    }
def verify_custom_token(custom_token, expected_claims):
    assert isinstance(custom_token, six.binary_type)
    token = google.oauth2.id_token.verify_token(
        custom_token, testutils.MockRequest(200, MOCK_PUBLIC_CERTS),
        FIREBASE_AUDIENCE)
    assert token['uid'] == MOCK_UID
    assert token['iss'] == MOCK_SERVICE_ACCOUNT_EMAIL
    assert token['sub'] == MOCK_SERVICE_ACCOUNT_EMAIL
    header = jwt.decode_header(custom_token)
    assert header.get('typ') == 'JWT'
    assert header.get('alg') == 'RS256'
    if expected_claims:
        for key, value in expected_claims.items():
            assert value == token['claims'][key]
def _validate_iap_jwt(iap_jwt, expected_audience):
    try:
        # Retrieve public key for token signature verification.
        key_id = jwt.decode_header(iap_jwt).get('kid')
        if not key_id:
            return (None, None, '**ERROR: no key ID**')
        key = get_iap_key(key_id)

        # Verify token signature, expiry and audience.
        decoded_jwt = jwt.decode(iap_jwt, certs=key, audience=expected_audience)

        # Verify token issuer.
        if decoded_jwt.get('iss') != 'https://cloud.google.com/iap':
            return (None, None, '**ERROR: invalid issuer**')

        return (decoded_jwt['sub'], decoded_jwt['email'], '')
    except (ValueError, requests.exceptions.RequestException) as e:
        return (None, None, '**ERROR: JWT validation error {}**'.format(e))
Ejemplo n.º 8
0
    def _get_public_key(self, iap_jwt: str) -> Optional[str]:
        try:
            key_id = jwt.decode_header(iap_jwt).get('kid')
            if not key_id:
                return None
            if key_id not in self.keys:
                # invalidate cache
                del self.keys

                # refetch
                if key_id not in self.keys:
                    logging.error(f'public key {key_id} not found')
                    return None

            return self.keys[key_id]
        except Exception:
            logging.exception(f'failed to retrieve public keys')
            return None
Ejemplo n.º 9
0
def _verify_token(id_token):
    # First check the key ID from the token's header
    header = jwt.decode_header(id_token)
    # Get the firebase app initialized in RenewalAPI.__init__
    app = firebase_admin.get_app()
    service_acct_key_id = app.credential.signer.key_id
    if header['kid'] == service_acct_key_id:
        # This is a token signed using our Google service account's
        # private key.  It is used primarily for custom tokens
        # generated for recsystems and must be verified
        # differently; see
        # renewal_backend.utils.create_custom_token
        claims = google.oauth2.id_token.verify_token(
            id_token, REQUEST, certs_url=g.client_x509_cert_url)

    else:
        claims = google.oauth2.id_token.verify_firebase_token(
            id_token, REQUEST, g.config.firebase.project_id)

    if claims.get('renewal_role') == 'recsystem':
        # recsystem tokens have an associated token_id used to verify whether
        # the token has been revoked
        user_id = claims.get('user_id')
        token_id = claims.get('renewal_token_id')
        if not (user_id and token_id):
            return None  # implies unverified

        # Look up the recsystem _id / token_id
        try:
            filt = {'_id': ObjectId(user_id), 'token_id': token_id}
            found = g.db.recsystems.find_one(filt)
        except Exception:
            found = None

        if not found:
            return None

    return claims
Ejemplo n.º 10
0
    def verify_id_token(self, id_token):
        """Verifies the signature and data for the provided JWT.

        Accepts a signed token string, verifies that is the current, and issued
        to this project, and that it was correctly signed by Google.

        Args:
          id_token: A string of the encoded JWT.

        Returns:
          dict: A dictionary of key-value pairs parsed from the decoded JWT.

        Raises:
          ValueError: The app was not initialized with a credentials.Certificate instance.
          AppIdenityError: The JWT was found to be invalid, the message will contain details.
        """
        if not id_token:
            raise ValueError(
                'Illegal ID token provided: {0}. ID token must be a non-empty '
                'string.'.format(id_token))

        if isinstance(id_token, six.text_type):
            id_token = id_token.encode('ascii')
        if not isinstance(id_token, six.binary_type):
            raise ValueError(
                'Illegal ID token provided: {0}. ID token must be a non-empty '
                'string.'.format(id_token))

        try:
            project_id = self._app.credential.project_id
            if project_id is None:
                project_id = os.environ.get(GCLOUD_PROJECT_ENV_VAR)
        except AttributeError:
            project_id = os.environ.get(GCLOUD_PROJECT_ENV_VAR)

        if not project_id:
            raise ValueError(
                'Failed to ascertain project ID from the credential or the '
                'environment. Must initialize app with a credentials.Certificate or '
                'set your Firebase project ID as the GCLOUD_PROJECT environment '
                'variable to call verify_id_token().')

        header = jwt.decode_header(id_token)
        payload = jwt.decode(id_token, verify=False)
        issuer = payload.get('iss')
        audience = payload.get('aud')
        subject = payload.get('sub')
        expected_issuer = self.ISSUER_PREFIX + project_id

        project_id_match_msg = ('Make sure the ID token comes from the same'
                                ' Firebase project as the service account used'
                                ' to authenticate this SDK.')
        verify_id_token_msg = (
            'See https://firebase.google.com/docs/auth/admin/verify-id-tokens'
            ' for details on how to retrieve an ID token.')
        error_message = None
        if not header.get('kid'):
            if audience == self.FIREBASE_AUDIENCE:
                error_message = ('verify_id_token() expects an ID token, but '
                                 'was given a custom token.')
            elif header.get('alg') == 'HS256' and payload.get(
                    'v') is 0 and 'uid' in payload.get('d', {}):
                error_message = ('verify_id_token() expects an ID token, but '
                                 'was given a legacy custom token.')
            else:
                error_message = 'Firebase ID token has no "kid" claim.'
        elif header.get('alg') != 'RS256':
            error_message = ('Firebase ID token has incorrect algorithm. '
                             'Expected "RS256" but got "{0}". {1}'.format(
                                 header.get('alg'), verify_id_token_msg))
        elif audience != project_id:
            error_message = (
                'Firebase ID token has incorrect "aud" (audience) claim. '
                'Expected "{0}" but got "{1}". {2} {3}'.format(
                    project_id, audience, project_id_match_msg,
                    verify_id_token_msg))
        elif issuer != expected_issuer:
            error_message = (
                'Firebase ID token has incorrect "iss" (issuer) '
                'claim. Expected "{0}" but got "{1}". {2} {3}'.format(
                    expected_issuer, issuer, project_id_match_msg,
                    verify_id_token_msg))
        elif subject is None or not isinstance(subject, six.string_types):
            error_message = ('Firebase ID token has no "sub" (subject) '
                             'claim. ') + verify_id_token_msg
        elif not subject:
            error_message = ('Firebase ID token has an empty string "sub" '
                             '(subject) claim. ') + verify_id_token_msg
        elif len(subject) > 128:
            error_message = ('Firebase ID token has a "sub" (subject) '
                             'claim longer than 128 '
                             'characters. ') + verify_id_token_msg

        if error_message:
            raise ValueError(error_message)

        verified_claims = google.oauth2.id_token.verify_firebase_token(
            id_token, request=_request, audience=project_id)
        verified_claims['uid'] = verified_claims['sub']
        return verified_claims
Ejemplo n.º 11
0
    def verify(self, token, request):
        """Verifies the signature and data for the provided JWT."""
        token = token.encode('utf-8') if isinstance(token,
                                                    six.text_type) else token
        if not isinstance(token, six.binary_type) or not token:
            raise ValueError(
                'Illegal {0} provided: {1}. {0} must be a non-empty '
                'string.'.format(self.short_name, token))

        if not self.project_id:
            raise ValueError(
                'Failed to ascertain project ID from the credential or the environment. Project '
                'ID is required to call {0}. Initialize the app with a credentials.Certificate '
                'or set your Firebase project ID as an app option. Alternatively set the '
                'GOOGLE_CLOUD_PROJECT environment variable.'.format(
                    self.operation))

        header = jwt.decode_header(token)
        payload = jwt.decode(token, verify=False)
        issuer = payload.get('iss')
        audience = payload.get('aud')
        subject = payload.get('sub')
        expected_issuer = self.issuer + self.project_id

        project_id_match_msg = (
            'Make sure the {0} comes from the same Firebase project as the service account used '
            'to authenticate this SDK.'.format(self.short_name))
        verify_id_token_msg = (
            'See {0} for details on how to retrieve {1}.'.format(
                self.url, self.short_name))

        error_message = None
        if not header.get('kid'):
            if audience == FIREBASE_AUDIENCE:
                error_message = ('{0} expects {1}, but was given a custom '
                                 'token.'.format(self.operation,
                                                 self.articled_short_name))
            elif header.get('alg') == 'HS256' and payload.get(
                    'v') is 0 and 'uid' in payload.get('d', {}):
                error_message = (
                    '{0} expects {1}, but was given a legacy custom '
                    'token.'.format(self.operation, self.articled_short_name))
            else:
                error_message = 'Firebase {0} has no "kid" claim.'.format(
                    self.short_name)
        elif header.get('alg') != 'RS256':
            error_message = (
                'Firebase {0} has incorrect algorithm. Expected "RS256" but got '
                '"{1}". {2}'.format(self.short_name, header.get('alg'),
                                    verify_id_token_msg))
        elif audience != self.project_id:
            error_message = (
                'Firebase {0} has incorrect "aud" (audience) claim. Expected "{1}" but '
                'got "{2}". {3} {4}'.format(self.short_name, self.project_id,
                                            audience, project_id_match_msg,
                                            verify_id_token_msg))
        elif issuer != expected_issuer:
            error_message = (
                'Firebase {0} has incorrect "iss" (issuer) claim. Expected "{1}" but '
                'got "{2}". {3} {4}'.format(self.short_name, expected_issuer,
                                            issuer, project_id_match_msg,
                                            verify_id_token_msg))
        elif subject is None or not isinstance(subject, six.string_types):
            error_message = ('Firebase {0} has no "sub" (subject) claim. '
                             '{1}'.format(self.short_name,
                                          verify_id_token_msg))
        elif not subject:
            error_message = (
                'Firebase {0} has an empty string "sub" (subject) claim. '
                '{1}'.format(self.short_name, verify_id_token_msg))
        elif len(subject) > 128:
            error_message = (
                'Firebase {0} has a "sub" (subject) claim longer than 128 characters. '
                '{1}'.format(self.short_name, verify_id_token_msg))

        if error_message:
            raise ValueError(error_message)

        verified_claims = google.oauth2.id_token.verify_token(
            token,
            request=request,
            audience=self.project_id,
            certs_url=self.cert_url)
        verified_claims['uid'] = verified_claims['sub']
        return verified_claims
Ejemplo n.º 12
0
def test_encode_custom_alg_in_headers(signer):
    encoded = jwt.encode(signer, {}, header={"alg": "foo"})
    header = jwt.decode_header(encoded)
    assert header == {"typ": "JWT", "alg": "foo", "kid": signer.key_id}