|
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