i am trying to change google oauth2 example code from python flask to FastAPI.
this is the code
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('oauth2callback', _external=True)
# Use the authorization server's response to fetch the OAuth 2.0 tokens.
print("this is the flask request url -----------", flask.request.url)
**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)
creds = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
return flask.redirect(flask.url_for('test_api_request'))
the line is authorization_response = flask.request.url
the value of it is "http://localhost:8080/oauth2callback?state=79aCVleeoxdA4bYgo5YnzuK8vsvM22&code=4%2F0AWtgzh611Sc3dhUo_pqQSa4RVTEZOgX9rJHc328XCJ4UmLvHdg5zz3t1k8VS3ihZwKMEA&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.readonly+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.modify+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fgmail.labels+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&authuser=0&prompt=none"
now i just want to change this line to the same in fastapi, i have tried request.url.path but it gives me "/oauth2callback" or sometimes None.
The request.url property is a string-like object, and you can get the full URL by casting it as a string.
i.e. str(request.url)
You can also access properties of the URL individually - more information on this can be found in Starlette's documentation.
Related
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']])
I'm trying to use the OAuth token and storing it in the session object as I move between different subdomains. So I have the application starting the OAuth1.0a workflow at flask.mydomain.com. It then redirects to sub.mydomain.com/initiate to fetch the request tokens and I want to save the oauth token secret using sessions (not sure if this is the best way), and then authorizes at sub.mydomain.com/authorize. Once the authorization is complete, it goes to the callback but the oauth token secret does not exist in the session. I'm also trying to save the OAuth1Session so that whenever we go to a new route, that data is saved. So I'm not sure how to handle it since the sessions are defined within the scope of the function.
I read that app.secret_key = os.urandom(someval) doesn't make it work which is what I don't have and I made the OAuth1Session to be global originally which doesn't sound like a good idea (happens in the callback).
#app.route("/initiate")
def initiate():
oauth_session = OAuth1Session(client_key=client_key,client_secret=client_secret,callback_uri=callback_uri)
fetch_response = oauth_session.fetch_request_token(request_token_url)
oauth_token = fetch_response.get('oauth_token')
oauth_token_secret = fetch_response.get('oauth_token_secret')
session['oauth_token_secret'] = oauth_token_secret
full_authorization_url = oauth_session.authorization_url(authorize_url, request_token=oauth_token)
return redirect(full_authorization_url)
#app.route("/callback")
def callback():
session.permanent = True
if ('oauth_token_secret' not in session):
return "There is no oauth token secret"
verifier = request.args.get("oauth_verifier")
oauth_token = request.args.get("oauth_token")
oauth = OAuth1Session(
client_key=client_key,
client_secret=client_secret,
resource_owner_key=oauth_token,
resource_owner_secret=session['oauth_token_secret'],
verifier=verifier) # I would like this oauth session to persist until a certain amount of time before having to reauthenticate
fetch_access_tokens= oauth.fetch_access_token(access_token_url)
return redirect(url_for('.getstatus'))
#app.route("/getStatus")
def getstatus():
r = oauth.get(webservice_url + "/statuses")
if r.status_code == 401:
return redirect(url_for('initiate'))
print(r.content)
return r.content
I fixed the issue, it was with the SERVER_NAME config for the application. The SERVER_NAME was set to flask.mydomain.com, so I just removed it entirely
I get data from the Google Analytics API v3
https://www.googleapis.com/analytics/v3/data/ga?ids=ga:181335694&metrics=ga:sessions&start-date=7daysAgo&end-date=today
Once I run the browser is throwing an error
{
"error":{
"errors":[
{
"domain":"global",
"reason":"required",
"message":"Login Required",
"locationType":"header",
"location":"Authorization"
}
],
"code":401,
"message":"Login Required"
}
}
How to resolve this error?
Once I run python code I get the Google Analytics api, but I run in browser is throwing error for login is required how to resolve it.
There are two types of data when we are talking about Google APIs.
Public data data that is not owned by anyone and everyone can look at
Private data which is owned by a user and you must have permission to access it.
"Login Required",
Means exactly that you must be authenticated in order to access the data you are trying to access. You need the permission of the owner of that data. You cant just take that get string and kick it off in a browser you need an access token in order to do that. You get an access token from the authentication flow.
Since you mentioned python you should be following the hello analytics tutorial which will show you how to set up your project and authenticate your script so that you can get access to the data you need.
import argparse
from apiclient.discovery import build
import httplib2
from oauth2client import client
from oauth2client import file
from oauth2client import tools
def get_service(api_name, api_version, scope, client_secrets_path):
"""Get a service that communicates to a Google API.
Args:
api_name: string The name of the api to connect to.
api_version: string The api version to connect to.
scope: A list of strings representing the auth scopes to authorize for the
connection.
client_secrets_path: string A path to a valid client secrets file.
Returns:
A service that is connected to the specified API.
"""
# Parse command-line arguments.
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
parents=[tools.argparser])
flags = parser.parse_args([])
# Set up a Flow object to be used if we need to authenticate.
flow = client.flow_from_clientsecrets(
client_secrets_path, scope=scope,
message=tools.message_if_missing(client_secrets_path))
# Prepare credentials, and authorize HTTP object with them.
# If the credentials don't exist or are invalid run through the native client
# flow. The Storage object will ensure that if successful the good
# credentials will get written back to a file.
storage = file.Storage(api_name + '.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = tools.run_flow(flow, storage, flags)
http = credentials.authorize(http=httplib2.Http())
# Build the service object.
service = build(api_name, api_version, http=http)
return service
def get_first_profile_id(service):
# Use the Analytics service object to get the first profile id.
# Get a list of all Google Analytics accounts for the authorized user.
accounts = service.management().accounts().list().execute()
if accounts.get('items'):
# Get the first Google Analytics account.
account = accounts.get('items')[0].get('id')
# Get a list of all the properties for the first account.
properties = service.management().webproperties().list(
accountId=account).execute()
if properties.get('items'):
# Get the first property id.
property = properties.get('items')[0].get('id')
# Get a list of all views (profiles) for the first property.
profiles = service.management().profiles().list(
accountId=account,
webPropertyId=property).execute()
if profiles.get('items'):
# return the first view (profile) id.
return profiles.get('items')[0].get('id')
return None
def get_results(service, profile_id):
# Use the Analytics Service Object to query the Core Reporting API
# for the number of sessions in the past seven days.
return service.data().ga().get(
ids='ga:' + profile_id,
start_date='7daysAgo',
end_date='today',
metrics='ga:sessions').execute()
def print_results(results):
# Print data nicely for the user.
if results:
print 'View (Profile): %s' % results.get('profileInfo').get('profileName')
print 'Total Sessions: %s' % results.get('rows')[0][0]
else:
print 'No results found'
def main():
# Define the auth scopes to request.
scope = ['https://www.googleapis.com/auth/analytics.readonly']
# Authenticate and construct service.
service = get_service('analytics', 'v3', scope, 'client_secrets.json')
profile = get_first_profile_id(service)
print_results(get_results(service, profile))
if __name__ == '__main__':
main()
I am working with beacons and want to display all the registered beacons on a same web page by making the request in python.
I am confused, After setting up the scope of OAuth2, how to send the request or discovery.build() to get list of all the requests.
I am setting up the scope by this:
#portal.route('/oauth2callback')
def oauth2callback():
flow = client.flow_from_clientsecrets(
'client_secrets.json',
scope='https://www.googleapis.com/auth/userlocation.beacon.registry',
redirect_uri=flask.url_for('portal.oauth2callback', _external=True),
)
if 'code' not in flask.request.args:
auth_uri = flow.step1_get_authorize_url()
return flask.redirect(auth_uri)
else:
auth_code = flask.request.args.get('code')
credentials = flow.step2_exchange(auth_code)
flask.session['credentials'] = credentials.to_json()
return flask.redirect(flask.url_for('portal.index'))
#portal.route('/')
def index():
if 'credentials' not in flask.session:
return flask.redirect(flask.url_for('portal.oauth2callback'))
credentials = client.OAuth2Credentials.from_json(
flask.session['credentials']
)
if credentials.access_token_expired:
return flask.redirect(flask.url_for('portal.oauth2callback'))
else:
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('# What should I write')
# Or, requests.get(What should I write)
Can some one help me how to get list of all registered beacons by making the request.
Hrm. I don't have much experience with Python, but I'm pretty sure there are Google Sign-In clients for python such as here on GitHub.
With this you can integrate a login flow to your app.
Then, when you make calls to the Proximity Beacon Admin API, to auth you just have to set the HTTP header:
Authorization: Bearer <<OAUTH_BEARER_TOKEN_HERE>>
and your call should be authenticated properly. Then you can do the python equivalent of like http.get() or http.post(), add that HTTP header, and you should be able to see responses.
It get done by sending request like:
sess = requests.Session()
req = sess.get('https://proximitybeacon.googleapis.com/v1beta1/beacons', headers={'Authorization': 'Bearer '+credentials.access_token})
I am using the oauth2decorator_from_clientsecrets() to query various Google API's. I have to store the credentials so I can access the user's data offline (in a cron job). I have the following Credentials class Set up:
class CredentialsModel(db.Model):
credentials = CredentialsProperty()
I try to store the credentials in the Datastore as follows:
if decorator.has_credentials():
storage = StorageByKeyName(CredentialsModel, user.user_id(),'credentials')
credentials = decorator.get_credentials()
storage.put(credentials)
I receive the following error:
AttributeError: 'OAuth2DecoratorFromClientSecrets' object has no attribute 'get_credentials'
When I looked at the documentation it looks like the decorator should have get_credentials().
I am going about this correct way?
Example of usage:
Additional imports needed for the below code:
from google.appengine.api import users
from google.appengine.api import memcache
import pickle
Trigger flow example:
flow = OAuth2WebServerFlow(
client_id=client_id,
client_secret=client_secret,
scope=scope,
access_type = 'offline',
approval_prompt='force',
redirect_uri=self.request.relative_url('/admin/auth_return'))
# Get the url now
authorize_url = flow.step1_get_authorize_url()
# Save flow object in memcache for later retrieval on OAuth callback,
# and redirect this session to Google's OAuth 2.0 authorization site.
logging.info('saving flow for user ' + user.user_id())
memcache.set(user.user_id(), pickle.dumps(flow))
# redirect directly
self.redirect(authorize_url, abort=True)
Save
...
user = users.get_current_user()
logging.info('retrieving flow for user ' + user.user_id())
flow = pickle.loads(memcache.get(user.user_id()))
credentials = flow.step2_exchange(self.request.params)
StorageByKeyName(CredentialsModel, user.user_id(), 'credentials').locked_put(credentials)
Get
# here is how you can get them
credentials = StorageByKeyName(CredentialsModel, user.user_id(),'credentials').locked_get()
...