Skip to content

Commit 7e15258

Browse files
crwilcoxanibadde
andauthored
feat: add asyncio based auth flow (#612)
* feat: asyncio http request logic and asynchronous credentials logic (#572) Co-authored-by: Anirudh Baddepudi <[email protected]>
1 parent cc91e75 commit 7e15258

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+4949
-63
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
google.auth.credentials\_async module
2+
=====================================
3+
4+
.. automodule:: google.auth._credentials_async
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
google.auth.jwt\_async module
2+
=============================
3+
4+
.. automodule:: google.auth.jwt_async
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:

docs/reference/google.auth.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ Submodules
2424

2525
google.auth.app_engine
2626
google.auth.credentials
27+
google.auth._credentials_async
2728
google.auth.environment_vars
2829
google.auth.exceptions
2930
google.auth.iam
3031
google.auth.impersonated_credentials
3132
google.auth.jwt
33+
google.auth.jwt_async
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
google.auth.transport.aiohttp\_requests module
2+
==============================================
3+
4+
.. automodule:: google.auth.transport._aiohttp_requests
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:

docs/reference/google.auth.transport.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Submodules
1212
.. toctree::
1313
:maxdepth: 4
1414

15+
google.auth.transport._aiohttp_requests
1516
google.auth.transport.grpc
1617
google.auth.transport.mtls
1718
google.auth.transport.requests
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
google.oauth2.credentials\_async module
2+
=======================================
3+
4+
.. automodule:: google.oauth2._credentials_async
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:

docs/reference/google.oauth2.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ Submodules
1313
:maxdepth: 4
1414

1515
google.oauth2.credentials
16+
google.oauth2._credentials_async
1617
google.oauth2.id_token
1718
google.oauth2.service_account
19+
google.oauth2._service_account_async
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
google.oauth2.service\_account\_async module
2+
============================================
3+
4+
.. automodule:: google.oauth2._service_account_async
5+
:members:
6+
:inherited-members:
7+
:show-inheritance:

google/auth/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@
1818

1919
from google.auth._default import default, load_credentials_from_file
2020

21-
2221
__all__ = ["default", "load_credentials_from_file"]
2322

24-
2523
# Set default logging handler to avoid "No handler found" warnings.
2624
logging.getLogger(__name__).addHandler(logging.NullHandler())

google/auth/_credentials_async.py

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
# Copyright 2020 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
"""Interfaces for credentials."""
17+
18+
import abc
19+
import inspect
20+
21+
import six
22+
23+
from google.auth import credentials
24+
25+
26+
@six.add_metaclass(abc.ABCMeta)
27+
class Credentials(credentials.Credentials):
28+
"""Async inherited credentials class from google.auth.credentials.
29+
The added functionality is the before_request call which requires
30+
async/await syntax.
31+
All credentials have a :attr:`token` that is used for authentication and
32+
may also optionally set an :attr:`expiry` to indicate when the token will
33+
no longer be valid.
34+
35+
Most credentials will be :attr:`invalid` until :meth:`refresh` is called.
36+
Credentials can do this automatically before the first HTTP request in
37+
:meth:`before_request`.
38+
39+
Although the token and expiration will change as the credentials are
40+
:meth:`refreshed <refresh>` and used, credentials should be considered
41+
immutable. Various credentials will accept configuration such as private
42+
keys, scopes, and other options. These options are not changeable after
43+
construction. Some classes will provide mechanisms to copy the credentials
44+
with modifications such as :meth:`ScopedCredentials.with_scopes`.
45+
"""
46+
47+
async def before_request(self, request, method, url, headers):
48+
"""Performs credential-specific before request logic.
49+
50+
Refreshes the credentials if necessary, then calls :meth:`apply` to
51+
apply the token to the authentication header.
52+
53+
Args:
54+
request (google.auth.transport.Request): The object used to make
55+
HTTP requests.
56+
method (str): The request's HTTP method or the RPC method being
57+
invoked.
58+
url (str): The request's URI or the RPC service's URI.
59+
headers (Mapping): The request's headers.
60+
"""
61+
# pylint: disable=unused-argument
62+
# (Subclasses may use these arguments to ascertain information about
63+
# the http request.)
64+
65+
if not self.valid:
66+
if inspect.iscoroutinefunction(self.refresh):
67+
await self.refresh(request)
68+
else:
69+
self.refresh(request)
70+
self.apply(headers)
71+
72+
73+
class CredentialsWithQuotaProject(credentials.CredentialsWithQuotaProject):
74+
"""Abstract base for credentials supporting ``with_quota_project`` factory"""
75+
76+
77+
class AnonymousCredentials(credentials.AnonymousCredentials, Credentials):
78+
"""Credentials that do not provide any authentication information.
79+
80+
These are useful in the case of services that support anonymous access or
81+
local service emulators that do not use credentials. This class inherits
82+
from the sync anonymous credentials file, but is kept if async credentials
83+
is initialized and we would like anonymous credentials.
84+
"""
85+
86+
87+
@six.add_metaclass(abc.ABCMeta)
88+
class ReadOnlyScoped(credentials.ReadOnlyScoped):
89+
"""Interface for credentials whose scopes can be queried.
90+
91+
OAuth 2.0-based credentials allow limiting access using scopes as described
92+
in `RFC6749 Section 3.3`_.
93+
If a credential class implements this interface then the credentials either
94+
use scopes in their implementation.
95+
96+
Some credentials require scopes in order to obtain a token. You can check
97+
if scoping is necessary with :attr:`requires_scopes`::
98+
99+
if credentials.requires_scopes:
100+
# Scoping is required.
101+
credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
102+
103+
Credentials that require scopes must either be constructed with scopes::
104+
105+
credentials = SomeScopedCredentials(scopes=['one', 'two'])
106+
107+
Or must copy an existing instance using :meth:`with_scopes`::
108+
109+
scoped_credentials = _credentials_async.with_scopes(scopes=['one', 'two'])
110+
111+
Some credentials have scopes but do not allow or require scopes to be set,
112+
these credentials can be used as-is.
113+
114+
.. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
115+
"""
116+
117+
118+
class Scoped(credentials.Scoped):
119+
"""Interface for credentials whose scopes can be replaced while copying.
120+
121+
OAuth 2.0-based credentials allow limiting access using scopes as described
122+
in `RFC6749 Section 3.3`_.
123+
If a credential class implements this interface then the credentials either
124+
use scopes in their implementation.
125+
126+
Some credentials require scopes in order to obtain a token. You can check
127+
if scoping is necessary with :attr:`requires_scopes`::
128+
129+
if credentials.requires_scopes:
130+
# Scoping is required.
131+
credentials = _credentials_async.create_scoped(['one', 'two'])
132+
133+
Credentials that require scopes must either be constructed with scopes::
134+
135+
credentials = SomeScopedCredentials(scopes=['one', 'two'])
136+
137+
Or must copy an existing instance using :meth:`with_scopes`::
138+
139+
scoped_credentials = credentials.with_scopes(scopes=['one', 'two'])
140+
141+
Some credentials have scopes but do not allow or require scopes to be set,
142+
these credentials can be used as-is.
143+
144+
.. _RFC6749 Section 3.3: https://tools.ietf.org/html/rfc6749#section-3.3
145+
"""
146+
147+
148+
def with_scopes_if_required(credentials, scopes):
149+
"""Creates a copy of the credentials with scopes if scoping is required.
150+
151+
This helper function is useful when you do not know (or care to know) the
152+
specific type of credentials you are using (such as when you use
153+
:func:`google.auth.default`). This function will call
154+
:meth:`Scoped.with_scopes` if the credentials are scoped credentials and if
155+
the credentials require scoping. Otherwise, it will return the credentials
156+
as-is.
157+
158+
Args:
159+
credentials (google.auth.credentials.Credentials): The credentials to
160+
scope if necessary.
161+
scopes (Sequence[str]): The list of scopes to use.
162+
163+
Returns:
164+
google.auth._credentials_async.Credentials: Either a new set of scoped
165+
credentials, or the passed in credentials instance if no scoping
166+
was required.
167+
"""
168+
if isinstance(credentials, Scoped) and credentials.requires_scopes:
169+
return credentials.with_scopes(scopes)
170+
else:
171+
return credentials
172+
173+
174+
@six.add_metaclass(abc.ABCMeta)
175+
class Signing(credentials.Signing):
176+
"""Interface for credentials that can cryptographically sign messages."""

0 commit comments

Comments
 (0)