I am trying to authenticate using Google OAuth but am having a little trouble following the tutorial.
Here is my current setup:
FLOW = OAuth2WebServerFlow(
client_id='67490467925.apps.googleusercontent.com',
client_secret='K1tkrPK97B2W16ZGY',
scope='https://www.googleapis.com/auth/calendar',
user_agent='Real_Hub/1.0',
redirect_uri='http://127.0.0.1:8000/',)
storage = Storage('calendar.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
auth_uri = FLOW.step1_get_authorize_url()
return auth_uri
else:
http = httplib2.Http()
http = credentials.authorize(http)
service = build(serviceName='calendar', version='v3', http=http,
developerKey='AIzaSyCBGjIQ2uNbThW_2oMO9P-Ufb8kc')
return service
#End OAUTH...
I am unsure where I should put credentials = flow.step2_exchange(code) and storage.put(credentials) and how do I get the "code" variable? In the API it says from the redirect url. But I do not follow how to do this.
You need to define a method for handling callbacks from the OAuth provider, then map that callback method to a url on your application, something like
http://yourserver/auth_callback
Then set the redirect_uri to the auth_callback url when you create the Flow class
FLOW = OAuth2WebServerFlow(
client_id='67490467925.apps.googleusercontent.com',
...
redirect_uri='http://yourserver/auth_callback')
After you get the auth_uri, you need to redirect the user to that uri so they can authenticate/authorize
self.redirect(auth_uri, ...)
After authentication/authorization, OAuth provider will "call you back" to the redirect_uri you specified earlier. In your callback handler method, you will now parse for code or if it's not present, check for error parameter
code = self.request.get("code")
credentials = FLOW.step2_exchange(code)
NOTE: I haven't tested this, and I haven't worked with python in awhile so syntax may be off, but hopefully you get the general idea.
Related
Working re-creating a script with very limited python knowledge.
Error: Error 400: invalid_request
The out-of-band (OOB) flow has been blocked in order to keep users secure. Follow the Out-of-Band (OOB) flow migration guide linked in the developer docs below to migrate your app to an alternative method.
Request details: redirect_uri=urn:ietf:wg:oauth:2.0:oob
Need to authenticate
I understand that OOB has been depreciated
Searched around for a work around, tried localhost as the redirect URL
looks like I need to use flow.run_local_server()?
Here is the code that I am using currently:
OAUTH_SCOPE = 'https://www.googleapis.com/auth/webmasters.readonly'
REDIRECT_URI = 'https://localhost:1'
# Run through the OAuth flow and retrieve credentials
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, redirect_uri=REDIRECT_URI)
authorize_url = flow.step1_get_authorize_url()
print ('Go to the following link in your browser: ' + authorize_url)
code = input('Enter verification code: ').strip()
credentials = flow.step2_exchange(code)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
http = credentials.authorize(http)
webmasters_service = build('webmasters', 'v3', http=http)
Any help would greatly appreciated! Please be nice :)
I have followed the guide below to obtain a Google Ads API refresh token for my application.
https://github.com/googleads/googleads-python-lib/wiki/API-access-on-behalf-of-your-clients-(web-flow)
Using the script below, everything worked, but the response only had an access token, while the refresh token was None.
from googleads import oauth2
import google.oauth2.credentials
import google_auth_oauthlib.flow
# Initialize the flow using the client ID and secret downloaded earlier.
# Note: You can use the GetAPIScope helper function to retrieve the
# appropriate scope for AdWords or Ad Manager.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
[oauth2.GetAPIScope('adwords')])
# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required.
flow.redirect_uri = 'https://www.example.com'
# Generate URL for request to Google's OAuth 2.0 server.
# Use kwargs to set optional request parameters.
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true',
# approval_prompt='force'
)
print("\n" + authorization_url)
print("\nVisit the above URL and grant access. You will be redirected. Get the 'code' from the query params of the redirect URL.")
auth_code = input('\nCode: ').strip()
flow.fetch_token(code=auth_code)
credentials = flow.credentials
print(credentials.__dict__)
The problem seemed to be that I have already completed these steps before.
The solution was to include approval_prompt='force' in flow.authorization_url(). After generating the authorization_url this way, the response included a refresh token as well.
Can anyone provide sample code that would be a working replacement for the code that used the now defunct gdata.docs.client API:
import gdata.docs, gdata.docs.client, gdata
pw = file('pw','r').read()
client = gdata.docs.client.DocsClient()
client.ClientLogin('username#domain.org',pw,None)
Until yesterday, that code worked, but google has finally axed the depreceated ClientLogin API (https://developers.google.com/identity/protocols/AuthForInstalledApps).
In reading over the documentation for the OAuth 2.0 library, it looks like the process is supposed to involve user interaction to finish the authentication process, but I need a script to run in a cronjob on a regular basis without user involvement (we update various parts of our google site using a script on a cronjob).
Current Answer:
Hard-coding authentication for the docs API was possible, but that API is also discontinued, so here's the way to do it for the new, preferred DRIVE API.
credentials = OAuth2WebServerFlow(
client_id = "CLIENT ID",
client_secret = "CLIENT SECRET",
scope = 'https://www.googleapis.com/auth/drive',
user_agent = "HTTP",
access_token = "ACCESS TOKEN",
refresh_token = "REFRESH TOKEN",
)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
# Now it gets ugly
# The authorize method below changes the "request" method of http_client
# Not at all sure why I had to fake this to make it work, but I did --
# I believe this must get set somewhere in the normal gdrive flow which
# we're circumventing with this method. You can see the code that
# requires this fake client here:
# https://code.google.com/p/gdata-python client/source/browse/src/gdata/gauth.py
# at ~line 1324
Class FakeClient:
request = 'Fake'
http.http_client = FakeClient()
http = credentials.authorize(http)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
Class FakeClient:
request = 'Fake'
http.http_client = FakeClient()
http = credentials.authorize(http)
In order to get those credentials, you can use the standard OAuth method described in the google documentation and then just dig into the variables to find all the right information. Here's some code I wrote myself to print it all out:
if NEED_NEW_CREDENTIALS:
CLIENT_ID = 'ID'
CLIENT_SECRET = 'SECRET'
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE,
redirect_uri=REDIRECT_URI)
authorize_url = flow.step1_get_authorize_url()
print 'go to the following link in your browser: ' + authorize_url
code = raw_input('Enter verification code: ').strip()
credentials = flow.step2_exchange(code)
print 'Hey buddy -- you better hardcode these new credentials in.'
print 'client_id = "%s"'%credentials.client_id
print 'client_secret = "%s"'%credentials.client_secret
print 'scope = "%s"'%OAUTH_SCOPE
print 'user_agent = "%s"'%credentials.user_agent
print 'access_token = "%s"'%credentials.token_response['access_token']
print 'refresh_token = "%s"'%credentials.token_response['refresh_token']
Ok, I found a better solution in the PyDrive library, which already wraps all of this nicely up for you:
http://pythonhosted.org/PyDrive/
If you set PyDrive to store credentials, it will only make you go through the browser once, then remember the credentials automatically.
You'll need to set up a settings.yaml file that looks like this to work with it:
save_credentials: true
save_credentials_backend: file
save_credentials_file: credentials.txt
client_config_file: client_secrets.json
Once you've done that, and installed your secret in client_secrets.json, the login process is as simple as:
from pydrive.auth import GoogleAuth
gauth = GoogleAuth()
gauth.LocalWebserverAuth()
Then you're working with the PyDrive API, which is pretty friendly to use and well documented here: http://pythonhosted.org/PyDrive/oauth.html#customizing-authentication-with-settings-yaml
The Google Drive API has the following OAuth2.0 procedure from their quickstart to receive the drive_service at the end:
# Copy your credentials from the APIs Console
CLIENT_ID = 'YOUR_CLIENT_ID'
CLIENT_SECRET = 'YOUR_CLIENT_SECRET'
# Check https://developers.google.com/drive/scopes for all available scopes
OAUTH_SCOPE = 'https://www.googleapis.com/auth/drive'
# Redirect URI for installed apps
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
# Path to the file to upload
FILENAME = 'document.txt'
# Run through the OAuth flow and retrieve credentials
flow = OAuth2WebServerFlow(CLIENT_ID, CLIENT_SECRET, OAUTH_SCOPE, REDIRECT_URI)
authorize_url = flow.step1_get_authorize_url()
print 'Go to the following link in your browser: ' + authorize_url
code = raw_input('Enter verification code: ').strip()
credentials = flow.step2_exchange(code)
# Create an httplib2.Http object and authorize it with our credentials
http = httplib2.Http()
http = credentials.authorize(http)
drive_service = build('drive', 'v2', http=http)
Notice that you will be given the variable authorize_url which is printed out. You are supposed to visit it using a browser and then confirm that you allow Google Drive to access your information, which then allows you get a "verification code." Is there any way that I can avoid the step of manual intervention and create a program that automates this step?
Yes, you can use web server to get OAuth callback which doesn't require any user interaction.
Basically, you set up your server to retrieve oauth code and add redirect uri to oauth flow so that oauth sends code to given uri instead of telling user to put code into the textbox.
Take a look at tools.run_flow() method at google-api-python-client.
It has pretty handy code of local webserver oauth flow.
I am trying to access the Google My Business API on a flask app, and having trouble. I have set up the O-Auth procedure with an authorize and oauth-callback functions. The oauth claims to have gone through fine, becuase it finishes the oauth-callback function and redirects to the locations method. I added a developer api key to the build function. When I try and build the connection to the api using the build function i get this: googleapiclient.errors.UnknownApiNameOrVersion: name: mybusiness version:
v4. Im pretty sure are the right api details, because in a command line version without oauth that api name and version number works. Im stuck and I think the error message could be a little misleading, maybe something is wrong with my oauth proccess. I am doing is incorrect?
I have tried the google drive api using the same procedure and it worked. I have also made sure the google my business api is enabled in the google developers console.
Here is the authorize function:
#bp.route('/authorize')
def authorize():
# Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES)
# The URI created here must exactly match one of the authorized redirect URIs
# for the OAuth 2.0 client, which you configured in the API Console. If this
# value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch'
# error.
flow.redirect_uri = url_for('google_questions.oauth2callback', _external=True)
code_verifier = generate_code_verifier()
flow.code_verifier = str(code_verifier)
flask.session['code_verifier'] = str(code_verifier)
authorization_url, state = flow.authorization_url(
# Enable offline access so that you can refresh an access token without
# re-prompting the user for permission. Recommended for web server apps.
access_type='offline',
# Enable incremental authorization. Recommended as a best practice.
include_granted_scopes='true')
# Store the state so the callback can verify the auth server response.
flask.session['state'] = state
apobj.notify(title='Auth url',
body=authorization_url)
return redirect(authorization_url)
Here is the oauth-callback function:
#bp.route('/oauth2callback')
def oauth2callback():
# Specify the state when creating the flow in the callback so that it can
# verified in the authorization server response.
state = flask.session['state']
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
CLIENT_SECRETS_FILE, scopes=SCOPES, state=state)
flow.redirect_uri = flask.url_for('google_questions.oauth2callback', _external=True)
flow.code_verifier = flask.session['code_verifier']
# Use the authorization server's response to fetch the OAuth 2.0 tokens.
authorization_response = flask.request.url
flow.fetch_token(authorization_response=authorization_response)
# Store credentials in the session.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
credentials = flow.credentials
flask.session['credentials'] = credentials_to_dict(credentials)
return flask.redirect(flask.url_for('google_questions.locations'))
Here is the creds_to_dict method:
def credentials_to_dict(credentials):
return {'token': credentials.token,
'refresh_token': credentials.refresh_token,
'token_uri': credentials.token_uri,
'client_id': credentials.client_id,
'client_secret': credentials.client_secret,
'scopes': credentials.scopes}
Here is where it chokes in the locations method:
#bp.route('/locations', methods=['GET','POST'])
#roles_required(['Admin'])
def locations():
# Use the discovery doc to build a service that we can use to make
# MyBusiness API calls, and authenticate the user so we can access their
# account
if 'credentials' not in flask.session:
return flask.redirect('authorize')
# Load credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'], developerKey={api-key})
business = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials)
These are the scopes and api details defined globally:
SCOPES = ['https://www.googleapis.com/auth/business.manage']
API_SERVICE_NAME = 'mybusiness'
API_VERSION = 'v4'
I expect the api to connect and allow for api requests.
The google-api-python-client looks for a discovery document thats default does not have the mybusiness v4 included. You can find the code here https://github.com/googleapis/google-api-python-client/blob/master/googleapiclient/discovery.py You can specify a discovery document with the discoveryServiceUrl parameter. set it to this:
discoveryServiceUrl='https://developers.google.com/my-business/samples/mybusiness_google_rest_v4p5.json'
your full build should look like this:
business = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials, discoveryServiceUrl='https://developers.google.com/my-business/samples/mybusiness_google_rest_v4p5.json')