Error using google API and OAUTH2 in Python to get user info - google-api

My web app is successfully going through google's recommended flow to get credentials to query Google Drive API. This works fine. However, when I try to use the same credentials already obtained to get the user's email and name, I get an error.
Here I retrieve credentials and query Google Drive API. This works perfectly fine
def analyze():
credentials = getCredentials()
drive_service = googleapiclient.discovery.build('drive', 'v3', credentials=credentials)
theFiles = drive_service.files().list(pageSize=1000,q="trashed=false", fields="files(id,name,modifiedTime, size)").execute() #THIS WORKS
Right after that, I try to use the SAME CREDENTIALS to get user info, but now it doesn't work
oauth2_client = googleapiclient.discovery.build('oauth2','v2',credentials=credentials)
user_info= oauth2_client.userinfo().get().execute() #THIS FAILS
givenName = user_info['given_name']
Error: https://www.googleapis.com/oauth2/v2/userinfo?alt=json returned "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.">
SOME OTHER IMPORTANT FUNCTIONS:
def getCredentials():
*Loads credentials from the session.*
sc = session['credentials']
credentials = google.oauth2.credentials.Credentials(token=sc.get('token'),
client_id=sc.get('client_id'),
refresh_token=sc.get('refresh_token'),
token_uri=sc.get('token_uri'),
client_secret=sc.get('client_secret'),
scopes=sc.get('scopes'))
the credentials are obtained in the callback page:
#app.route('/OAcallback')
def OAcallback():
flow =google_auth_oauthlib.flow.Flow.from_client_secrets_file('client_id.json', scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile'])
flow.redirect_uri = return_uri
authorization_response = request.url
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
* Store the credentials in the session.*
credentials_to_dict(credentials)
Please help me understand why my credentials are not working when trying to get user info. What should I change?
Thanks in advance!!!

You are only requesting the profile scope. To also request the email address add the scope email.
Change this part of your code from:
scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile']
to:
scopes=['https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/userinfo.profile' 'email']

Related

How to extract the ID token after google authentication if I use the scope openid in java?

I made a small spring boot project and I need the id token. When I try to open an endpoint what requires google login I can login and then see the result of the endpoint calling. My question is that how to get the ID token? Where it can be found? How to extract it?
I send the openid scope but no idea where is the id token can be found to put the name/email/whatever on the front page if I want to:
security.oauth2.resource.user-info-uri = https://www.googleapis.com/userinfo/v2/me
security.oauth2.client.client-id = clientid
security.oauth2.client.client-secret = clientsecret
security.oauth2.client.user-authorization-uri = https://accounts.google.com/o/oauth2/v2/auth
security.oauth2.client.access-token-uri = https://oauth2.googleapis.com/token
security.oauth2.client.scope = openid email profile
Thanks in advance.

python-keycloak package with KeycloakOpenID : logout does not work

I have a keycloak docker container (pulled image jboss/keycloak ) and a Django 2.2 web container. For integration of django with keycloak social-auth-app-django was used. Login works fine. Now trying to implement logout using python-keycloak following instructions described here:
https://github.com/marcospereirampj/python-keycloak :
from keycloak import KeycloakOpenID
keycloak_openid = KeycloakOpenID(server_url="http://<my IP>:8080/auth/",
client_id="<client_id>",
realm_name="<my realm name>",
client_secret_key="<my secret>",
verify=True)
config_well_know = keycloak_openid.well_know()
token = keycloak_openid.token("<username>", "<password>")
print(token) # all tokens returned ok
userinfo = keycloak_openid.userinfo(token['access_token'])
print ("userinfo:", userinfo) # userinfo returned ok
keycloak_openid.logout(token['refresh_token'])
in the container log:
Some clients have been not been logged out for user <username> in <my realm name> realm: <client_id>
No logout happens, still can browse the site.
What's missing? Thanks
UPDATE
Maybe I understood the problem. The token I get from keycloak_openid.token() call is not the token that was generated for me at the moment of login. The only token that can be fed to keycloak_openid.logout() call for it to work is that original token ('refresh_token' key value of the token dict, to be specific). Calling keycloak_openid.refresh_token() also issues a new token which is rejected as logout credential. But the originally issued refresh_token does not seem to be stored anywhere - sessions, cookies or keycloak db. (Note: I did find access_token, it's in the django DB in social_auth_usersocialauth table, but I need refresh_token). However, it's dumped to the console output at the moment of login, so if I copy it and call keycloak_openid.logout() with it, it does logout from keycoak. The question is where can I find that original refresh_token?
I used to experience the same issue. What helped was
Going to admin page and location your user in the realm
open your browser's developer console and monitor the networks
Go to sessions tab on keycloak and click log out
Observe which end point is being called and mimic that in your python backend, with proper header in the request.
Hope this helps!
I understand that this question is outdated, but I managed to logout by this:
Add the following variables to settings.py:
SOCIAL_AUTH_KEYCLOAK_LOGOUT_URL = 'https://your-keycloak/auth/realms/your-realm/openid-connect/logout'
SOCIAL_AUTH_KEYCLOAK_EXTRA_DATA=[("refresh_token","refresh_token")]
Now it will save the refresh token in extra_data.
Add into urlpatterns list in urls.py:
url(r'^logout/$', views.logout, name='logout'),
Add the logout view with communication code to views.py:
from django.contrib.auth import logout as auth_logout
import requests
def logout(request):
if request.user.is_authenticated:
user = request.user
if user.social_auth.filter(provider='keycloak'):
social = user.social_auth.get(provider='keycloak')
access_token=social.extra_data['access_token']
refresh_token=social.extra_data['refresh_token']
#logger.debug(access_token) # you can view the tokens
#logger.debug(refresh_token)
logout_request_data={"client_id": settings.SOCIAL_AUTH_KEYCLOAK_KEY, "refresh_token": refresh_token, "client_secret": settings.SOCIAL_AUTH_KEYCLOAK_SECRET}
headers={"Authorization" : "Bearer "+access_token,"Content-Type" : "application/x-www-form-urlencoded"}
result=requests.post(settings.SOCIAL_AUTH_KEYCLOAK_LOGOUT_URL,data=logout_request_data,headers=headers)
auth_logout(request)
return redirect('/')
result code will be 204 on success.

Google Oauth Error: redirect_uri_mismatch

I'm trying to use google Oauth 2 to authenticate with google calendar API for a web server running on AWS EC2.
When I generated the credentials I selected 'OAuth Client ID' and then 'Web Application'. For the Authorised redirect URIs I have entered:
http://ec2-XX-XX-XX-XXX.eu-west-1.compute.amazonaws.com
(I've blanked out the IP of my EC2 instance). I have checked this is the correct URL that I want the callback to go to.
The link that is generated in the server logs is of the form:
https://accounts.google.com/o/oauth2/auth?access_type=offline&client_id=XXXXXXXXXXXX-XXXXXXXXXXXXXX.apps.googleusercontent.com&redirect_uri=http://localhost:47258/Callback&response_type=code&scope=https://www.googleapis.com/auth/calendar.readonly
When I follow the link I get the error
'Error: redirect_uri_mismatch'.
I've read this SO question and have checked that I am using HTTP and there is no trialing '/'
I suspect that the URL generated should not have 'localhost' in it but I've reset the client_secret.json several times and each time I restart tomcat with the new client secret I still get a link with localhost but just over a different port.
Locally, I had selected Credentials type of 'other' previously and was not given an option for the Authorised redirect URI. I did try this for the EC2 instance but this won't give me the control I want over the redirect URI and sends the redirect over localhost.
Google throws redirect_uri_mismatch when the uri (including ports) supplied with the request doesn't match the one registered with the application.
Make sure you registered the Authorised redirect URIs and Authorised JavaScript origins on the web console correctly.
This is a sample configuration that works for me.
In case you are seeing this error while making API call from your server to get tokens.
Short Answer 👇 - What solved my problem
use string postmessage in place of actual redirectUri that you configured on cloud console.
Here is my initilization of OAuth2 client that worked for me.
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
My Case
On the frontend, I am using react to prompt the user for login with google with the authentication-code flow. On success, this returns code in the payload that needs to be posted to the google API server to get token - Access Token, Refresh Token, ID Token etc.
I am using googleapis package on my server. Here is how I am retrieving user info from google
// import {Auth, google} from 'googleapis`;
const clientID = process.env.GOOGLE_OAUTH_CLIENT_ID;
const clientSecret = process.env.GOOGLE_OAUTH_CLIENT_SECRET;
oauthClient = new google.auth.OAuth2(clientID,clientSecret,'postmessage');
/*
get tokens from google to make api calls on behalf of user.
#param: code -> code posted to backend from the frontend after the user successfully grant access from consent screen
*/
const handleGoogleAuth = (code: string) => {
oauthClient.getToken(code, async (err, tokens: Auth.Credentials) {
if (err) throw new Error()
// get user information
const tokenInfo = await oauthClient.verifyIdToken({
idToken: tokens.id_token
});
const {email, given_name, family_name, email} = tokenInfo.getPayload();
// do whatever you want to do with user informaton
}
}
When creating a Oath client ID, DO NOT select web application, Select "Other". This way, the Redirect URI is not required.

How to get a refresh token when using the Google Calendar API Ruby Library?

I'm a newbie trying to implement the Google Calendar API into a web-based app and after following the instructions that they provide to the t, fetching information only works for about 20 minutes (while the access token is still valid). I understand that you need a refresh token in order to generate a new access token, but running this script from the terminal (which google provided in their documentation) doesn't provide a refresh token.
The code I executed in terminal:
google-api oauth-2-login --scope=https://www.googleapis.com/auth/calendar --client- id=CLIENT_ID --client-secret=CLIENT_SECRET
This generated a .yaml file with all of my keys which looks like this:
---
mechanism: oauth_2
scope: SCOPE_HERE
client_id: CLIENT_ID_HERE
client_secret: CLIENT_SECRET_HERE
access_token: ACCESS_TOKEN_HERE
refresh_token:
And the code that they provided if the access token expires:
oauth_yaml = YAML.load_file('.google-api.yaml')
client = Google::APIClient.new
client.authorization.client_id = oauth_yaml["client_id"]
client.authorization.client_secret = oauth_yaml["client_secret"]
client.authorization.scope = oauth_yaml["scope"]
client.authorization.refresh_token = oauth_yaml["refresh_token"]
client.authorization.access_token = oauth_yaml["access_token"]
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
service = client.discovered_api('calendar', 'v3')
So, according the yaml file, client.authorization.refresh_token is always 'nil', and it never gets a new access token. Also, client.authorization.expired? always returns false, even after the app has stopped working.
I've seen some other questions on here pertaining to the same issue, but since I'm generating my tokens via a terminal command, I'm not really sure how to go about getting that refresh token.
You need to specify that you want offline access to get a refresh token: access_type=offline
See https://developers.google.com/accounts/docs/OAuth2WebServer#offline

Getting a 403 - Forbidden for Google Service Account

I am trying to get an access token for Google Service Account. Following is my code -
String SERVICE_ACCOUNT_EMAIL = "edited#developer.gserviceaccount.com";
List scope = new ArrayList();
scope.add("https://www.googleapis.com/auth/admin.directory.user");
String keyFile = "C:\\edited-privatekey.p12";
HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(HTTP_TRANSPORT)
.setJsonFactory(JSON_FACTORY)
.setServiceAccountId(SERVICE_ACCOUNT_EMAIL)
.setServiceAccountScopes(scope)
.setServiceAccountPrivateKeyFromP12File(new java.io.File(keyFile))
.build();
credential.refreshToken();
String accessTokens = credential.getAccessToken();
Although the code works fine and I do get an access token, when I try to use it to 'GET' a Google Apps User using the Google Directory APIs, I get a 403 - Forbidden response code. Could someone please help?
I know the code for GET user is correct because it works fine with the access token generated by Google Apps Admin.
You need to set an admin account with:
.setServiceAccountUser(some_admin_email)
And make sure your App (with the correct scopes) is granted access in the cpanel.
Proceed to https://admin.google.com . Login and add Security control if not exists from More Controls.
Click on Security->Advance Settings->Manage ThirdParty OAuth Client Access and check that those scopes are added(comma separated) for your xxxxxxxxxxxxxxxxxx.apps.googleusercontent.com service account id/client id.
You have to enable the specific api before using it inside https://console.developers.google.com/ library, to make it work with your api key.
watch the video https://www.youtube.com/watch?v=s_G5CnAu69M.

Resources