|
3 | 3 |
|
4 | 4 | import json |
5 | 5 | try: |
6 | | - from urllib.parse import urlencode, parse_qs, quote_plus, urlparse |
| 6 | + from urllib.parse import urlencode, parse_qs, quote_plus, urlparse, urlunparse |
7 | 7 | except ImportError: |
8 | | - from urlparse import parse_qs, urlparse |
| 8 | + from urlparse import parse_qs, urlparse, urlunparse |
9 | 9 | from urllib import urlencode, quote_plus |
10 | 10 | import logging |
11 | 11 | import warnings |
@@ -573,16 +573,8 @@ def authorize(): # A controller in a web app |
573 | 573 | def obtain_token_by_browser( |
574 | 574 | # Name influenced by RFC 8252: "native apps should (use) ... user's browser" |
575 | 575 | self, |
576 | | - scope=None, |
577 | | - extra_scope_to_consent=None, |
578 | 576 | redirect_uri=None, |
579 | | - timeout=None, |
580 | | - welcome_template=None, |
581 | | - success_template=None, |
582 | | - error_template=None, |
583 | | - auth_params=None, |
584 | | - auth_uri_callback=None, |
585 | | - browser_name=None, |
| 577 | + auth_code_receiver=None, |
586 | 578 | **kwargs): |
587 | 579 | """A native app can use this method to obtain token via a local browser. |
588 | 580 |
|
@@ -625,38 +617,64 @@ def obtain_token_by_browser( |
625 | 617 |
|
626 | 618 | :return: Same as :func:`~obtain_token_by_auth_code_flow()` |
627 | 619 | """ |
| 620 | + if auth_code_receiver: # Then caller already knows the listen port |
| 621 | + return self._obtain_token_by_browser( # Use all input param as-is |
| 622 | + auth_code_receiver, redirect_uri=redirect_uri, **kwargs) |
| 623 | + # Otherwise we will listen on _redirect_uri.port |
628 | 624 | _redirect_uri = urlparse(redirect_uri or "http://127.0.0.1:0") |
629 | 625 | if not _redirect_uri.hostname: |
630 | 626 | raise ValueError("redirect_uri should contain hostname") |
631 | | - if _redirect_uri.scheme == "https": |
632 | | - raise ValueError("Our local loopback server will not use https") |
633 | | - listen_port = _redirect_uri.port if _redirect_uri.port is not None else 80 |
634 | | - # This implementation allows port-less redirect_uri to mean port 80 |
| 627 | + listen_port = ( # Conventionally, port-less uri would mean port 80 |
| 628 | + 80 if _redirect_uri.port is None else _redirect_uri.port) |
635 | 629 | try: |
636 | 630 | with _AuthCodeReceiver(port=listen_port) as receiver: |
637 | | - flow = self.initiate_auth_code_flow( |
638 | | - redirect_uri="http://{host}:{port}".format( |
639 | | - host=_redirect_uri.hostname, port=receiver.get_port(), |
640 | | - ) if _redirect_uri.port is not None else "http://{host}".format( |
641 | | - host=_redirect_uri.hostname |
642 | | - ), # This implementation uses port-less redirect_uri as-is |
643 | | - scope=_scope_set(scope) | _scope_set(extra_scope_to_consent), |
644 | | - **(auth_params or {})) |
645 | | - auth_response = receiver.get_auth_response( |
646 | | - auth_uri=flow["auth_uri"], |
647 | | - state=flow["state"], # Optional but we choose to do it upfront |
648 | | - timeout=timeout, |
649 | | - welcome_template=welcome_template, |
650 | | - success_template=success_template, |
651 | | - error_template=error_template, |
652 | | - auth_uri_callback=auth_uri_callback, |
653 | | - browser_name=browser_name, |
654 | | - ) |
| 631 | + uri = redirect_uri if _redirect_uri.port != 0 else urlunparse(( |
| 632 | + _redirect_uri.scheme, |
| 633 | + "{}:{}".format(_redirect_uri.hostname, receiver.get_port()), |
| 634 | + _redirect_uri.path, |
| 635 | + _redirect_uri.params, |
| 636 | + _redirect_uri.query, |
| 637 | + _redirect_uri.fragment, |
| 638 | + )) # It could be slightly different than raw redirect_uri |
| 639 | + self.logger.debug("Using {} as redirect_uri".format(uri)) |
| 640 | + return self._obtain_token_by_browser( |
| 641 | + receiver, redirect_uri=uri, **kwargs) |
655 | 642 | except PermissionError: |
656 | | - if 0 < listen_port < 1024: |
657 | | - self.logger.error( |
658 | | - "Can't listen on port %s. You may try port 0." % listen_port) |
659 | | - raise |
| 643 | + raise ValueError( |
| 644 | + "Can't listen on port %s. You may try port 0." % listen_port) |
| 645 | + |
| 646 | + def _obtain_token_by_browser( |
| 647 | + self, |
| 648 | + auth_code_receiver, |
| 649 | + scope=None, |
| 650 | + extra_scope_to_consent=None, |
| 651 | + redirect_uri=None, |
| 652 | + timeout=None, |
| 653 | + welcome_template=None, |
| 654 | + success_template=None, |
| 655 | + error_template=None, |
| 656 | + auth_params=None, |
| 657 | + auth_uri_callback=None, |
| 658 | + browser_name=None, |
| 659 | + **kwargs): |
| 660 | + # Internally, it calls self.initiate_auth_code_flow() and |
| 661 | + # self.obtain_token_by_auth_code_flow(). |
| 662 | + # |
| 663 | + # Parameters are documented in public method obtain_token_by_browser(). |
| 664 | + flow = self.initiate_auth_code_flow( |
| 665 | + redirect_uri=redirect_uri, |
| 666 | + scope=_scope_set(scope) | _scope_set(extra_scope_to_consent), |
| 667 | + **(auth_params or {})) |
| 668 | + auth_response = auth_code_receiver.get_auth_response( |
| 669 | + auth_uri=flow["auth_uri"], |
| 670 | + state=flow["state"], # Optional but we choose to do it upfront |
| 671 | + timeout=timeout, |
| 672 | + welcome_template=welcome_template, |
| 673 | + success_template=success_template, |
| 674 | + error_template=error_template, |
| 675 | + auth_uri_callback=auth_uri_callback, |
| 676 | + browser_name=browser_name, |
| 677 | + ) |
660 | 678 | return self.obtain_token_by_auth_code_flow( |
661 | 679 | flow, auth_response, scope=scope, **kwargs) |
662 | 680 |
|
|
0 commit comments