I was using gdata module to access, upload, download files from google doc. I have the oauth key and secret with me. Now I want to switch to google drive api. Learning and studying a bit on google drive api , it looks like a bit different in the authentication. I also have downloaded pydrive module so as I can start things up. But I am not able to authorize my server side python code to authorize/authenticate the user using my oauth keys and access my drive. Do any one has any spare know how on how I can use pydrive to access my drive with my previous auth keys. I just need a simple way to authenticate.
For using the gdata module we use either of these credentials-
1> username & password or
2> consumer oauth key and secret key.
Since you are trying to use oauth credentials, I think you want a Domain Wide Delegated Access for Google Drive, which will help you to achieve uploading/downloading files into any user's google drive through out the domain.
For this you need to generate a new Client ID of a Service Account Type from
Developer's Console
*.p12 file will get downloaded. Note the path where you save it.
Also note the email address of your Service account. These will be use while coding.
Below is the python code where u have to carefully edit-
PATH TO SERIVE ACCOUNT PRIVATE KEY, something#developer.gserviceaccount.com, EMAIL_ID#YOURDOMAIN.COM in order to run it properly and test it.
Hope this will help!
Resource- Google Drive API
import httplib2
import pprint
import sys
from apiclient.discovery import build
from oauth2client.client import SignedJwtAssertionCredentials
"""Email of the Service Account"""
SERVICE_ACCOUNT_EMAIL = 'something#developer.gserviceaccount.com'
"""Path to the Service Account's Private Key file"""
SERVICE_ACCOUNT_PKCS12_FILE_PATH = 'PATH TO SERIVE ACCOUNT PRIVATE KEY'
def createDriveService(user_email):
"""Build and returns a Drive service object authorized with the service accounts
that act on behalf of the given user.
Args:
user_email: The email of the user.
Returns:
Drive service object.
"""
f = file(SERVICE_ACCOUNT_PKCS12_FILE_PATH, 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(SERVICE_ACCOUNT_EMAIL, key,
scope='https://www.googleapis.com/auth/drive', sub=user_email)
http = httplib2.Http()
http = credentials.authorize(http)
return build('drive', 'v2', http=http)
drive_service=createDriveService('EMAIL_ID#YOURDOMAIN.COM')
result = []
page_token = None
while True:
try:
param = {}
if page_token:
param['pageToken'] = page_token
files = drive_service.files().list().execute()
#print files
result.extend(files['items'])
page_token = files.get('nextPageToken')
if not page_token:
break
except errors.HttpError, error:
print 'An error occurred: %s' % error
break
for f in result:
print '\n\nFile: ',f.get('title')
print "\n"
Related
first question ever on StackOverflow.
I am trying to write a Cloud Function on gcp to login to vault via hvac.
https://hvac.readthedocs.io/en/stable/usage/auth_methods/gcp.html#login
It says here that a path to a SA json but I am writing this on Cloud Function.
Does anyone have an example on how to do this properly? The default cloud identity SA associated with the function has permission already to the vault address.
Thanks
In Cloud Functions you don't need the path to the Service Account key because the Cloud Identity SA is already loaded as the Application Default Credentials (ADC).
The code from the link you share it's okay for environments where you don't have configured the ADC or simply you prefer to use another account.
For Functions, the code can be simpler:
import time
import json
import googleapiclient.discovery
import google.auth
import hvac
credentials, project = google.auth.default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
now = int(time.time())
expires = now + 900
payload = {
'iat': now,
'exp': expires,
'sub': credentials.service_account_email,
'aud': 'vault/my-role'
}
body = {'payload': json.dumps(payload)}
name = f'projects/{project}/serviceAccounts/{credentials.service_account_email}'
iam = googleapiclient.discovery.build('iam', 'v1', credentials=credentials)
request = iam.projects().serviceAccounts().signJwt(name=name, body=body)
resp = request.execute()
jwt = resp['signedJwt']
client.auth.gcp.login(
role='my-role',
jwt=jwt,
)
I have read extensively on how to access GCP Gmail API using service account and have given it domain-wide authority, using the instruction here:
https://support.google.com/a/answer/162106
Here is my service account:
Here is the scopes added to the domain-wide authority. You can see that the ID matches the service account.
One thing I notice is that my GCP project is an internal project, I havent' published it or anything, yet when I added the scope, it is not showing the service account email name but the project name. Does it make any difference? Do I need to set anything here? In the OAuth Consent Screen, I see the name of the project is being defined there. I have added all same scope on this screen too, not sure if it make any difference.
Here is my code:
from google.oauth2 import service_account
from googleapiclient import discovery
credentials_file = get_credentials('gmail.json')
scopes = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.labels', 'https://www.googleapis.com/auth/gmail.modify']
credentials = service_account.Credentials.from_service_account_info(credentials_file, scopes=scopes)
delegated_credentials = credentials.with_subject("abc#mydomain.com")
GMAIL_SERVICE = discovery.build('gmail', 'v1', credentials=delegated_credentials)
labels = GMAIL_SERVICE.users().labels().list(userId='me').execute()
Error message:
Google.auth.exceptions.RefreshError: ('unauthorized_client: Client is
unauthorized to retrieve access tokens using this method, or client
not authorized for any of the scopes requested.', {'error':
'unauthorized_client', 'error_description': 'Client is unauthorized to
retrieve access tokens using this method, or client not authorized for
any of the scopes requested.'})
Not sure I can answer precisely on the original question (I think not), but here how things are done in cloud functions developed by me. The following particular code snippet is written/adopted for this answer, and it was not tested:
import os
import google.auth
import google.auth.iam
from google.oauth2 import service_account
from google.auth.exceptions import MutualTLSChannelError
from google.auth.transport import requests
import googleapiclient.discovery
from google.cloud import error_reporting
GMAIL_SERV_ACCOUNT = "A service account which makes the API CALL"
OAUTH_TOKEN_URI = "https://accounts.google.com/o/oauth2/token"
GMAIL_SCOPES_LIST = ["https://mail.google.com/"] # for example
GMAIL_USER = "User's email address, who's email we would like to access. abc#mydomain.com - from your question"
# inside the cloud function code:
local_credentials, project_id = google.auth.default()
local_credentials.refresh(requests.Request())
signer = google.auth.iam.Signer(requests.Request(), local_credentials, GMAIL_SERV_ACCOUNT)
delegate_credentials = service_account.Credentials(
signer, GMAIL_SERV_ACCOUNT, OAUTH_TOKEN_URI, scopes=GMAIL_SCOPES_LIST, subject=GMAIL_USER)
delegate_credentials.refresh(requests.Request())
try:
email_api_service = googleapiclient.discovery.build(
'gmail', 'v1', credentials=delegate_credentials, cache_discovery=False)
except MutualTLSChannelError as err:
# handle it somehow, for example (stupid, artificial)
ER = error_reporting.Client(service="my-app", version=os.getenv("K_REVISION", "0"))
ER.report_exception()
return 0
So, the idea is to use my (or 'local') cloud function's service account to create credentials of a dedicated service account (GMAIL_SERV_ACCOUNT - which is used in many different cloud functions running under many different 'local' service accounts); then use that 'delegate' service account to get API service access.
I don't remember if the GMAIL_SERV_ACCOUNT should have any specific IAM roles. But I think the 'local' cloud function's service account should get roles/iam.serviceAccountTokenCreator for it.
Updated:
Some clarification on the IAM role. In terraform (I use it for my CICD) for a given functional component, it looks:
# this service account is an 'external' for the given functional component,
# it is managed in another repository and terraform state file
# so we should get it at first
data "google_service_account" "gmail_srv_account" {
project = "some project id"
account_id = "actual GMAIL_SERV_ACCOUNT account"
}
# now we provide IAM role for that working with it
# where 'google_service_account.local_cf_sa' is the service account,
# under which the given cloud function is running
resource "google_service_account_iam_member" "iam_token_creator_gmail_sa" {
service_account_id = data.google_service_account.gmail_srv_account.name
role = "roles/iam.serviceAccountTokenCreator"
member = "serviceAccount:${google_service_account.local_cf_sa.email}"
depends_on = [
google_service_account.local_cf_sa,
]
}
I'm trying to create a user using Googles Directory API and a service account. However I'm getting the error
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://admin.googleapis.com/admin/directory/v1/users?alt=json returned "Not Authorized to access this resource/api". Details: "Not Authorized to access this resource/api">
I've created a service account on the Google Console and allowed Domain wide delegation. It also says the Admin SDK API is enabled for my project. However I can't seem to create a user. The documentation is confusing me slightly. Here is my implementation
def create_googleuser(content, randpass):
''' This function creates a Google Apps account for a user passing webhook contents and password as arguments '''
# Get User info from Webhook and store them in variables
firstname = get_firstname(content)
secondname = get_secondname(content)
emailaddress = firstname + "." + secondname + "#example.com"
# Connect to google API
userscope = ['https://www.googleapis.com/auth/admin.directory.user']
service_account_credentials = ('serviceaccountcredentials.json')
credentials = service_account.Credentials.from_service_account_file(service_account_credentials, scopes=userscope)
userservice = googleapiclient.discovery.build('admin', 'directory_v1', credentials=credentials)
# Create a user dictionary with user details
userinfo = {"primaryEmail": emailaddress,"name":{"givenName":firstname,"familyName":secondname},"password":randpass}
print (emailaddress)
# Create user through googleAPI
userservice.users().insert(body = userinfo).execute()
I'm thinking that my implementation is wrong rather than the permissions as the serviceaccountcredentials.json should have the correct permissions. Any suggestions?
There are two possibilities for getting this error.
If the API method requires an impersonated user to be used.
If the impersonated user has not the relevant service enabled.
Solution for case 1:
Follow the documentation to impersonate a user account.
Solution for case 2:
In the Admin console, open user information and check that the user is not suspended.
Open the "Apps" panel and check that the relevant service is "On".
May be caused by a user not having a license which allows access to the service (Cloud Identity instead of Google Workspace), or a user being in an organizational unit which has the service disabled.
Also this link might be helpful.
Thanks for the input. You were both correct to a point. Basically there were two issues. The service account user needs to be delegated domain administrator privileges that require domain admin actions, domain wide delegation isn't enough. Also the domain scope needed to be broader in the Admin console and the scope definition within the code. There is github issue open which helped here:
https://github.com/googleapis/google-api-nodejs-client/issues/1884
My working code looks like this
def create_googleuser(content, randpass):
''' This function creates a Google Apps account for a user passing webhook contents and password as arguments '''
# Get User info from Webhook and store them in variables
username = get_username(content)
firstname = get_firstname(content)
secondname = get_secondname(content)
emailaddress = firstname + "." + secondname + "#example.com"
# Connect to google API
userscope = ['https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/admin.directory.user.security']
service_account_credentials = ('serviceaccountcredentials.json')
credentials = service_account.Credentials.from_service_account_file(service_account_credentials, scopes=userscope)
delegated_credentials = credentials.with_subject('domain.admin#example.com')
userservice = googleapiclient.discovery.build('admin', 'directory_v1', credentials=delegated_credentials)
# Create a user dictionary with user details
userinfo = {"primaryEmail": emailaddress,"name":{"givenName":firstname,"familyName":secondname},"password":randpass}
# Create user through googleAPI
userservice.users().insert(body = userinfo).execute()
I am stuck on setting up the mfa for amazon cognito with google authenticator. Did I miss any step?
I have tried following this guide "https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-mfa-totp.html".
I passed the session to the "associate_software_token" and got the secret key, converted it into a QR Code.
After converting it into QR, I downloaded the google authenticator from playstore and tried to proceed. Sadly this is where I'm stuck, google authenticator doesn't recognize the QR Code.
def get(self):
# This is where the secret key which will be later used as password.
data = request.args
client = boto3.client('cognito-idp')
secret_response = client.associate_software_token(Session=data["session"])
# Create QR
try:
img = qrcode.make(secret_response.get('SecretCode'))
except ClientError as e:
return self.handle_boto_error(e)
temp_assets = os.path.join(ASSETS_DIRS, 'temp/')
filename = secure_filename(secret_response.get('SecretCode') + '.png')
to_save_on = os.path.join(temp_assets, filename)
print(secret_response.get('SecretCode'))
img.save(to_save_on)
return send_file(to_save_on, mimetype='image/png'), status.HTTP_200_OK
I expected it to give me the TOTP, which will complete the whole authentication process of the Congito Pool.
Fixed this by converting the qr into the right format, which is:
"link = f"otpauth://totp/{SITE_NAME}:{username}?secret={secret_code}&issuer={SITE_NAME}"
Instead of directly parsing the secret code to QR Code.
I am making an app in flutter which uses Google sign-in. I also have a Django backend linked to the app and I want to verify the user in the Django backend. I found many solutions on the internet but none is working. Probably I am messing up somewhere.
I tried using python-jose for verification and here is the code:
from jose import jwt
import urllib.request, json
token = '<token recieved using await user.getIdToken in flutter>'
target_audience = "<tried projectid/appid>"
certificate_url = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com'
response = urllib.request.urlopen(certificate_url)
certs = response.read()
certs = json.loads(certs)
print(certs)
user = jwt.decode(token, certs, algorithms='RS256',
audience=target_audience)
I also tried oauth2client, the code is here:
from oauth2client import crypt
import urllib.request, json
certificate_url = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken#system.gserviceaccount.com'
target_audience = 'tried projectid/appid'
response = urllib.request.urlopen(certificate_url)
certs = response.read()
certs = json.loads(certs)
print(certs)
crypt.MAX_TOKEN_LIFETIME_SECS = 30 * 86400
idtoken = 'token received from await user.getIdToken()'
crypt.verify_signed_jwt_with_certs(idtoken, certs, target_audience)
I also tried firebase_admin for python:
import firebase_admin
from firebase_admin import credentials
from firebase_admin import auth
cred = credentials.Certificate('<firebase service accounts private key>')
default_app = firebase_admin.initialize_app(cred)
token = 'token from flutter'
verifyied =auth.verify_id_token(id_token=token)
Just to check whether the firebase_admin library itself is working or not, I passed the userid to server from the app and tried deleting the user using firebase_admin and I could do that. But for some reason I am unable to verify the token.
Thanks for the help.
I have also faced the same issue.
Case:
Initially: I was printing auth token in vscode console and was verifying in terminal.
It gave me the error: token length cannot be 1 more than % 4.
I tried verifying the token from jwt.io and it was seemingly correct.
Actual Reason for the issue:
The console output of vscode (in my case windows 7 and 64 bit). Is limited to 1064 characters for a line.
Although the actual length of token is supposed to be 1170 characters.
Workaround Solution:
Print the substring in the vscode console and the join them in python shell to verify.
Answering my own question. The problem was that my server was not actually deployed, so, I was copying the token printed in vscode console when the user logs in and pasting it into the python code and trying to verify the token. Turns out it doesn't work that way.
I hosted my Django app and passed the token in a post request and then tried to verify the token and it worked.
You can refer the solutions here if you are stuck :
https://coders-blogs.blogspot.com/2018/11/authenticating-user-on-backend-of-your.html