1# Copyright 2019 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#     https://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"""Get user credentials from interactive code environments. 
    16 
    17This module contains helpers for getting user credentials from interactive 
    18code environments installed on a development machine, such as Jupyter 
    19notebooks. 
    20""" 
    21 
    22from __future__ import absolute_import 
    23 
    24import contextlib 
    25import socket 
    26 
    27import google_auth_oauthlib.flow 
    28 
    29 
    30LOCALHOST = "localhost" 
    31DEFAULT_PORTS_TO_TRY = 100 
    32 
    33 
    34def is_port_open(port): 
    35    """Check if a port is open on localhost. 
    36    Based on StackOverflow answer: https://stackoverflow.com/a/43238489/101923 
    37    Parameters 
    38    ---------- 
    39    port : int 
    40        A port to check on localhost. 
    41    Returns 
    42    ------- 
    43    is_open : bool 
    44        True if a socket can be opened at the requested port. 
    45    """ 
    46    with contextlib.closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sock: 
    47        try: 
    48            sock.bind((LOCALHOST, port)) 
    49            sock.listen(1) 
    50        except socket.error: 
    51            is_open = False 
    52        else: 
    53            is_open = True 
    54    return is_open 
    55 
    56 
    57def find_open_port(start=8080, stop=None): 
    58    """Find an open port between ``start`` and ``stop``. 
    59    Parameters 
    60    ---------- 
    61    start : Optional[int] 
    62        Beginning of range of ports to try. Defaults to 8080. 
    63    stop : Optional[int] 
    64        End of range of ports to try (not including exactly equals ``stop``). 
    65        This function tries 100 possible ports if no ``stop`` is specified. 
    66    Returns 
    67    ------- 
    68    Optional[int] 
    69        ``None`` if no open port is found, otherwise an integer indicating an 
    70        open port. 
    71    """ 
    72    if not stop: 
    73        stop = start + DEFAULT_PORTS_TO_TRY 
    74 
    75    for port in range(start, stop): 
    76        if is_port_open(port): 
    77            return port 
    78 
    79    # No open ports found. 
    80    return None 
    81 
    82 
    83def get_user_credentials( 
    84    scopes, client_id, client_secret, minimum_port=8080, maximum_port=None 
    85): 
    86    """Gets credentials associated with your Google user account. 
    87 
    88    This function authenticates using your user credentials by going through 
    89    the OAuth 2.0 flow. You'll open a browser window to authenticate to your 
    90    Google account. The permissions it requests correspond to the scopes 
    91    you've provided. 
    92 
    93    To obtain the ``client_id`` and ``client_secret``, create an **OAuth 
    94    client ID** with application type **Other** from the `Credentials page on 
    95    the Google Developer's Console 
    96    <https://console.developers.google.com/apis/credentials>`_. Learn more 
    97    with the `Authenticating as an end user 
    98    <https://cloud.google.com/docs/authentication/end-user>`_ guide. 
    99 
    100    Args: 
    101        scopes (Sequence[str]): 
    102            A list of scopes to use when authenticating to Google APIs. See 
    103            the `list of OAuth 2.0 scopes for Google APIs 
    104            <https://developers.google.com/identity/protocols/googlescopes>`_. 
    105        client_id (str): 
    106            A string that identifies your application to Google APIs. Find 
    107            this value in the `Credentials page on the Google Developer's 
    108            Console 
    109            <https://console.developers.google.com/apis/credentials>`_. 
    110        client_secret (str): 
    111            A string that verifies your application to Google APIs. Find this 
    112            value in the `Credentials page on the Google Developer's Console 
    113            <https://console.developers.google.com/apis/credentials>`_. 
    114        minimum_port (int): 
    115            Beginning of range of ports to try for redirect URI HTTP server. 
    116            Defaults to 8080. 
    117        maximum_port (Optional[int]): 
    118            End of range of ports to try (not including exactly equals ``stop``). 
    119            This function tries 100 possible ports if no ``stop`` is specified. 
    120 
    121    Returns: 
    122        google.oauth2.credentials.Credentials: 
    123            The OAuth 2.0 credentials for the user. 
    124 
    125    Examples: 
    126        Get credentials for your user account and use them to run a query 
    127        with BigQuery:: 
    128 
    129            import google_auth_oauthlib 
    130 
    131            # TODO: Create a client ID for your project. 
    132            client_id = "YOUR-CLIENT-ID.apps.googleusercontent.com" 
    133            client_secret = "abc_ThIsIsAsEcReT" 
    134 
    135            # TODO: Choose the needed scopes for your applications. 
    136            scopes = ["https://www.googleapis.com/auth/cloud-platform"] 
    137 
    138            credentials = google_auth_oauthlib.get_user_credentials( 
    139                scopes, client_id, client_secret 
    140            ) 
    141 
    142            # 1. Open the link. 
    143            # 2. Authorize the application to have access to your account. 
    144            # 3. Copy and paste the authorization code to the prompt. 
    145 
    146            # Use the credentials to construct a client for Google APIs. 
    147            from google.cloud import bigquery 
    148 
    149            bigquery_client = bigquery.Client( 
    150                credentials=credentials, project="your-project-id" 
    151            ) 
    152            print(list(bigquery_client.query("SELECT 1").result())) 
    153    """ 
    154 
    155    client_config = { 
    156        "installed": { 
    157            "client_id": client_id, 
    158            "client_secret": client_secret, 
    159            "auth_uri": "https://accounts.google.com/o/oauth2/auth", 
    160            "token_uri": "https://oauth2.googleapis.com/token", 
    161        } 
    162    } 
    163 
    164    app_flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_config( 
    165        client_config, scopes=scopes 
    166    ) 
    167 
    168    port = find_open_port(start=minimum_port, stop=maximum_port) 
    169    if not port: 
    170        raise ConnectionError("Could not find open port.") 
    171 
    172    return app_flow.run_local_server(host=LOCALHOST, port=port)