Codebase list google-auth-oauthlib / ffd1993
Import upstream version 1.0.0 Debian Janitor 1 year, 30 days ago
17 changed file(s) with 452 addition(s) and 275 deletion(s). Raw diff Collapse all Expand all
0 Apache License
0
1 Apache License
12 Version 2.0, January 2004
2 https://www.apache.org/licenses/
3 http://www.apache.org/licenses/
34
45 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
56
191192 you may not use this file except in compliance with the License.
192193 You may obtain a copy of the License at
193194
194 https://www.apache.org/licenses/LICENSE-2.0
195 http://www.apache.org/licenses/LICENSE-2.0
195196
196197 Unless required by applicable law or agreed to in writing, software
197198 distributed under the License is distributed on an "AS IS" BASIS,
1515
1616 # Generated by synthtool. DO NOT EDIT!
1717 include README.rst LICENSE
18 recursive-include google *.json *.proto
18 recursive-include google *.json *.proto py.typed
1919 recursive-include tests *
2020 global-exclude *.py[co]
2121 global-exclude __pycache__
2222
2323 # Exclude scripts for samples readmegen
24 prune scripts/readme-gen
24 prune scripts/readme-gen
00 Metadata-Version: 2.1
11 Name: google-auth-oauthlib
2 Version: 0.4.2
2 Version: 1.0.0
33 Summary: Google Authentication Library
44 Home-page: https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib
55 Author: Google Cloud Platform
66 Author-email: jonwayne+google-auth@google.com
77 License: Apache 2.0
8 Description: oauthlib integration for Google Auth
9 ====================================
10
11 |pypi|
12
13 This library provides `oauthlib`_ integration with `google-auth`_.
14
15 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=master
16 :target: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib
17 .. |docs| image:: https://readthedocs.org/projects/google-auth-oauthlib/badge/?version=latest
18 :target: https://google-auth-oauthlib.readthedocs.io/en/latest/
19 .. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
20 :target: https://pypi.python.org/pypi/google-auth-oauthlib
21
22 .. _oauthlib: https://github.com/idan/oauthlib
23 .. _google-auth: https://github.com/googleapis/google-auth-library-python
24
25 Installing
26 ----------
27
28 You can install using `pip`_::
29
30 $ pip install google-auth-oauthlib
31
32 .. _pip: https://pip.pypa.io/en/stable/
33
34 Documentation
35 -------------
36
37 The latest documentation is available at `google-auth-oauthlib.readthedocs.io`_.
38
39 .. _google-auth-oauthlib.readthedocs.io: http://google-auth-oauthlib.readthedocs.io/
40
41 License
42 -------
43
44 Apache 2.0 - See `the LICENSE`_ for more information.
45
46 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/master/LICENSE
47
488 Keywords: google auth oauth client oauthlib
49 Platform: UNKNOWN
509 Classifier: Programming Language :: Python :: 3
5110 Classifier: Programming Language :: Python :: 3.6
5211 Classifier: Programming Language :: Python :: 3.7
5312 Classifier: Programming Language :: Python :: 3.8
5413 Classifier: Programming Language :: Python :: 3.9
55 Classifier: Development Status :: 3 - Alpha
14 Classifier: Programming Language :: Python :: 3.10
15 Classifier: Programming Language :: Python :: 3.11
16 Classifier: Development Status :: 5 - Production/Stable
5617 Classifier: Intended Audience :: Developers
5718 Classifier: License :: OSI Approved :: Apache Software License
5819 Classifier: Operating System :: POSIX
6223 Classifier: Topic :: Internet :: WWW/HTTP
6324 Requires-Python: >=3.6
6425 Provides-Extra: tool
26 License-File: LICENSE
27
28 oauthlib integration for Google Auth
29 ====================================
30
31 |pypi|
32
33 This library provides `oauthlib`_ integration with `google-auth`_.
34
35 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=main
36 :target: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
37 .. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
38 :target: https://pypi.python.org/pypi/google-auth-oauthlib
39
40 .. _oauthlib: https://github.com/idan/oauthlib
41 .. _google-auth: https://github.com/googleapis/google-auth-library-python
42
43 Installing
44 ----------
45
46 You can install using `pip`_::
47
48 $ pip install google-auth-oauthlib
49
50 .. _pip: https://pip.pypa.io/en/stable/
51
52 Documentation
53 -------------
54
55 The latest documentation is available at `google-auth-oauthlib.googleapis.dev`_.
56
57 .. _google-auth-oauthlib.googleapis.dev: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
58
59 Supported Python Versions
60 -------------------------
61 Python >= 3.6
62
63
64 Unsupported Python Versions
65 ---------------------------
66
67 Python == 2.7, Python == 3.5.
68
69 The last version of this library compatible with Python 2.7 and 3.5 is
70 `google-auth-oauthlib==0.4.1`.
71
72 License
73 -------
74
75 Apache 2.0 - See `the LICENSE`_ for more information.
76
77 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/main/LICENSE
44
55 This library provides `oauthlib`_ integration with `google-auth`_.
66
7 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=master
8 :target: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib
9 .. |docs| image:: https://readthedocs.org/projects/google-auth-oauthlib/badge/?version=latest
10 :target: https://google-auth-oauthlib.readthedocs.io/en/latest/
7 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=main
8 :target: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
119 .. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
1210 :target: https://pypi.python.org/pypi/google-auth-oauthlib
1311
2624 Documentation
2725 -------------
2826
29 The latest documentation is available at `google-auth-oauthlib.readthedocs.io`_.
27 The latest documentation is available at `google-auth-oauthlib.googleapis.dev`_.
3028
31 .. _google-auth-oauthlib.readthedocs.io: http://google-auth-oauthlib.readthedocs.io/
29 .. _google-auth-oauthlib.googleapis.dev: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
30
31 Supported Python Versions
32 -------------------------
33 Python >= 3.6
34
35
36 Unsupported Python Versions
37 ---------------------------
38
39 Python == 2.7, Python == 3.5.
40
41 The last version of this library compatible with Python 2.7 and 3.5 is
42 `google-auth-oauthlib==0.4.1`.
3243
3344 License
3445 -------
3546
3647 Apache 2.0 - See `the LICENSE`_ for more information.
3748
38 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/master/LICENSE
49 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/main/LICENSE
1414 """OAuth 2.0 Authorization Flow
1515
1616 This module provides integration with `requests-oauthlib`_ for running the
17 `OAuth 2.0 Authorization Flow`_ and acquiring user credentials.
18
19 Here's an example of using :class:`Flow` with the installed application
20 authorization flow::
21
22 from google_auth_oauthlib.flow import Flow
17 `OAuth 2.0 Authorization Flow`_ and acquiring user credentials. See
18 `Using OAuth 2.0 to Access Google APIs`_ for an overview of OAuth 2.0
19 authorization scenarios Google APIs support.
20
21 Here's an example of using :class:`InstalledAppFlow`::
22
23 from google_auth_oauthlib.flow import InstalledAppFlow
2324
2425 # Create the flow using the client secrets file from the Google API
2526 # Console.
26 flow = Flow.from_client_secrets_file(
27 'path/to/client_secrets.json',
28 scopes=['profile', 'email'],
29 redirect_uri='urn:ietf:wg:oauth:2.0:oob')
30
31 # Tell the user to go to the authorization URL.
32 auth_url, _ = flow.authorization_url(prompt='consent')
33
34 print('Please go to this URL: {}'.format(auth_url))
35
36 # The user will get an authorization code. This code is used to get the
37 # access token.
38 code = input('Enter the authorization code: ')
39 flow.fetch_token(code=code)
27 flow = InstalledAppFlow.from_client_secrets_file(
28 'client_secrets.json',
29 scopes=['profile', 'email'])
30
31 flow.run_local_server()
4032
4133 # You can use flow.credentials, or you can just get a requests session
4234 # using flow.authorized_session.
4335 session = flow.authorized_session()
44 print(session.get('https://www.googleapis.com/userinfo/v2/me').json())
45
46 This particular flow can be handled entirely by using
47 :class:`InstalledAppFlow`.
48
49 .. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/stable/
36
37 profile_info = session.get(
38 'https://www.googleapis.com/userinfo/v2/me').json()
39
40 print(profile_info)
41 # {'name': '...', 'email': '...', ...}
42
43 .. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/latest/
5044 .. _OAuth 2.0 Authorization Flow:
5145 https://tools.ietf.org/html/rfc6749#section-1.2
46 .. _Using OAuth 2.0 to Access Google APIs:
47 https://developers.google.com/identity/protocols/oauth2
48
5249 """
5350 from base64 import urlsafe_b64encode
5451 import hashlib
6663
6764 import google.auth.transport.requests
6865 import google.oauth2.credentials
69 from six.moves import input
7066
7167 import google_auth_oauthlib.helpers
7268
8783 from the `Google API Console`_.
8884
8985 .. _client secrets file:
90 https://developers.google.com/identity/protocols/OAuth2WebServer
86 https://developers.google.com/identity/protocols/oauth2/web-server
9187 #creatingcred
9288 .. _Google API Console:
9389 https://console.developers.google.com/apis/credentials
10096 client_config,
10197 redirect_uri=None,
10298 code_verifier=None,
103 autogenerate_code_verifier=False,
99 autogenerate_code_verifier=True,
104100 ):
105101 """
106102 Args:
118114 autogenerate_code_verifier (bool): If true, auto-generate a
119115 code_verifier.
120116 .. _client secrets:
121 https://developers.google.com/api-client-library/python/guide
122 /aaa_client_secrets
117 https://github.com/googleapis/google-api-python-client/blob
118 /main/docs/client-secrets.md
123119 """
124120 self.client_type = client_type
125121 """str: The client type, either ``'web'`` or ``'installed'``"""
152148 format.
153149
154150 .. _client secrets:
155 https://developers.google.com/api-client-library/python/guide
156 /aaa_client_secrets
151 https://github.com/googleapis/google-api-python-client/blob/main/docs/client-secrets.md
157152 """
158153 if "web" in client_config:
159154 client_type = "web"
212207
213208 @redirect_uri.setter
214209 def redirect_uri(self, value):
210 """The OAuth 2.0 redirect URI. Pass-through to
211 ``self.oauth2session.redirect_uri``."""
215212 self.oauth2session.redirect_uri = value
216213
217214 def authorization_url(self, **kwargs):
326323 local development or applications that are installed on a desktop operating
327324 system.
328325
329 This flow has two strategies: The console strategy provided by
330 :meth:`run_console` and the local server strategy provided by
331 :meth:`run_local_server`.
326 This flow uses a local server strategy provided by :meth:`run_local_server`.
332327
333328 Example::
334329
349344 # {'name': '...', 'email': '...', ...}
350345
351346
352 Note that these aren't the only two ways to accomplish the installed
353 application flow, they are just the most common ways. You can use the
347 Note that this isn't the only way to accomplish the installed
348 application flow, just one of the most common. You can use the
354349 :class:`Flow` class to perform the same flow with different methods of
355350 presenting the authorization URL to the user or obtaining the authorization
356351 response, such as using an embedded web view.
357352
358353 .. _Installed Application Authorization Flow:
359 https://developers.google.com/api-client-library/python/auth
360 /installed-app
354 https://github.com/googleapis/google-api-python-client/blob/main/docs/oauth-installed.md
361355 """
362
363 _OOB_REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"
364356
365357 _DEFAULT_AUTH_PROMPT_MESSAGE = (
366358 "Please visit this URL to authorize this application: {url}"
375367 "The authentication flow has completed. You may close this window."
376368 )
377369
378 def run_console(
379 self,
380 authorization_prompt_message=_DEFAULT_AUTH_PROMPT_MESSAGE,
381 authorization_code_message=_DEFAULT_AUTH_CODE_MESSAGE,
382 **kwargs
383 ):
384 """Run the flow using the console strategy.
385
386 The console strategy instructs the user to open the authorization URL
387 in their browser. Once the authorization is complete the authorization
388 server will give the user a code. The user then must copy & paste this
389 code into the application. The code is then exchanged for a token.
390
391 Args:
392 authorization_prompt_message (str): The message to display to tell
393 the user to navigate to the authorization URL.
394 authorization_code_message (str): The message to display when
395 prompting the user for the authorization code.
396 kwargs: Additional keyword arguments passed through to
397 :meth:`authorization_url`.
398
399 Returns:
400 google.oauth2.credentials.Credentials: The OAuth 2.0 credentials
401 for the user.
402 """
403 kwargs.setdefault("prompt", "consent")
404
405 self.redirect_uri = self._OOB_REDIRECT_URI
406
407 auth_url, _ = self.authorization_url(**kwargs)
408
409 print(authorization_prompt_message.format(url=auth_url))
410
411 code = input(authorization_code_message)
412
413 self.fetch_token(code=code)
414
415 return self.credentials
416
417370 def run_local_server(
418371 self,
419372 host="localhost",
373 bind_addr=None,
420374 port=8080,
421375 authorization_prompt_message=_DEFAULT_AUTH_PROMPT_MESSAGE,
422376 success_message=_DEFAULT_WEB_SUCCESS_MESSAGE,
423377 open_browser=True,
378 redirect_uri_trailing_slash=True,
379 timeout_seconds=None,
424380 **kwargs
425381 ):
426382 """Run the flow using the server strategy.
436392 Args:
437393 host (str): The hostname for the local redirect server. This will
438394 be served over http, not https.
395 bind_addr (str): Optionally provide an ip address for the redirect
396 server to listen on when it is not the same as host
397 (e.g. in a container). Default value is None,
398 which means that the redirect server will listen
399 on the ip address specified in the host parameter.
439400 port (int): The port for the local redirect server.
440 authorization_prompt_message (str): The message to display to tell
441 the user to navigate to the authorization URL.
401 authorization_prompt_message (str | None): The message to display to tell
402 the user to navigate to the authorization URL. If None or empty,
403 don't display anything.
442404 success_message (str): The message to display in the web browser
443405 the authorization flow is complete.
444406 open_browser (bool): Whether or not to open the authorization URL
445407 in the user's browser.
408 redirect_uri_trailing_slash (bool): whether or not to add trailing
409 slash when constructing the redirect_uri. Default value is True.
410 timeout_seconds (int): It will raise an error after the timeout timing
411 if there are no credentials response. The value is in seconds.
412 When set to None there is no timeout.
413 Default value is None.
446414 kwargs: Additional keyword arguments passed through to
447415 :meth:`authorization_url`.
448416
454422 # Fail fast if the address is occupied
455423 wsgiref.simple_server.WSGIServer.allow_reuse_address = False
456424 local_server = wsgiref.simple_server.make_server(
457 host, port, wsgi_app, handler_class=_WSGIRequestHandler
458 )
459
460 self.redirect_uri = "http://{}:{}/".format(host, local_server.server_port)
425 bind_addr or host, port, wsgi_app, handler_class=_WSGIRequestHandler
426 )
427
428 redirect_uri_format = (
429 "http://{}:{}/" if redirect_uri_trailing_slash else "http://{}:{}"
430 )
431 self.redirect_uri = redirect_uri_format.format(host, local_server.server_port)
461432 auth_url, _ = self.authorization_url(**kwargs)
462433
463434 if open_browser:
464435 webbrowser.open(auth_url, new=1, autoraise=True)
465436
466 print(authorization_prompt_message.format(url=auth_url))
467
437 if authorization_prompt_message:
438 print(authorization_prompt_message.format(url=auth_url))
439
440 local_server.timeout = timeout_seconds
468441 local_server.handle_request()
469442
470443 # Note: using https here because oauthlib is very picky that
516489 Returns:
517490 Iterable[bytes]: The response body.
518491 """
519 start_response("200 OK", [("Content-type", "text/plain")])
492 start_response("200 OK", [("Content-type", "text/plain; charset=utf-8")])
520493 self.last_request_uri = wsgiref.util.request_uri(environ)
521494 return [self._success_message.encode("utf-8")]
1717 Typically, you'll want to use the higher-level helpers in
1818 :mod:`google_auth_oauthlib.flow`.
1919
20 .. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/stable/
20 .. _requests-oauthlib: http://requests-oauthlib.readthedocs.io/en/latest/
2121 """
2222
2323 import datetime
2424 import json
2525
26 from google.auth import external_account_authorized_user
2627 import google.oauth2.credentials
2728 import requests_oauthlib
2829
5051 oauthlib session and the validated client configuration.
5152
5253 .. _client secrets:
53 https://developers.google.com/api-client-library/python/guide
54 /aaa_client_secrets
54 https://github.com/googleapis/google-api-python-client/blob/main/docs/client-secrets.md
5555 """
5656
5757 if "web" in client_config:
8888 oauthlib session and the validated client configuration.
8989
9090 .. _client secrets:
91 https://developers.google.com/api-client-library/python/guide
92 /aaa_client_secrets
91 https://github.com/googleapis/google-api-python-client/blob/main/docs/client-secrets.md
9392 """
9493 with open(client_secrets_file, "r") as json_file:
9594 client_config = json.load(json_file)
126125 "There is no access token for this session, did you call " "fetch_token?"
127126 )
128127
129 credentials = google.oauth2.credentials.Credentials(
130 session.token["access_token"],
131 refresh_token=session.token.get("refresh_token"),
132 id_token=session.token.get("id_token"),
133 token_uri=client_config.get("token_uri"),
134 client_id=client_config.get("client_id"),
135 client_secret=client_config.get("client_secret"),
136 scopes=session.scope,
137 )
128 if "3pi" in client_config:
129 credentials = external_account_authorized_user.Credentials(
130 token=session.token["access_token"],
131 refresh_token=session.token.get("refresh_token"),
132 token_url=client_config.get("token_uri"),
133 client_id=client_config.get("client_id"),
134 client_secret=client_config.get("client_secret"),
135 token_info_url=client_config.get("token_info_url"),
136 scopes=session.scope,
137 )
138 else:
139 credentials = google.oauth2.credentials.Credentials(
140 session.token["access_token"],
141 refresh_token=session.token.get("refresh_token"),
142 id_token=session.token.get("id_token"),
143 token_uri=client_config.get("token_uri"),
144 client_id=client_config.get("client_id"),
145 client_secret=client_config.get("client_secret"),
146 scopes=session.scope,
147 granted_scopes=session.token.get("scope"),
148 )
138149 credentials.expiry = datetime.datetime.utcfromtimestamp(session.token["expires_at"])
139150 return credentials
2020
2121 from __future__ import absolute_import
2222
23 import contextlib
24 import socket
25
2326 import google_auth_oauthlib.flow
2427
2528
26 def get_user_credentials(scopes, client_id, client_secret):
29 LOCALHOST = "localhost"
30 DEFAULT_PORTS_TO_TRY = 100
31
32
33 def is_port_open(port):
34 """Check if a port is open on localhost.
35 Based on StackOverflow answer: https://stackoverflow.com/a/43238489/101923
36 Parameters
37 ----------
38 port : int
39 A port to check on localhost.
40 Returns
41 -------
42 is_open : bool
43 True if a socket can be opened at the requested port.
44 """
45 with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock:
46 try:
47 sock.bind((LOCALHOST, port))
48 sock.listen(1)
49 except socket.error:
50 is_open = False
51 else:
52 is_open = True
53 return is_open
54
55
56 def find_open_port(start=8080, stop=None):
57 """Find an open port between ``start`` and ``stop``.
58 Parameters
59 ----------
60 start : Optional[int]
61 Beginning of range of ports to try. Defaults to 8080.
62 stop : Optional[int]
63 End of range of ports to try (not including exactly equals ``stop``).
64 This function tries 100 possible ports if no ``stop`` is specified.
65 Returns
66 -------
67 Optional[int]
68 ``None`` if no open port is found, otherwise an integer indicating an
69 open port.
70 """
71 if not stop:
72 stop = start + DEFAULT_PORTS_TO_TRY
73
74 for port in range(start, stop):
75 if is_port_open(port):
76 return port
77
78 # No open ports found.
79 return None
80
81
82 def get_user_credentials(
83 scopes, client_id, client_secret, minimum_port=8080, maximum_port=None
84 ):
2785 """Gets credentials associated with your Google user account.
2886
2987 This function authenticates using your user credentials by going through
52110 A string that verifies your application to Google APIs. Find this
53111 value in the `Credentials page on the Google Developer's Console
54112 <https://console.developers.google.com/apis/credentials>`_.
113 minimum_port (int):
114 Beginning of range of ports to try for redirect URI HTTP server.
115 Defaults to 8080.
116 maximum_port (Optional[int]):
117 End of range of ports to try (not including exactly equals ``stop``).
118 This function tries 100 possible ports if no ``stop`` is specified.
55119
56120 Returns:
57121 google.oauth2.credentials.Credentials:
91155 "installed": {
92156 "client_id": client_id,
93157 "client_secret": client_secret,
94 "redirect_uris": ["urn:ietf:wg:oauth:2.0:oob"],
95158 "auth_uri": "https://accounts.google.com/o/oauth2/auth",
96159 "token_uri": "https://oauth2.googleapis.com/token",
97160 }
101164 client_config, scopes=scopes
102165 )
103166
104 return app_flow.run_console()
167 port = find_open_port(start=minimum_port, stop=maximum_port)
168 if not port:
169 raise ConnectionError("Could not find open port.")
170
171 return app_flow.run_local_server(host=LOCALHOST, port=port)
7171 default=os.path.join(click.get_app_dir(APP_NAME), DEFAULT_CREDENTIALS_FILENAME),
7272 help="Path to store OAuth2 credentials.",
7373 )
74 @click.option(
75 "--headless",
76 is_flag=True,
77 metavar="<headless_mode>",
78 show_default=True,
79 default=False,
80 help="Run a console based flow.",
81 )
82 def main(client_secrets, scope, save, credentials, headless):
74 def main(client_secrets, scope, save, credentials):
8375 """Command-line tool for obtaining authorization and credentials from a user.
8476
8577 This tool uses the OAuth 2.0 Authorization Code grant as described
8779 https://tools.ietf.org/html/rfc6749#section-1.3.1
8880
8981 This tool is intended for assist developers in obtaining credentials
90 for testing applications where it may not be possible or easy to run a
91 complete OAuth 2.0 authorization flow, especially in the case of code
92 samples or embedded devices without input / display capabilities.
82 for testing applications or samples.
9383
9484 This is not intended for production use where a combination of
9585 companion and on-device applications should complete the OAuth 2.0
10191 client_secrets, scopes=scope
10292 )
10393
104 if not headless:
105 creds = flow.run_local_server()
106 else:
107 creds = flow.run_console()
94 creds = flow.run_local_server()
10895
10996 creds_data = {
11097 "token": creds.token,
00 Metadata-Version: 2.1
11 Name: google-auth-oauthlib
2 Version: 0.4.2
2 Version: 1.0.0
33 Summary: Google Authentication Library
44 Home-page: https://github.com/GoogleCloudPlatform/google-auth-library-python-oauthlib
55 Author: Google Cloud Platform
66 Author-email: jonwayne+google-auth@google.com
77 License: Apache 2.0
8 Description: oauthlib integration for Google Auth
9 ====================================
10
11 |pypi|
12
13 This library provides `oauthlib`_ integration with `google-auth`_.
14
15 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=master
16 :target: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib
17 .. |docs| image:: https://readthedocs.org/projects/google-auth-oauthlib/badge/?version=latest
18 :target: https://google-auth-oauthlib.readthedocs.io/en/latest/
19 .. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
20 :target: https://pypi.python.org/pypi/google-auth-oauthlib
21
22 .. _oauthlib: https://github.com/idan/oauthlib
23 .. _google-auth: https://github.com/googleapis/google-auth-library-python
24
25 Installing
26 ----------
27
28 You can install using `pip`_::
29
30 $ pip install google-auth-oauthlib
31
32 .. _pip: https://pip.pypa.io/en/stable/
33
34 Documentation
35 -------------
36
37 The latest documentation is available at `google-auth-oauthlib.readthedocs.io`_.
38
39 .. _google-auth-oauthlib.readthedocs.io: http://google-auth-oauthlib.readthedocs.io/
40
41 License
42 -------
43
44 Apache 2.0 - See `the LICENSE`_ for more information.
45
46 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/master/LICENSE
47
488 Keywords: google auth oauth client oauthlib
49 Platform: UNKNOWN
509 Classifier: Programming Language :: Python :: 3
5110 Classifier: Programming Language :: Python :: 3.6
5211 Classifier: Programming Language :: Python :: 3.7
5312 Classifier: Programming Language :: Python :: 3.8
5413 Classifier: Programming Language :: Python :: 3.9
55 Classifier: Development Status :: 3 - Alpha
14 Classifier: Programming Language :: Python :: 3.10
15 Classifier: Programming Language :: Python :: 3.11
16 Classifier: Development Status :: 5 - Production/Stable
5617 Classifier: Intended Audience :: Developers
5718 Classifier: License :: OSI Approved :: Apache Software License
5819 Classifier: Operating System :: POSIX
6223 Classifier: Topic :: Internet :: WWW/HTTP
6324 Requires-Python: >=3.6
6425 Provides-Extra: tool
26 License-File: LICENSE
27
28 oauthlib integration for Google Auth
29 ====================================
30
31 |pypi|
32
33 This library provides `oauthlib`_ integration with `google-auth`_.
34
35 .. |build| image:: https://travis-ci.org/googleapis/google-auth-library-python-oauthlib.svg?branch=main
36 :target: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
37 .. |pypi| image:: https://img.shields.io/pypi/v/google-auth-oauthlib.svg
38 :target: https://pypi.python.org/pypi/google-auth-oauthlib
39
40 .. _oauthlib: https://github.com/idan/oauthlib
41 .. _google-auth: https://github.com/googleapis/google-auth-library-python
42
43 Installing
44 ----------
45
46 You can install using `pip`_::
47
48 $ pip install google-auth-oauthlib
49
50 .. _pip: https://pip.pypa.io/en/stable/
51
52 Documentation
53 -------------
54
55 The latest documentation is available at `google-auth-oauthlib.googleapis.dev`_.
56
57 .. _google-auth-oauthlib.googleapis.dev: https://googleapis.dev/python/google-auth-oauthlib/latest/index.html
58
59 Supported Python Versions
60 -------------------------
61 Python >= 3.6
62
63
64 Unsupported Python Versions
65 ---------------------------
66
67 Python == 2.7, Python == 3.5.
68
69 The last version of this library compatible with Python 2.7 and 3.5 is
70 `google-auth-oauthlib==0.4.1`.
71
72 License
73 -------
74
75 Apache 2.0 - See `the LICENSE`_ for more information.
76
77 .. _the LICENSE: https://github.com/googleapis/google-auth-library-python-oauthlib/blob/main/LICENSE
00 [console_scripts]
11 google-oauthlib-tool = google_auth_oauthlib.tool.__main__:main [tool]
2
0 google-auth
0 google-auth>=2.15.0
11 requests-oauthlib>=0.7.0
22
33 [tool]
4 click
4 click>=6.0.0
1717 from setuptools import setup
1818
1919
20 TOOL_DEPENDENCIES = "click"
20 TOOL_DEPENDENCIES = "click>=6.0.0"
2121
22 DEPENDENCIES = ("google-auth", "requests-oauthlib>=0.7.0")
22 DEPENDENCIES = ("google-auth>=2.15.0", "requests-oauthlib>=0.7.0")
2323
2424
2525 with io.open("README.rst", "r") as fh:
2626 long_description = fh.read()
2727
2828
29 version = "0.4.2"
29 version = "1.0.0"
3030
3131 setup(
3232 name="google-auth-oauthlib",
5353 "Programming Language :: Python :: 3.7",
5454 "Programming Language :: Python :: 3.8",
5555 "Programming Language :: Python :: 3.9",
56 "Development Status :: 3 - Alpha",
56 "Programming Language :: Python :: 3.10",
57 "Programming Language :: Python :: 3.11",
58 "Development Status :: 5 - Production/Stable",
5759 "Intended Audience :: Developers",
5860 "License :: OSI Approved :: Apache Software License",
5961 "Operating System :: POSIX",
66 "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
77 "client_secret": "itsasecrettoeveryone",
88 "redirect_uris": [
9 "urn:ietf:wg:oauth:2.0:oob",
109 "http://localhost"
1110 ]
1211 }
2323 import mock
2424 import pytest
2525 import requests
26 from six.moves import urllib
26 import urllib
2727
2828 from google_auth_oauthlib import flow
2929
281281 with fetch_token_patch as fetch_token_mock:
282282 yield fetch_token_mock
283283
284 @mock.patch("google_auth_oauthlib.flow.input", autospec=True)
285 def test_run_console(self, input_mock, instance, mock_fetch_token):
286 input_mock.return_value = mock.sentinel.code
287 instance.code_verifier = "amanaplanacanalpanama"
288 credentials = instance.run_console()
289
290 assert credentials.token == mock.sentinel.access_token
291 assert credentials._refresh_token == mock.sentinel.refresh_token
292 assert credentials.id_token == mock.sentinel.id_token
293
294 mock_fetch_token.assert_called_with(
295 CLIENT_SECRETS_INFO["web"]["token_uri"],
296 client_secret=CLIENT_SECRETS_INFO["web"]["client_secret"],
297 code=mock.sentinel.code,
298 code_verifier="amanaplanacanalpanama",
299 )
300
301284 @pytest.mark.webtest
302285 @mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
303286 def test_run_local_server(self, webbrowser_mock, instance, mock_fetch_token, port):
320303 assert credentials._refresh_token == mock.sentinel.refresh_token
321304 assert credentials.id_token == mock.sentinel.id_token
322305 assert webbrowser_mock.open.called
306 assert instance.redirect_uri == f"http://localhost:{port}/"
323307
324308 expected_auth_response = auth_redirect_url.replace("http", "https")
325309 mock_fetch_token.assert_called_with(
340324 instance.code_verifier = "amanaplanacanalpanama"
341325
342326 with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
343 future = pool.submit(partial(instance.run_local_server, port=port))
327 future = pool.submit(
328 partial(
329 instance.run_local_server,
330 port=port,
331 redirect_uri_trailing_slash=False,
332 )
333 )
344334
345335 while not future.done():
346336 try:
354344 assert credentials._refresh_token == mock.sentinel.refresh_token
355345 assert credentials.id_token == mock.sentinel.id_token
356346 assert webbrowser_mock.open.called
347 assert instance.redirect_uri == f"http://localhost:{port}"
357348
358349 expected_auth_response = auth_redirect_url.replace("http", "https")
359350 mock_fetch_token.assert_called_with(
377368 instance.run_local_server(open_browser=False)
378369
379370 assert not webbrowser_mock.open.called
371
372 @mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
373 @mock.patch("wsgiref.simple_server.make_server", autospec=True)
374 def test_run_local_server_bind_addr(
375 self, make_server_mock, webbrowser_mock, instance, mock_fetch_token
376 ):
377 def assign_last_request_uri(host, port, wsgi_app, **kwargs):
378 wsgi_app.last_request_uri = self.REDIRECT_REQUEST_PATH
379 return mock.Mock()
380
381 make_server_mock.side_effect = assign_last_request_uri
382
383 my_ip = socket.gethostbyname(socket.gethostname())
384 instance.run_local_server(bind_addr=my_ip, host="localhost")
385
386 assert webbrowser_mock.open.called
387 name, args, kwargs = make_server_mock.mock_calls[0]
388 assert args[0] == my_ip
380389
381390 @pytest.mark.webtest
382391 @mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
1818 import mock
1919 import pytest
2020
21 from google.auth import external_account_authorized_user
22 import google.oauth2.credentials
2123 from google_auth_oauthlib import helpers
2224
2325 DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
8486
8587 credentials = helpers.credentials_from_session(session, CLIENT_SECRETS_INFO["web"])
8688
89 assert isinstance(credentials, google.oauth2.credentials.Credentials)
8790 assert credentials.token == mock.sentinel.access_token
8891 assert credentials.expiry == datetime.datetime(1990, 5, 29, 8, 20, 0)
8992 assert credentials._refresh_token == mock.sentinel.refresh_token
9194 assert credentials._client_id == CLIENT_SECRETS_INFO["web"]["client_id"]
9295 assert credentials._client_secret == CLIENT_SECRETS_INFO["web"]["client_secret"]
9396 assert credentials._token_uri == CLIENT_SECRETS_INFO["web"]["token_uri"]
97 assert credentials.scopes == session.scope
98 assert credentials.granted_scopes is None
99
100
101 def test_credentials_from_session_granted_scopes(session):
102 granted_scopes = ["scope1", "scope2"]
103 session.token = {
104 "access_token": mock.sentinel.access_token,
105 "refresh_token": mock.sentinel.refresh_token,
106 "id_token": mock.sentinel.id_token,
107 "expires_at": 643969200.0,
108 "scope": granted_scopes,
109 }
110
111 credentials = helpers.credentials_from_session(session, CLIENT_SECRETS_INFO["web"])
112
113 assert isinstance(credentials, google.oauth2.credentials.Credentials)
114 assert credentials.token == mock.sentinel.access_token
115 assert credentials.expiry == datetime.datetime(1990, 5, 29, 8, 20, 0)
116 assert credentials._refresh_token == mock.sentinel.refresh_token
117 assert credentials.id_token == mock.sentinel.id_token
118 assert credentials._client_id == CLIENT_SECRETS_INFO["web"]["client_id"]
119 assert credentials._client_secret == CLIENT_SECRETS_INFO["web"]["client_secret"]
120 assert credentials._token_uri == CLIENT_SECRETS_INFO["web"]["token_uri"]
121 assert credentials.scopes == session.scope
122 assert credentials.granted_scopes == granted_scopes
123
124
125 def test_credentials_from_session_3pi(session):
126 session.token = {
127 "access_token": mock.sentinel.access_token,
128 "refresh_token": mock.sentinel.refresh_token,
129 "id_token": mock.sentinel.id_token,
130 "expires_at": 643969200.0,
131 }
132
133 client_secrets_info = CLIENT_SECRETS_INFO["web"].copy()
134 client_secrets_info["3pi"] = True
135 client_secrets_info[
136 "token_info_url"
137 ] = "https://accounts.google.com/o/oauth2/introspect"
138 credentials = helpers.credentials_from_session(session, client_secrets_info)
139
140 assert isinstance(credentials, external_account_authorized_user.Credentials)
141 assert credentials.token == mock.sentinel.access_token
142 assert credentials.expiry == datetime.datetime(1990, 5, 29, 8, 20, 0)
143 assert credentials._refresh_token == mock.sentinel.refresh_token
144 assert credentials._client_id == CLIENT_SECRETS_INFO["web"]["client_id"]
145 assert credentials._client_secret == CLIENT_SECRETS_INFO["web"]["client_secret"]
146 assert credentials._token_url == CLIENT_SECRETS_INFO["web"]["token_uri"]
147 assert (
148 credentials._token_info_url == "https://accounts.google.com/o/oauth2/introspect"
149 )
150 assert credentials.scopes == session.scope
94151
95152
96153 def test_bad_credentials(session):
1111 # See the License for the specific language governing permissions and
1212 # limitations under the License.
1313
14 import socket
15
1416 import mock
17 import pytest
18
19
20 def test_find_open_port_finds_start_port(monkeypatch):
21 from google_auth_oauthlib import interactive as module_under_test
22
23 monkeypatch.setattr(socket, "socket", mock.create_autospec(socket.socket))
24 port = module_under_test.find_open_port(9999)
25 assert port == 9999
26
27
28 def test_find_open_port_finds_stop_port(monkeypatch):
29 from google_auth_oauthlib import interactive as module_under_test
30
31 socket_instance = mock.create_autospec(socket.socket, instance=True)
32
33 def mock_socket(family, type_):
34 return socket_instance
35
36 monkeypatch.setattr(socket, "socket", mock_socket)
37 socket_instance.listen.side_effect = [socket.error] * 99 + [None]
38 port = module_under_test.find_open_port(9000, stop=9100)
39 assert port == 9099
40
41
42 def test_find_open_port_returns_none(monkeypatch):
43 from google_auth_oauthlib import interactive as module_under_test
44
45 socket_instance = mock.create_autospec(socket.socket, instance=True)
46
47 def mock_socket(family, type_):
48 return socket_instance
49
50 monkeypatch.setattr(socket, "socket", mock_socket)
51 socket_instance.listen.side_effect = socket.error
52 port = module_under_test.find_open_port(9000)
53 assert port is None
54 socket_instance.listen.assert_has_calls(mock.call(1) for _ in range(100))
1555
1656
1757 def test_get_user_credentials():
3272 actual_client_config = mock_flow.from_client_config.call_args[0][0]
3373 assert actual_client_config["installed"]["client_id"] == "some-client-id"
3474 assert actual_client_config["installed"]["client_secret"] == "shh-secret"
35 mock_flow_instance.run_console.assert_called_once()
75 mock_flow_instance.run_local_server.assert_called_once()
76
77
78 def test_get_user_credentials_raises_connectionerror(monkeypatch):
79 from google_auth_oauthlib import flow
80 from google_auth_oauthlib import interactive as module_under_test
81
82 def mock_find_open_port(start=8080, stop=None):
83 return None
84
85 monkeypatch.setattr(module_under_test, "find_open_port", mock_find_open_port)
86 mock_flow = mock.create_autospec(flow.InstalledAppFlow, instance=True)
87
88 with mock.patch(
89 "google_auth_oauthlib.flow.InstalledAppFlow", autospec=True
90 ) as mock_flow, pytest.raises(ConnectionError):
91 mock_flow.from_client_config.return_value = mock_flow
92 module_under_test.get_user_credentials(
93 ["scopes"], "some-client-id", "shh-secret"
94 )
95
96 mock_flow.run_local_server.assert_not_called()
5656 flow.return_value = dummy_credentials
5757 yield flow
5858
59 @pytest.fixture
60 def console_mock(self, dummy_credentials):
61 run_console_patch = mock.patch.object(
62 google_auth_oauthlib.flow.InstalledAppFlow, "run_console", autospec=True
63 )
64
65 with run_console_patch as flow:
66 flow.return_value = dummy_credentials
67 yield flow
68
6959 def test_help(self, runner):
7060 result = runner.invoke(cli.main, ["--help"])
7161 assert not result.exception
8979 assert creds.client_id == dummy_credentials.client_id
9080 assert creds.client_secret == dummy_credentials.client_secret
9181 assert creds.scopes == dummy_credentials.scopes
92
93 def test_headless(self, runner, dummy_credentials, console_mock):
94 result = runner.invoke(
95 cli.main,
96 [
97 "--client-secrets",
98 CLIENT_SECRETS_FILE,
99 "--scope",
100 "somescope",
101 "--headless",
102 ],
103 )
104 console_mock.assert_called_with(mock.ANY)
105 assert not result.exception
106 assert dummy_credentials.refresh_token in result.output
107 assert result.exit_code == 0
10882
10983 def test_save_new_dir(self, runner, dummy_credentials, local_server_mock):
11084 credentials_tmpdir = tempfile.mkdtemp()