Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
reauth.py369 linesDownload Raw Back to oauth2
1# Copyright 2021 Google LLC2#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 at6#7#      http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14 15"""A module that provides functions for handling rapt authentication.16 17Reauth is a process of obtaining additional authentication (such as password,18security token, etc.) while refreshing OAuth 2.0 credentials for a user.19 20Credentials that use the Reauth flow must have the reauth scope,21``https://www.googleapis.com/auth/accounts.reauth``.22 23This module provides a high-level function for executing the Reauth process,24:func:`refresh_grant`, and lower-level helpers for doing the individual25steps of the reauth process.26 27Those steps are:28 291. Obtaining a list of challenges from the reauth server.302. Running through each challenge and sending the result back to the reauth31   server.323. Refreshing the access token using the returned rapt token.33"""34 35import sys36 37from google.auth import exceptions38from google.auth import metrics39from google.oauth2 import _client40from google.oauth2 import challenges41 42 43_REAUTH_SCOPE = "https://www.googleapis.com/auth/accounts.reauth"44_REAUTH_API = "https://reauth.googleapis.com/v2/sessions"45 46_REAUTH_NEEDED_ERROR = "invalid_grant"47_REAUTH_NEEDED_ERROR_INVALID_RAPT = "invalid_rapt"48_REAUTH_NEEDED_ERROR_RAPT_REQUIRED = "rapt_required"49 50_AUTHENTICATED = "AUTHENTICATED"51_CHALLENGE_REQUIRED = "CHALLENGE_REQUIRED"52_CHALLENGE_PENDING = "CHALLENGE_PENDING"53 54 55# Override this global variable to set custom max number of rounds of reauth56# challenges should be run.57RUN_CHALLENGE_RETRY_LIMIT = 558 59 60def is_interactive():61    """Check if we are in an interractive environment.62 63    Override this function with a different logic if you are using this library64    outside a CLI.65 66    If the rapt token needs refreshing, the user needs to answer the challenges.67    If the user is not in an interractive environment, the challenges can not68    be answered and we just wait for timeout for no reason.69 70    Returns:71        bool: True if is interactive environment, False otherwise.72    """73 74    return sys.stdin.isatty()75 76 77def _get_challenges(78    request, supported_challenge_types, access_token, requested_scopes=None79):80    """Does initial request to reauth API to get the challenges.81 82    Args:83        request (google.auth.transport.Request): A callable used to make84            HTTP requests.85        supported_challenge_types (Sequence[str]): list of challenge names86            supported by the manager.87        access_token (str): Access token with reauth scopes.88        requested_scopes (Optional(Sequence[str])): Authorized scopes for the credentials.89 90    Returns:91        dict: The response from the reauth API.92    """93    body = {"supportedChallengeTypes": supported_challenge_types}94    if requested_scopes:95        body["oauthScopesForDomainPolicyLookup"] = requested_scopes96    metrics_header = {metrics.API_CLIENT_HEADER: metrics.reauth_start()}97 98    return _client._token_endpoint_request(99        request,100        _REAUTH_API + ":start",101        body,102        access_token=access_token,103        use_json=True,104        headers=metrics_header,105    )106 107 108def _send_challenge_result(109    request, session_id, challenge_id, client_input, access_token110):111    """Attempt to refresh access token by sending next challenge result.112 113    Args:114        request (google.auth.transport.Request): A callable used to make115            HTTP requests.116        session_id (str): session id returned by the initial reauth call.117        challenge_id (str): challenge id returned by the initial reauth call.118        client_input: dict with a challenge-specific client input. For example:119            ``{'credential': password}`` for password challenge.120        access_token (str): Access token with reauth scopes.121 122    Returns:123        dict: The response from the reauth API.124    """125    body = {126        "sessionId": session_id,127        "challengeId": challenge_id,128        "action": "RESPOND",129        "proposalResponse": client_input,130    }131    metrics_header = {metrics.API_CLIENT_HEADER: metrics.reauth_continue()}132 133    return _client._token_endpoint_request(134        request,135        _REAUTH_API + "/{}:continue".format(session_id),136        body,137        access_token=access_token,138        use_json=True,139        headers=metrics_header,140    )141 142 143def _run_next_challenge(msg, request, access_token):144    """Get the next challenge from msg and run it.145 146    Args:147        msg (dict): Reauth API response body (either from the initial request to148            https://reauth.googleapis.com/v2/sessions:start or from sending the149            previous challenge response to150            https://reauth.googleapis.com/v2/sessions/id:continue)151        request (google.auth.transport.Request): A callable used to make152            HTTP requests.153        access_token (str): reauth access token154 155    Returns:156        dict: The response from the reauth API.157 158    Raises:159        google.auth.exceptions.ReauthError: if reauth failed.160    """161    for challenge in msg["challenges"]:162        if challenge["status"] != "READY":163            # Skip non-activated challenges.164            continue165        c = challenges.AVAILABLE_CHALLENGES.get(challenge["challengeType"], None)166        if not c:167            raise exceptions.ReauthFailError(168                "Unsupported challenge type {0}. Supported types: {1}".format(169                    challenge["challengeType"],170                    ",".join(list(challenges.AVAILABLE_CHALLENGES.keys())),171                )172            )173        if not c.is_locally_eligible:174            raise exceptions.ReauthFailError(175                "Challenge {0} is not locally eligible".format(176                    challenge["challengeType"]177                )178            )179        client_input = c.obtain_challenge_input(challenge)180        if not client_input:181            return None182        return _send_challenge_result(183            request,184            msg["sessionId"],185            challenge["challengeId"],186            client_input,187            access_token,188        )189    return None190 191 192def _obtain_rapt(request, access_token, requested_scopes):193    """Given an http request method and reauth access token, get rapt token.194 195    Args:196        request (google.auth.transport.Request): A callable used to make197            HTTP requests.198        access_token (str): reauth access token199        requested_scopes (Sequence[str]): scopes required by the client application200 201    Returns:202        str: The rapt token.203 204    Raises:205        google.auth.exceptions.ReauthError: if reauth failed206    """207    msg = _get_challenges(208        request,209        list(challenges.AVAILABLE_CHALLENGES.keys()),210        access_token,211        requested_scopes,212    )213 214    if msg["status"] == _AUTHENTICATED:215        return msg["encodedProofOfReauthToken"]216 217    for _ in range(0, RUN_CHALLENGE_RETRY_LIMIT):218        if not (219            msg["status"] == _CHALLENGE_REQUIRED or msg["status"] == _CHALLENGE_PENDING220        ):221            raise exceptions.ReauthFailError(222                "Reauthentication challenge failed due to API error: {}".format(223                    msg["status"]224                )225            )226 227        if not is_interactive():228            raise exceptions.ReauthFailError(229                "Reauthentication challenge could not be answered because you are not"230                " in an interactive session."231            )232 233        msg = _run_next_challenge(msg, request, access_token)234 235        if not msg:236            raise exceptions.ReauthFailError("Failed to obtain rapt token.")237        if msg["status"] == _AUTHENTICATED:238            return msg["encodedProofOfReauthToken"]239 240    # If we got here it means we didn't get authenticated.241    raise exceptions.ReauthFailError("Failed to obtain rapt token.")242 243 244def get_rapt_token(245    request, client_id, client_secret, refresh_token, token_uri, scopes=None246):247    """Given an http request method and refresh_token, get rapt token.248 249    Args:250        request (google.auth.transport.Request): A callable used to make251            HTTP requests.252        client_id (str): client id to get access token for reauth scope.253        client_secret (str): client secret for the client_id254        refresh_token (str): refresh token to refresh access token255        token_uri (str): uri to refresh access token256        scopes (Optional(Sequence[str])): scopes required by the client application257 258    Returns:259        str: The rapt token.260    Raises:261        google.auth.exceptions.RefreshError: If reauth failed.262    """263    sys.stderr.write("Reauthentication required.\n")264 265    # Get access token for reauth.266    access_token, _, _, _ = _client.refresh_grant(267        request=request,268        client_id=client_id,269        client_secret=client_secret,270        refresh_token=refresh_token,271        token_uri=token_uri,272        scopes=[_REAUTH_SCOPE],273    )274 275    # Get rapt token from reauth API.276    rapt_token = _obtain_rapt(request, access_token, requested_scopes=scopes)277 278    return rapt_token279 280 281def refresh_grant(282    request,283    token_uri,284    refresh_token,285    client_id,286    client_secret,287    scopes=None,288    rapt_token=None,289    enable_reauth_refresh=False,290):291    """Implements the reauthentication flow.292 293    Args:294        request (google.auth.transport.Request): A callable used to make295            HTTP requests.296        token_uri (str): The OAuth 2.0 authorizations server's token endpoint297            URI.298        refresh_token (str): The refresh token to use to get a new access299            token.300        client_id (str): The OAuth 2.0 application's client ID.301        client_secret (str): The Oauth 2.0 appliaction's client secret.302        scopes (Optional(Sequence[str])): Scopes to request. If present, all303            scopes must be authorized for the refresh token. Useful if refresh304            token has a wild card scope (e.g.305            'https://www.googleapis.com/auth/any-api').306        rapt_token (Optional(str)): The rapt token for reauth.307        enable_reauth_refresh (Optional[bool]): Whether reauth refresh flow308            should be used. The default value is False. This option is for309            gcloud only, other users should use the default value.310 311    Returns:312        Tuple[str, Optional[str], Optional[datetime], Mapping[str, str], str]: The313            access token, new refresh token, expiration, the additional data314            returned by the token endpoint, and the rapt token.315 316    Raises:317        google.auth.exceptions.RefreshError: If the token endpoint returned318            an error.319    """320    body = {321        "grant_type": _client._REFRESH_GRANT_TYPE,322        "client_id": client_id,323        "client_secret": client_secret,324        "refresh_token": refresh_token,325    }326    if scopes:327        body["scope"] = " ".join(scopes)328    if rapt_token:329        body["rapt"] = rapt_token330    metrics_header = {metrics.API_CLIENT_HEADER: metrics.token_request_user()}331 332    response_status_ok, response_data, retryable_error = _client._token_endpoint_request_no_throw(333        request, token_uri, body, headers=metrics_header334    )335 336    if not response_status_ok and isinstance(response_data, str):337        raise exceptions.RefreshError(response_data, retryable=False)338 339    if (340        not response_status_ok341        and response_data.get("error") == _REAUTH_NEEDED_ERROR342        and (343            response_data.get("error_subtype") == _REAUTH_NEEDED_ERROR_INVALID_RAPT344            or response_data.get("error_subtype") == _REAUTH_NEEDED_ERROR_RAPT_REQUIRED345        )346    ):347        if not enable_reauth_refresh:348            raise exceptions.RefreshError(349                "Reauthentication is needed. Please run `gcloud auth application-default login` to reauthenticate."350            )351 352        rapt_token = get_rapt_token(353            request, client_id, client_secret, refresh_token, token_uri, scopes=scopes354        )355        body["rapt"] = rapt_token356        (357            response_status_ok,358            response_data,359            retryable_error,360        ) = _client._token_endpoint_request_no_throw(361            request, token_uri, body, headers=metrics_header362        )363 364    if not response_status_ok:365        _client._handle_error_response(response_data, retryable_error)366    return _client._handle_refresh_grant_response(response_data, refresh_token) + (367        rapt_token,368    )369 
codekingpro/portable-devtools · Team Ai