iterative / PyDrive2

Google Drive API Python wrapper library. Maintained fork of PyDrive.
https://docs.iterative.ai/PyDrive2
Other
580 stars 69 forks source link

Initialize a GoogleAuth with a pre-existing access_token ? #250

Closed rajiteh closed 1 year ago

rajiteh commented 1 year ago

Hello!

Assuming that I already possess a valid access token obtained from elsewhere, am I able to initialize a GoogleAuth object with that token?

The token I have is similar to this format:

{
  "access_token":"****",
  "scope": [ 
    "https://www.googleapis.com/auth/drive",
    "openid",
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email"
  ],
  "token_type": "Bearer", 
  "expires_in": 3599,
  "expires_at":1670816869.1503553
}

And of course I have the values for client_id and client_secret as well.

rajiteh commented 1 year ago

I went ahead and experimented a little with the library and I seem to have something that works for my use case, see below:

    from oauth2client import GOOGLE_REVOKE_URI, GOOGLE_TOKEN_INFO_URI, GOOGLE_TOKEN_URI
    from oauth2client.client import OAuth2Credentials, _extract_id_token

    def get_credentials(
        token_response: dict,
        client_id: str,
        client_secret: str,
        token_uri: str,
        revoke_uri: str,
        token_info_uri: str,
    ):
        access_token = token_response["access_token"]
        refresh_token = token_response.get("refresh_token", None)
        token_expiry = None
        if "expires_in" in token_response:
            delta = datetime.timedelta(seconds=int(token_response["expires_in"]))
            token_expiry = delta + datetime.datetime.utcnow()

        extracted_id_token = None
        id_token_jwt = None
        if "id_token" in token_response:
            extracted_id_token = _extract_id_token(token_response["id_token"])
            id_token_jwt = token_response["id_token"]

        return OAuth2Credentials(
            access_token,
            client_id,
            client_secret,
            refresh_token,
            token_expiry,
            token_uri,
            user_agent=None,
            revoke_uri=revoke_uri,
            id_token=extracted_id_token,
            id_token_jwt=id_token_jwt,
            token_response=token_response,
            scopes=token_response["scope"],
            token_info_uri=token_info_uri,
        )

    class GoogleAuthPlus(GoogleAuth):
        def AuthFromTokenResponse(
            self, response: dict, client_id: str, client_secret: str
        ):

            # Authenticate
            self.credentials = get_credentials(
                token_response=response,
                client_id=client_id,
                client_secret=client_secret,
                token_uri=GOOGLE_TOKEN_URI,
                revoke_uri=GOOGLE_REVOKE_URI,
                token_info_uri=GOOGLE_TOKEN_INFO_URI,
            )

            self.Authorize()

    gauth = GoogleAuthPlus()
    gauth.AuthFromTokenResponse(response, client_id, client_secret)
    _DRIVE = GoogleDrive(gauth)
HCAWN commented 1 year ago

I would like the same functionality. It seem slightly obscure there is no way to parametrise the token variables rather than saving them as a file and have the auth process read the file. I propose in settings.yml setting this flag client_config_backend: dictionary or something akin would allow the developer to provide a dictionary for the token rather than a path to the JSON file? H

shcheklein commented 1 year ago

@HCAWN it can do it now (accept settings as a dict, and even accept direct value as far as I remember for certain auth flows). An an example:

https://docs.iterative.ai/PyDrive2/oauth/#authentication-with-a-service-account

It's specific to the service account flow, but it can be done for any auth flow.

And save_credentials_backend can be already set to dictionary. Please give it a try. Share if you have a complete working example. You would need to passsave_credentials_dict` I think.

Here is the code that corresponds to this logic: https://github.com/iterative/PyDrive2/blob/main/pydrive2/auth.py#L344

shcheklein commented 1 year ago

Closing this for now, but please folks share examples / contribute docs if possible.

HCAWN commented 1 year ago

Hi Ivan, It looks like what you've described and linked is the ability to pass a dictionary containing information about the JSON file that is to be loaded

settings = {
                "client_config_backend": "service",
                "service_config": {
                    "client_json_file_path": "service-secrets.json",
                }
            }

I'm after no JSON file interactions at all. I have a variable that contains the a token much like rajiteh showed at the top of this thread, and would like to pass that directly instead of instructions to open a file as I do not wish to save the token to a file when I create it (via other means). e.g.:

token= {
  "access_token":"****",
  "scope": [ 
    "https://www.googleapis.com/auth/drive",
    "openid",
    "https://www.googleapis.com/auth/userinfo.profile",
    "https://www.googleapis.com/auth/userinfo.email"
  ],
  "token_type": "Bearer", 
  "expires_in": 3599,
  "expires_at":1670816869.1503553
}
gauth = GoogleAuth(token=token)

H

shcheklein commented 1 year ago

@HCAWN no, I meant exactly "no JSON file interactions". Yes, you need to pass the settings dictionary, but inside it you could specify save_credentials_backend: dictionary and pass credentials as a dict.

Please check the code here: https://github.com/iterative/PyDrive2/blob/main/pydrive2/auth.py#L355