LA_
LA_

Reputation: 20409

How to migrate from OOB OAuth?

I am running a python script on the device with no screen/no browser (ssh access only). The script used offline Google's OAuth:

def get_authenticated_service( self , args):
    flow = flow_from_clientsecrets(os.path.dirname(os.path.abspath(__file__)) + "/" + CLIENT_SECRET_FILE,
      scope='https://www.googleapis.com/auth/photoslibrary',
      message='client_secrets files is missing')

    credentials = None
    storage = Storage(os.path.dirname(os.path.abspath(__file__)) + "/" + "token-oauth2.json")
    credentials = storage.get()

    if credentials is None or credentials.invalid:
      credentials = run_flow(flow, storage, args)

    self.token = credentials.access_token

    return build('photoslibrary', 'v1',
      http=credentials.authorize(httplib2.Http()))

If token is expired, it says:

Go to the following link in your browser:

    https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fphotoslibrary&redirect_uri=urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&client_id=12345678-abcdefg.apps.googleusercontent.com&access_type=offline

But now Google says that redirect_uri=ietf:wg:oauth:2.0:oob is invalid.

I tried to manually replace it with 'http://localhost' (taken from CLIENT_SECRET_FILE), but when I take the code from the URL:

http://localhost/?code=4/12345_67899-t_vgPpDmLMGwD75F_w&scope=https://www.googleapis.com/auth/photoslibrary

the script says Authentication has failed: redirect_uri_mismatchBad Request.

I use google api client library 1.6.2.

How should I fix it?

Upvotes: 3

Views: 1495

Answers (1)

Linda Lawton - DaImTo
Linda Lawton - DaImTo

Reputation: 116868

I am running a python script on the device with no screen/no browser (ssh access only).

The issue you are going to have is that the code needs to be authorized at least once. Run it on your local machine. in order to create the token the first time. Then copy it over to your server. with the file created by storage . there should be a refresh token in there.

Your refresh token should not be expiring. However if it expiring after a week then make sure that you have set your project to production in Google cloud console this way your refresh token will stop expiring.

There is no way around showing that consent screen to the user someone must authorize the code. That is done by showing the consent screen in a web browser.

oob issue

open your credentials.json file and make sure it says this

"redirect_uris":["http://localhost"]

If the oob end point is still there remove it.

The reason for your error is that the old credeintils.json file contained the following

"redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}

When the python client library ran it would always pick up the first one. That being "urn:ietf:wg:oauth:2.0:oob" which no longer works.

Now when you run your code it will probalby display a 404 error ignore this for now and look at the top bar url bar the code should be there in the response.

404

if you are getting a 404 and want to remove that read Loopback IP address (macOS, Linux, Windows desktop)

Google states.

To receive the authorization code using this URL, your application must be listening on the local web server. That is possible on many, but not all, platforms. However, if your platform supports it, this is the recommended mechanism for obtaining the authorization code.

So you need to figure out how to set up a local web app for an installed application in order to catch the response.

Google has not at this time told us how this is possible with all languages.

Sample

The following example works. It stores the user credentials in tokenPhotos.json. It will reload them as needed. Im not seeing any issues with OOB in this.

#   To install the Google client library for Python, run the following command:
#   pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib

from __future__ import print_function

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/photoslibrary']
CREDS = 'C:\YouTube\dev\credentials.json';


def main():
    """Shows basic usage of the Photos v1 API.
    Prints the names and ids of the first 20 albums the user has access to.
    """
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('tokenPhotos.json'):
        creds = Credentials.from_authorized_user_file('tokenPhotos.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                CREDS, SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('tokenPhotos.json', 'w') as token:
            token.write(creds.to_json())

    try:
        service = build('photoslibrary', 'v1', credentials=creds, static_discovery=False)

        # Call the Photos v1 API
        results = service.albums().list(
            pageSize=10, fields="albums(id,title),nextPageToken").execute()
        albums = results.get('albums', [])

        if not albums:
            print('No albums found.')
            return
        print('albums:')
        for album in albums:
            print(u'{0} ({1})'.format(album['title'], album['id']))
    except HttpError as error:
        # TODO(developer) - Handle errors from Photos API.
        print(f'An error occurred: {error}')


if __name__ == '__main__':
    main()

Upvotes: 1

Related Questions