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 :)
Related
first time using OAuth here and I am stuck. I am building a web app that needs to make authorized calls to the YouTube Data API. I am testing the OAuth flow from my local computer.
I am stuck receiving Error 400: redirect_uri_mismatch when I try to run my Google OAuth flow in Python. The error occurs when I access the link generated by flow.run_console()
Here is my code:
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
client_secrets_file="./client_secret.json"
scopes = ["https://www.googleapis.com/auth/youtube.readonly"]
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
client_secrets_file, scopes)
flow.redirect_uri = "http://127.0.0.1:8080" # Authorized in my client ID
credentials = flow.run_console()
This code returns the message:
Please visit this URL to authorize this application: ***google oauth url ***
Enter the authorization code:
Visiting the link results in the following error:
I tried setting the Authorized Redirect URI in my OAuth Client ID to http://127.0.0.1:8080 since I am testing from my local machine. I also set flow.redirect_uri to http://127.0.0.1:8080 in Python. Using http://127.0.0.1:8080 is currently my only option since the front end has not been set up yet.
I expected the code to authorize my request, since the Authorized URI matches the redirect_uri. But I am still receiving the error.
I have had no issues running the flow from Google's OAuth Playground, if that means anything.
Any help is appreciated, thank you.
Thanks for your help everyone. I was able to find a working solution for my issue from referencing this documentation: https://googleapis.github.io/google-api-python-client/docs/oauth.html
Here is my solution:
def get_subscribers(channel_id="Channel_id",
client_secrets_file="Client_secret_file", n=50):
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
flow = Flow.from_client_secrets_file(
client_secrets_file,
scopes=["https://www.googleapis.com/auth/youtube.readonly"])
flow.redirect_uri = "http://localhost"
auth_url, __ = flow.authorization_url(prompt="consent")
print('Please go to this URL: {}'.format(auth_url))
code = input('Enter the authorization code: ')
flow.fetch_token(code=code)
youtube = googleapiclient.discovery.build(
"youtube", "v3", credentials=flow.credentials
)
request = youtube.subscriptions().list(
part="subscriberSnippet",
forChannelId=channel_id,
maxResults=n,
mySubscribers=True
)
return request.execute()
Change redirect_uri to http://127.0.0.1/ or http://localhost/. I have faced a similar issue before with Google Drive API, and removing the port number worked for me.
The library seems to have a bug.
I know it is not so good but in this case the hack is
flow._OOB_REDIRECT_URI = = "http://127.0.0.1:8080"
are you sure this is definitely your redirect uri? go to your client_secrets.json you downloaded when generating your credentials from the API centre and your redirect uris are in there, http://localhost (NOTE - without the trailing slash!) was specified in mine by default - if it isnt in yours then use the one specified in the json.
I've been trying to get this to work for awhile and I can't get passed this error code I'm getting.
My goal with this code is to set a forwarding email address in gmail. Here is the Google documentation: https://developers.google.com/gmail/api/guides/forwarding_settings
I've copied and pasted the code and I get the same error message:
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/me/settings/forwardingAddresses?alt=json returned "Bad Request">
Bad request is by far the most frustrating error code to get in return. I'm using a service account with domain-wide delegation so I don't think it's a permissions issue. I've copied the code so it's hard to believe the json package is incorrect. I've looked all over the internet and cannot find example code of anyone actually using this feature. I'm afraid GAM will be my only option.
def test():
key_path = 'tokens/admin_client.json'
API_scopes =['https://www.googleapis.com/auth/gmail.settings.sharing','https://www.googleapis.com/auth/gmail.settings.basic']
credentials = service_account.Credentials.from_service_account_file(key_path,scopes=API_scopes)
gmail_service = build('gmail', 'v1', credentials=credentials)
address = { 'forwardingEmail': 'user2#example.com' }
gmail_service.users().settings().forwardingAddresses().create(userId='me', body=address).execute()
Please try this code I made. It is working for me:
from googleapiclient import discovery, errors
from oauth2client import file, client, tools
from google.oauth2 import service_account
SERVICE_ACCOUNT_FILE = 'service_account.json'
SCOPES = ['https://www.googleapis.com/auth/gmail.settings.sharing']
# The user we want to "impersonate"
USER_EMAIL = "user#domain"
ADDRESS = { 'forwardingEmail': 'user2#domain' }
# Set the crendentials
credentials = service_account.Credentials.\
from_service_account_file(SERVICE_ACCOUNT_FILE, scopes= SCOPES)
# Delegate the credentials to the user you want to impersonate
delegated_credentials = credentials.with_subject(USER_EMAIL)
try:
# Build the Gmail Service
service = discovery.build('gmail', 'v1', credentials=delegated_credentials)
# Create the forwardingAddresses:Create endpoint
result = service.users().settings().forwardingAddresses().\
create(userId='me', body=ADDRESS).execute()
if result.get('verificationStatus') == 'accepted':
body = {
'emailAddress': result.get('forwardingEmail'),
'enabled': True,
'disposition': 'trash'
}
# If accepted, update the auto forwarding
result_update = service.users().settings().\
updateAutoForwarding(userId='me', body=body).execute()
print(result_update)
# Handle errors if there are
except errors.HttpError as err:
print('\n---------------You have the following error-------------')
print(err)
print('---------------You have the following error-------------\n')
I based the code I'm providing you from Managing Forwarding (the same link you shared), you can also check the Users.settings.forwardingAddresses and the Python API library
for more information about the Gmail API library and the endpoints contained there.
Notice
You mentioned in one of your comments from another answer that when using .with_subject(arg) you're getting the error "the user did not have permission". Check OAuth: Managing API client access, it will provide you with some steps to enable the authorizations needed for the occasions when you are using a service account and G Suite.
You are using a service account, which does not have access to Gmail.
You can either use the service account to impersonate GSuite users or use OAuth to login as a user directly.
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 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.
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')