Google+ Domains API 403 Forbidden - python

I'm trying to create Circles with the Google+ API, but I'm kinda stuck, this is my code, it was more or less copied from the official API documentation (yes I know it doesn't create Circle, but the issue is the same)
import httplib2
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
import json
with open('client_secrets.json', 'r') as f:
json_data = json.load(f)
data = json_data['web']
CLIENT_ID = data['client_id']
CLIENT_SECRET = data['client_secret']
# List the scopes your app requires:
SCOPES = ['https://www.googleapis.com/auth/plus.me',
'https://www.googleapis.com/auth/plus.circles.write']
# The following redirect URI causes Google to return a code to the user's
# browser that they then manually provide to your app to complete the
# OAuth flow.
REDIRECT_URI = 'http://localhost/oauth2callback'
# For a breakdown of OAuth for Python, see
# https://developers.google.com/api-client-library/python/guide/aaa_oauth
# CLIENT_ID and CLIENT_SECRET come from your APIs Console project
flow = OAuth2WebServerFlow(client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPES,
redirect_uri=REDIRECT_URI)
auth_uri = flow.step1_get_authorize_url()
# This command-line server-side flow example requires the user to open the
# authentication URL in their browser to complete the process. In most
# cases, your app will use a browser-based server-side flow and your
# user will not need to copy and paste the authorization code. In this
# type of app, you would be able to skip the next 3 lines.
# You can also look at the client-side and one-time-code flows for other
# options at https://developers.google.com/+/web/signin/
print 'Please paste this URL in your browser to authenticate this program.'
print auth_uri
code = raw_input('Enter the code it gives you here: ')
# Set authorized credentials
credentials = flow.step2_exchange(code)
# Create a new authorized API client.
http = httplib2.Http()
http = credentials.authorize(http)
service = build('plusDomains', 'v1', http=http)
from apiclient import errors
try:
people_service = service.people()
people_document = people_service.get(userId='me').execute()
except errors.HttpError, e:
print e.content
My output:
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}
],
"code": 403,
"message": "Forbidden"
}
}
I searched for answer, but didn't really find any. On the API console I have Google+ API and
Google+ Domains API services added also my secret and client id are okay (otherwise the whole script would fail sooner). Also the auth is successful, my app's name is shown under https://accounts.google.com/IssuedAuthSubTokens. What did I miss?

The problem lies with your REDIRECT_URI variable. When you are using OAuth 2.0 in a purely server-side flow, the redirect URI MUST be 'urn:ietf:wg:oauth:2.0:oob'.
Try changing the variable like so (and be sure to update your client ID in the API Console):
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
Edit: Also, make sure that you are making your API call for a user within a domain. The Google+ Domains API only permits API calls that are restricted to users and content within that domain.

Related

How to get Microsoft Graph API token to read mails

I'm trying to retrieve mails from my organization's mailbox, and I can do that via Graph Explorer. However, when I use the same information that I used in Graph Explorer, the generated token returns an error stating '/me request is only valid with delegated authentication flow.' in me/messages endpoint.
So, how can I generate the acceptable token for the /me endpoint?
An example python code or example Postman request would be amazing.
It sounds like the endpoint you're using in Graph Explorer is something like this
https://graph.microsoft.com/v1.0/me/messages
/me is referring to the user signed into Graph Explorer. If you want to read another user's messages you would use
https://graph.microsoft.com/v1.0/users/user#domain.com/messages
When connecting to Graph API as an application with no user interaction, you can never use /me endpoints, as there's no user logged in.
Reference
https://learn.microsoft.com/en-us/graph/api/user-list-messages?view=graph-rest-1.0
Python example to list messages
import requests
def get_messages(access_token, user):
request_url = f"https://graph.microsoft.com/v1.0/users/{user}/messages"
request_headers = {
"Authorization": "Bearer " + access_token
}
result = requests.get(url = request_url, headers = request_headers)
return(result)
msgs = get_messages(access_token = token['access_token'], user = "userPrincipalName#domain.com")
print(msgs.content)
Additional example of obtaining a token, using an app registration and client secret
import msal
def get_token_with_client_secret(client_id, client_secret, tenant_id):
# This function is to obtain a bearer token using the client credentials flow, with a client secret instead of a certificate
# https://docs.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider
app = msal.ConfidentialClientApplication(
client_id = client_id,
client_credential = client_secret,
authority = f"https://login.microsoftonline.com/{tenant_id}")
scopes = ["https://graph.microsoft.com/.default"]
token = app.acquire_token_for_client(scopes = scopes)
return(token)

Error 400: invalid_request The out-of-band (OOB) flow has been blocked in order to keep users secure

using this quickstart.ipynb
I'm getting this error
You can’t sign in because "myapp" sent an invalid request. You can try again later, or contact the developer about this issue. Learn more about this error
If you are a developer of myapp, see error details.
Error 400: invalid_request
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
This is the code which is running
from apiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
CLIENT_CONFIG = {
'installed': {
'client_id':'----------------------------------------------------------',
'client_secret': '------------------------------------------------------',
'auth_uri':'https://accounts.google.com/o/oauth2/auth',
'token_uri':'https://oauth2.googleapis.com/token'
}
}
SCOPES = ['https://www.googleapis.com/auth/androidmanagement']
flow = InstalledAppFlow.from_client_config(CLIENT_CONFIG, SCOPES)
credentials = flow.run_console()
androidmanagement = build('androidmanagement', 'v1', credentials=credentials)
print('\nAuthentication succeeded.')
Since Google has deprecated OOB
You have to use the new method run_local_server() if you are using the google_auth_oauthlib package
Replace the line
credentials = flow.run_console()
to
credentials = flow.run_local_server()
The out-of-band (OOB) flow has been blocked.
Use the below code Replace your 'your client id' and 'your client secret'
from apiclient.discovery import build
from google_auth_oauthlib.flow import Flow
# This is a public OAuth config that you can use to run this guide.
# However, use different credentials when building your own solution.
CLIENT_CONFIG = {
'web': {
'client_id':'your client id',
'auth_uri':'https://accounts.google.com/o/oauth2/auth',
'token_uri':'https://oauth2.googleapis.com/token',
'auth_provider_x509_cert_url':'https://www.googleapis.com/oauth2/v1/certs',
'client_secret':'your client secret'
}
}
SCOPES = ['https://www.googleapis.com/auth/androidmanagement']
CALLBACK_URL = 'https://google.github.io/android-management-api- samples/oauth_callback.html'
# Run the OAuth flow.
flow = Flow.from_client_config(CLIENT_CONFIG, SCOPES)
flow.redirect_uri = CALLBACK_URL
auth_url, _ = flow.authorization_url()
print('Please visit this URL to authorize this application: {}'.format(auth_url))
code = input('Enter the authorization code: ')
flow.fetch_token(code=code)
# Create the API client.
androidmanagement = build('androidmanagement', 'v1', credentials=flow.credentials)
print('\nAuthentication succeeded.')
full link for Android Management API Quick Start https://colab.research.google.com/github/google/android-management-api-samples/blob/master/notebooks/quickstart.ipynb

How to use credentials obtained from google with google API

Libs: dj-rest-auth + allauth
I. I'm trying to interact with google API with credentials that I use to obtain internal access token. I managed to obtain both code and token but can't find how to use them with google API. The last page I found inapplicable is https://github.com/googleapis/google-api-python-client/blob/main/docs/oauth.md but probably I'm missing some things.
Here's the view I'm trying to use google API in:
class CreateGoogleDoc(GenericAPIView):
...
def get(self, request):
token = request.query_params['token']
module_dir = os.path.dirname(__file__) # get current directory
file_path = os.path.join(module_dir, 'client_secret.json')
flow = Flow.from_client_secrets_file(
file_path,
scopes=SCOPES,
redirect_uri='https://example.com'
)
credentials = service_account.Credentials.from_service_account_file(file_path, scopes=SCOPES)
service = build('docs', 'v1', credentials=credentials)
document = service.documents().create().execute()
return Response([document['documentId']])
II. While I tried to swap code to internal access token class I got another error:
Error retrieving access token: `{ "error": "invalid_request", "error_description": "You can't sign in to this app because it doesn't comply with Google's OAuth 2.0 policy for keeping apps secure. You can let the app developer know that this app doesn't comply with one or more Google validation rules."}`
Here's a view that I'm using for swapping:
GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
callback_url = 'http://localhist:8000/dj-rest-auth/google/'
client_class = OAuth2Client
Thanks!
Offering a workaround
If you already have a token from the GET response, why are you trying to get credentials from a service account file? Probably there is some wrong configuration there, but if you already have the access token, you can just use it like below and avoid the whole service account token fetching.
from google.oauth2.credentials import Credentials
# ...
def get(self, request):
token = request.query_params['token']
credentials = Credentials(token)
service = build('docs', 'v1', credentials=credentials)
document = service.documents().create().execute()
return Response([document['documentId']])

Webapplication authentication with refresh token is redirecting back to google login page

We have our jenkins authentication setup using google oauth using OpenId Connect in Apache (Reference: https://cloudavail.com/2014/06/07/apache-auth-oidc-google-apps-2/).
In order to automate some of the jenkins jobs, we have to authenticate first.
I got the access token and refresh token using client-secrets.json in a python script during which authentication to google mail is already done.
I am using the following script to auto refresh the token using the refresh token and then use the new token to list all of the jobs or projects in Jenkins.
Why is it redirecting me to google login when I have already authorized with gmail during the authorize step while fetching the access token. It will be a great help, if anyone can help me resolve this.
Following is the script (token.json and extra.json contains token and client details)
#!/usr/bin/env python
import requests_oauthlib, json
from requests_oauthlib import OAuth2Session, TokenUpdated
protected_url='<jenkins_url>/api/json?pretty=true'
refresh_url='https://accounts.google.com/o/oauth2/token'
#Start here
TOKEN_FILE='token.json'
EXTRA_FILE='extra.json'
with open(TOKEN_FILE, 'r') as f:
token = json.load(f)
with open(EXTRA_FILE, 'r') as f:
extra = json.load(f)
client_id=extra['client_id']
def token_saver(token_temp):
token = token_temp
from requests_oauthlib import OAuth2Session
client = OAuth2Session(client_id, token=token, auto_refresh_url=refresh_url,auto_refresh_kwargs=extra, token_updater=token_saver)
token=client.refresh_token(refresh_url, **extra)
token_saver(token)
client = OAuth2Session(client_id, token=token)
r = client.get(protected_url)
print r.content
token.json contents
{
"access_token": "{access_token}",
"refresh_token": "{Refresh_token}",
"id_token": "{id_token}",
"token_type": "Bearer",
"expires_in": "5"
}
extra.json contents
{
"client_id":"{client_id}",
"client_secret":"{client_secret}"
}

How can I refresh a stored Google oAuth credential

I'm using the Python API that google provides. What I want to do is just make sure that the access token doesn't expire. I have the refresh_token stored in the credentials file. I'm just not sure how to 'check' that the token is still good before making the call to the API and if need be refreshing it and re-storing it in the credentials file.
I did a test that even if I delete the access tokens from the credentials file that it rewrites them into it using the refresh token. I'm hoping that will work for expired access tokens as well.
Thanks
storage = Storage('cred_storage.txt')
credentials = storage.get()
if not 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)
storage.put(credentials)
http = httplib2.Http()
http = credentials.authorize(http)
print http
service = build('admin', 'reports_v1', http=http)
print service
data_query = service.customerUsageReports().get(**{'date':'2015-01-07'})
feed = data_query.execute()
print feed
Simply check the case of expired access token and refresh your expired access token like this:
if credentials.access_token_expired:
credentials.refresh(httplib2.Http())
Tip: While developing this, you can test by editing the access token expiry date in the credentials text file and forcing it to be older than an hour
Also, in your code on the line where you are checking if not credentials:, you can better handle that case with:
if credentials is None or credentials.invalid:
I came across this question while trying to find a way to refresh an access token when construction a credentials object when using from_authorized_user_info. Unfortunately, the following code did not work for me:
credentials.refresh(httplib2.Http())
But I found this documentation from the Oauth library that works wonder. Shared below:
import google.auth.transport.requests
import requests
request = google.auth.transport.requests.Request()
credentials.refresh(request)

Categories