示例#1
0
def before_request():
    if request.method == 'OPTIONS':
        return make_response(jsonify(ok=True), 204)
    auth_header = request.headers.get('Authorization')
    if auth_header:
        jwt_token = auth_header.split('Bearer ')[-1]
    else:
        return abort(403, 'no JWT token found on request')
    user_data = jwt.decode(jwt_token, verify=False)
    user_obj = store.user(user_data['email'])
    if user_obj is None:
        return abort(403, f"{user_data['email']} doesn't have access")
    g.current_user = user_obj
示例#2
0
def id_token_jwt_grant(request, token_uri, assertion):
    """Implements the JWT Profile for OAuth 2.0 Authorization Grants, but
    requests an OpenID Connect ID Token instead of an access token.

    This is a variant on the standard JWT Profile that is currently unique
    to Google. This was added for the benefit of authenticating to services
    that require ID Tokens instead of access tokens or JWT bearer tokens.

    Args:
        request (google.auth.transport.Request): A callable used to make
            HTTP requests.
        token_uri (str): The OAuth 2.0 authorization server's token endpoint
            URI.
        assertion (str): JWT token signed by a service account. The token's
            payload must include a ``target_audience`` claim.

    Returns:
        Tuple[str, Optional[datetime], Mapping[str, str]]:
            The (encoded) Open ID Connect ID Token, expiration, and additional
            data returned by the endpoint.

    Raises:
        google.auth.exceptions.RefreshError: If the token endpoint returned
            an error.
    """
    body = {
        'assertion': assertion,
        'grant_type': _JWT_GRANT_TYPE,
    }

    response_data = _token_endpoint_request(request, token_uri, body)

    try:
        id_token = response_data['id_token']
    except KeyError as caught_exc:
        new_exc = exceptions.RefreshError(
            'No ID token in response.', response_data)
        six.raise_from(new_exc, caught_exc)

    payload = jwt.decode(id_token, verify=False)
    expiry = datetime.datetime.utcfromtimestamp(payload['exp'])

    return id_token, expiry, response_data
def verify_token(id_token, request, audience=None,
                 certs_url=_GOOGLE_OAUTH2_CERTS_URL):
    """Verifies an ID token and returns the decoded token.

    Args:
        id_token (Union[str, bytes]): The encoded token.
        request (google.auth.transport.Request): The object used to make
            HTTP requests.
        audience (str): The audience that this token is intended for. If None
            then the audience is not verified.
        certs_url (str): The URL that specifies the certificates to use to
            verify the token. This URL should return JSON in the format of
            ``{'key id': 'x509 certificate'}``.

    Returns:
        Mapping[str, Any]: The decoded token.
    """
    certs = _fetch_certs(request, certs_url)

    return jwt.decode(id_token, certs=certs, audience=audience)
 def _verify_token(self, token):
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
     return payload
示例#5
0
 def test__make_authorization_grant_assertion_scoped(self):
     scopes = ['email', 'profile']
     credentials = self.credentials.with_scopes(scopes)
     token = credentials._make_authorization_grant_assertion()
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload['scope'] == 'email profile'
def test_decode_multicert_bad_cert(token_factory):
    certs = {"1": OTHER_CERT_BYTES, "2": PUBLIC_CERT_BYTES}
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token_factory(), certs)
    assert excinfo.match(r"Could not verify token signature")
def test_decode_no_key_id(token_factory):
    token = token_factory(key_id=False)
    certs = {"2": PUBLIC_CERT_BYTES}
    payload = jwt.decode(token, certs)
    assert payload["user"] == "billy bob"
def test_decode_bad_token_not_json():
    token = b".".join([base64.urlsafe_b64encode(b"123!")] * 3)
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token, PUBLIC_CERT_BYTES)
    assert excinfo.match(r"Can\'t parse segment")
def test_decode_bad_token_wrong_audience_list(token_factory):
    token = token_factory()
    audience = ["*****@*****.**", "*****@*****.**"]
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience)
    assert excinfo.match(r"Token has wrong audience")
示例#10
0
def test_decode_valid(token_factory):
    payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
    assert payload['aud'] == '*****@*****.**'
    assert payload['user'] == 'billy bob'
    assert payload['metadata']['meta'] == 'data'
示例#11
0
    def Run(self, args):
        """Run the authentication command."""

        scopes = config.CLOUDSDK_SCOPES
        # Add REAUTH scope in case the user has 2fact activated.
        # This scope is only used here and when refreshing the access token.
        scopes += (config.REAUTH_SCOPE, )

        if args.enable_gdrive_access:
            scopes += (auth_util.GOOGLE_DRIVE_SCOPE, )

        if c_devshell.IsDevshellEnvironment():
            if c_devshell.HasDevshellAuth():
                message = textwrap.dedent("""
            You are already authenticated with gcloud when running
            inside the Cloud Shell and so do not need to run this
            command. Do you wish to proceed anyway?
            """)
                answer = console_io.PromptContinue(message=message)
                if not answer:
                    return None
        elif c_gce.Metadata().connected:
            message = textwrap.dedent("""
          You are running on a Google Compute Engine virtual machine.
          It is recommended that you use service accounts for authentication.

          You can run:

            $ gcloud config set account `ACCOUNT`

          to switch accounts if necessary.

          Your credentials may be visible to others with access to this
          virtual machine. Are you sure you want to authenticate with
          your personal account?
          """)
            answer = console_io.PromptContinue(message=message)
            if not answer:
                return None

        account = args.account

        if account and not args.force:
            try:
                creds = c_store.Load(account=account, scopes=scopes)
            except c_store.Error:
                creds = None
            if creds:
                # Account already has valid creds, just switch to it.
                log.warning(
                    'Re-using locally stored credentials for [{}]. '
                    'To fetch new credentials, re-run the command with the '
                    '`--force` flag.'.format(account))
                return LoginAs(account, creds, args.project, args.activate,
                               args.brief, args.update_adc,
                               args.add_quota_project_to_adc)

        # No valid creds, do the web flow.
        launch_browser = check_browser.ShouldLaunchBrowser(args.launch_browser)
        if args.use_oauth2client:
            creds = auth_util.DoInstalledAppBrowserFlow(launch_browser, scopes)
            web_flow_account = creds.id_token['email']
        else:
            creds = auth_util.DoInstalledAppBrowserFlowGoogleAuth(
                launch_browser, scopes)
            decoded_id_token = jwt.decode(creds.id_token, verify=False)
            web_flow_account = decoded_id_token['email']
        if account and account.lower() != web_flow_account.lower():
            raise auth_exceptions.WrongAccountError(
                'You attempted to log in as account [{account}] but the received '
                'credentials were for account [{web_flow_account}].\n\n'
                'Please check that your browser is logged in as account [{account}] '
                'and that you are using the correct browser profile.'.format(
                    account=account, web_flow_account=web_flow_account))

        account = web_flow_account
        # We got new creds, and they are for the correct user.
        c_store.Store(creds, account, scopes)
        return LoginAs(account, creds, args.project, args.activate, args.brief,
                       args.update_adc, args.add_quota_project_to_adc)
示例#12
0
def test_decode_no_key_id(token_factory):
    token = token_factory(key_id=False)
    certs = {'2': PUBLIC_CERT_BYTES}
    payload = jwt.decode(token, certs)
    assert payload['user'] == 'billy bob'
示例#13
0
def test_roundtrip_explicit_key_id(token_factory):
    token = token_factory(key_id='3')
    certs = {'2': OTHER_CERT_BYTES, '3': PUBLIC_CERT_BYTES}
    payload = jwt.decode(token, certs)
    assert payload['user'] == 'billy bob'
示例#14
0
def test_decode_bad_token_not_base64():
    with pytest.raises((ValueError, TypeError)) as excinfo:
        jwt.decode('1.2.3', PUBLIC_CERT_BYTES)
    assert excinfo.match(r'Incorrect padding')
示例#15
0
def test_decode_valid_unverified(token_factory):
    payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
    assert payload['aud'] == '*****@*****.**'
    assert payload['user'] == 'billy bob'
    assert payload['metadata']['meta'] == 'data'
示例#16
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 JWT was found to be invalid, or the app was not initialized with a
              credentials.Certificate instance.
        """
        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
示例#17
0
 def test__make_authorization_grant_assertion(self):
     credentials = self.make_credentials()
     token = credentials._make_authorization_grant_assertion()
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
     assert payload["aud"] == service_account._GOOGLE_OAUTH2_TOKEN_ENDPOINT
示例#18
0
    async def build_user_creds(
        self,
        grant,
        client_creds=None,
        grant_type=AUTH_CODE_GRANT_TYPE,
        nonce=None,
        hd=None,
        verify=True,
    ):
        """
        Second step of Oauth2 authrization code flow and OpenID connect. Creates a User Creds object with access and refresh token

        Arguments:

            grant (str):
            
                * Aka: "code". 
                
                * The code received at your redirect URI from the auth callback

            client_creds (aiogoogle.auth.creds.ClientCreds):

                * Dict with client_id and client_secret items

            grant_type (str):

                * Optional

                * OAuth2 grant type

                * defaults to ``code`` (Authorization code flow)

            nonce (str):

                * Random value that prevents replay attacks

                * pass the one you used with ``self.authorization_url()`` method

            hd (str):

                * Optional

                * hosted domain for G-suite

                * used for id_token verification

            verify (str):

                * Optional

                * Whether or not to verify the received id_token

                * Unless you're building a speed critical application AND you're receiving your tokens directly from Google, you should leave this as True.

        Returns:

            aiogoogle.auth.creds.UserCreds: User Credentials with the following items:

                * ``access_token``

                * ``refresh_token``

                * ``expires_in`` (JSON format ISO 8601)

                * ``token_type`` always set to bearer

                * ``scopes``

        Raises:

            aiogoogle.excs.AuthError: Auth Error
        """
        client_creds = client_creds or self.client_creds
        user_creds = await super().build_user_creds(grant,
                                                    client_creds,
                                                    grant_type=grant_type)
        user_creds["id_token_jwt"] = user_creds["id_token"]
        if verify is False:
            user_creds["id_token"] = jwt.decode(user_creds["id_token_jwt"],
                                                verify=False)
        else:
            user_creds["id_token"] = await self.decode_and_validate(
                user_creds["id_token_jwt"], client_creds["client_id"], nonce,
                hd)
        return user_creds
def test_decode_bad_token_not_base64():
    with pytest.raises((ValueError, TypeError)) as excinfo:
        jwt.decode("1.2.3", PUBLIC_CERT_BYTES)
    assert excinfo.match(r"Incorrect padding|more than a multiple of 4")
示例#20
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
def test_decode_bad_token_no_iat_or_exp(signer):
    token = jwt.encode(signer, {"test": "value"})
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token, PUBLIC_CERT_BYTES)
    assert excinfo.match(r"Token does not contain required claim")
示例#22
0
def decodeUser(JWT):
    sub = jwt.decode(JWT, verify=False)
    user = sub['sub']
    return user
def test_decode_wrong_cert(token_factory):
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token_factory(), OTHER_CERT_BYTES)
    assert excinfo.match(r"Could not verify token signature")
 def test__make_authorization_grant_assertion(self):
     credentials = self.make_credentials()
     token = credentials._make_authorization_grant_assertion()
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload["iss"] == self.SERVICE_ACCOUNT_EMAIL
     assert payload["aud"] == self.TOKEN_URI
def test_decode_no_cert(token_factory):
    certs = {"2": PUBLIC_CERT_BYTES}
    with pytest.raises(ValueError) as excinfo:
        jwt.decode(token_factory(), certs)
    assert excinfo.match(r"Certificate for key id 1 not found")
def test_decode_valid(token_factory):
    payload = jwt.decode(token_factory(), certs=PUBLIC_CERT_BYTES)
    assert payload["aud"] == "*****@*****.**"
    assert payload["user"] == "billy bob"
    assert payload["metadata"]["meta"] == "data"
def test_roundtrip_explicit_key_id(token_factory):
    token = token_factory(key_id="3")
    certs = {"2": OTHER_CERT_BYTES, "3": PUBLIC_CERT_BYTES}
    payload = jwt.decode(token, certs)
    assert payload["user"] == "billy bob"
def test_decode_valid_es256(token_factory):
    payload = jwt.decode(token_factory(use_es256_signer=True),
                         certs=EC_PUBLIC_CERT_BYTES)
    assert payload["aud"] == "*****@*****.**"
    assert payload["user"] == "billy bob"
    assert payload["metadata"]["meta"] == "data"
示例#29
0
 def test__make_authorization_grant_assertion(self):
     token = self.credentials._make_authorization_grant_assertion()
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload['iss'] == self.SERVICE_ACCOUNT_EMAIL
     assert payload['aud'] == self.TOKEN_URI
def _verify_mocked_oauth2_token(token, request, audience):
    claims = jwt.decode(token, certs=PUBLIC_CERT_BYTES, verify=True)
    return claims
示例#31
0
 def test__make_authorization_grant_assertion_subject(self):
     subject = '*****@*****.**'
     credentials = self.credentials.with_subject(subject)
     token = credentials._make_authorization_grant_assertion()
     payload = jwt.decode(token, PUBLIC_CERT_BYTES)
     assert payload['sub'] == subject
def test_decode_bad_token_wrong_number_of_segments():
    with pytest.raises(ValueError) as excinfo:
        jwt.decode("1.2", PUBLIC_CERT_BYTES)
    assert excinfo.match(r"Wrong number of segments")
def test_decode_valid_unverified(token_factory):
    payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False)
    assert payload["aud"] == "*****@*****.**"
    assert payload["user"] == "billy bob"
    assert payload["metadata"]["meta"] == "data"
示例#34
0
 def __decrypt_user_details(self, encrypted_text):
     # public_key = Config.certs_dict['db02ab30e0b75b8ecd4f816bb9e1978f62849894']
     # public_key = b'-----BEGIN CERTIFICATE-----\nMIIDJjCCAg6gAwIBAgIIJqxqI3wDgKkwDQYJKoZIhvcNAQEFBQAwNjE0MDIGA1UE\nAxMrZmVkZXJhdGVkLXNpZ25vbi5zeXN0ZW0uZ3NlcnZpY2VhY2NvdW50LmNvbTAe\nFw0xOTExMjAxNDQ5MzVaFw0xOTEyMDcwMzA0MzVaMDYxNDAyBgNVBAMTK2ZlZGVy\nYXRlZC1zaWdub24uc3lzdGVtLmdzZXJ2aWNlYWNjb3VudC5jb20wggEiMA0GCSqG\nSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCov+058bzmz1a7ZqjCIXe8+WxfxiRI5sGI\nzdf66e6dhSYmt/7Mn2bShJn0lc7FfDwK9JDuP6Kw9iifmfmh3qqhRh7lgus6zHuU\nxNC04U1LDU/jhtU3Gwm+41bQaaQhtYWmBCPmUgwpw0DmQAIIp0RiO+sWw614havs\nbLIz8gSfCmqNmGmXJTHj41npZD3T5BpYIWAZzPTKsrtmnkseNjBvRTLR7j6Q6jAM\nCM4ozH/6lA2sIN1ehBwfi/vT0hlfG80uy1EDNbngS1eBKW4Wvglrreyy3h0KatKc\nedcqMhp4f7734m0OpoojdzzHytBJHXzxrm/tNWCAxuYU57W2ikiFAgMBAAGjODA2\nMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQMMAoGCCsG\nAQUFBwMCMA0GCSqGSIb3DQEBBQUAA4IBAQA4gy3aHdPnEavhc91XB4bWvagPG5es\n/8aQtJdztDx0VGcWOe3BaBKRysujADJd/lRewKDLXNBeyOWwYEhp+5yOgV1W8Tzo\nF9JAS5i4oNMnuBfqxHHr7wXOLfK3ofTWbDDYMyhae8OeKQOW3zqj4No2J28jJ9J6\nZfChp80w2njkq4ySe+/KEX2sF4CLkjo/wtJADyKNs0bwq9HcVs6bklV/OMbfTJC9\nyLhI/X4Rpp4hRhqZk/lXZVBP2i79sY+ZgTvYzmfLwRdCJmx/AsNBrnMVlgBuGHcl\n5HUElpzuDphYkSWVM8Hkjf7qKLki/bW+NWLQYHvxKU8USyrHCOHLWtht\n-----END CERTIFICATE-----\n'
     user_details = jwt.decode(encrypted_text, verify=False)
     return user_details