Based on this Google documentation I can generate the token for Computer Vision API request by calling this in terminal gcloud auth application-default print-access-token. However, I am going to call the request from my python code and I try to generate from Python code, something like below...
The code is based on this documentation page
with open( environ.get(KEY_ENV_VARIABLE) ) as f:
key = json.load(f)
iat = time.time()
exp = iat + 3600
payload = {
'iss': key.get('client_email'),
'sub': key.get('client_email'),
'aud': 'https://vision.googleapis.com/google.cloud.automl_v1beta1',
'iat': iat,
'exp': exp
}
additional_headers = { "kid": key.get("private_key_id") }
signed_jwt = jwt.encode(payload, key.get("private_key"), headers=additional_headers, algorithm='HS256')
return signed_jwt.decode('utf-8')
It does generate the token however it is different in terms of length, compared to the token generated by gcloud tool.
I know that the easiest and quick dirty fix would be calling os.system('gcloud auth application-default print-access-token'). However, I do not want to do the dirty way if possible and want to generate the token in correct way.
Try following this documentation to download a service account. After you get a key, you'll want to set GOOGLE_APPLICATION_CREDENTIALS to the file path of the key.
Related
I am trying to create a python script that adds some tasks to my Microsoft ToDo List by using the Microsoft Graph API from python.
So far I was able to achieve this such that every time I run the script I have to log into my account and give permission such that the script can access my tasks.
However, I now want to achieve the same result but without having to log into my account every time.
My script looks as follows
import msal
import requests
client_id = '....'
client_secret = '....'
authority = 'https://login.microsoftonline.com/....'
scope = ['https://graph.microsoft.com/.default']
client = msal.ConfidentialClientApplication(client_id, authority=authority, client_credential=client_secret)
# First, try to lookup an access token in cache
token_result = client.acquire_token_silent(scope, account=None)
# If the token is available in cache, save it to a variable
if token_result:
access_token = 'Bearer ' + token_result['access_token']
print('Access token was loaded from cache')
# If the token is not available in cache, acquire a new one from Azure AD and save it to a variable
if not token_result:
token_result = client.acquire_token_for_client(scopes=scope)
access_token = 'Bearer ' + token_result['access_token']
# print('New access token was acquired from Azure AD')
# print(access_token)
url = 'https://graph.microsoft.com/v1.0/users/ae294107-3a57-448f-be95-f58390836cca/todo/lists'
headers = {
'Authorization': access_token
}
graph_result = requests.get(url=url, headers=headers)
print(graph_result.json())
By using this script I do not have to log in every time. However I can only access my user information but not my task list.
Is there a possibility to access my task lists without having to log in every time?
In that case you need to use ROPC flow and hard-code the username and password in the back-end code. please follow the doc - https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
Hope this helps
Thanks
I hope to use IBM speech recognition service without - curl or ibm_watson module.
And my attempt is below:
import speech_recognition as sr
r = sr.Recognizer()
text = r.recognize_ibm(audio,username='',password='')
Even though, I have 'Service credentials' for IBM cloud - speech to text, I cannot find correct form for the function.
In the documents of recognize_ibm(), it is said that I need to enter the link_1 to find my username in XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX format.
But the link_1 is broken.
Where can I find the username and password?
I also tried text = r.recognize_ibm(audio,username='apikey',password=api_key) as the previous answers link_2.
Actually I realized the module was not working.
Here are the official API docs for Speech to Text: https://cloud.ibm.com/apidocs/speech-to-text
It includes various samples and further links. You can use the IAMAuthenticator to turn an API key into an authentication token and to handle refresh tokens. If you don't want to make use of the SDK you have to deal with the IBM Cloud IAM Identity Service API on your own. The API has functions to obtain authentication / access tokens.
I often use a function like this to turn an API key into an access token:
def getAuthTokens(api_key):
url = "https://iam.cloud.ibm.com/identity/token"
headers = { "Content-Type" : "application/x-www-form-urlencoded" }
data = "apikey=" + api_key + "&grant_type=urn:ibm:params:oauth:grant-type:apikey"
response = requests.post( url, headers=headers, data=data )
return response.json()
You could
I have a public Cloud Run, authenticated by JWT Token. Working 100%.
The logic inside the Cloud Run to decode the token is in python:
def decode_jwt(token: str) -> dict:
try:
decoded_token = jwt.decode(
token, JWT_SECRET, algorithms=[JWT_ALGORITHM])
return decoded_token if decoded_token["expires"] >= time.time() else None
except Exception as e:
raise InvalidTokenError
The Cloud Run is publicly available using a custom domain.
Now, I want to do some requests to the Cloud Run, using Cloud Tasks (each request have different parameters, created previously by a Cloud Functions).
In the Cloud Tasks, I create each task with a "Bearer {token}" parameter
Cloud Task Headers Code:
task["http_request"]["headers"] = \
{"Authorization": f"Bearer {token}",
"Accept": "application/json"}
First situation:
When I create the task without the "oidc_token" parameter in the http_request creation.
Cloud Run returns "403 Forbidden", and never reach the decode_jwt function inside cloud run.
Cloud Task http_request Code:
task = {
"http_request": {
"http_method": tasks_v2.HttpMethod.POST,
"url": url,
}
}
Second situation:
I add an "oidc_token".
task = {
"http_request": {
"http_method": tasks_v2.HttpMethod.POST,
"url": url,
"oidc_token": {
"service_account_email": "service-task#xxxxx.iam.gserviceaccount.com",
}
}
Now, the request reach the Cloud Run decode_jwt function, and the log in Cloud Run returns "InvalidTokenError".
Extra: I added a logging.info to expose the token received in Cloud Run, and is not the token I passed in the Cloud Task Creation.
Problem Summary:
you have a public (allUsers) Cloud Run service.
you have created your own authorization mechanism (HS256 - HMAC with SHA-256).
you want to assign a custom token for the HTTP Authorization Bearer value.
Cloud Run authorization is managed by IAP.
Authorization for the Cloud Run service is managed by the Identity Aware Proxy (IAP). If you add an HTTP Authorization Bearer token, IAP will verify that token. That step fails for your custom token which results in an HTTP 403 Forbidden error.
Cloud Tasks supports two types of HTTP Authorization Bearer tokens. OAuth Access tokens and OIDC Identity tokens. You cannot use your own token value to replace the supported types.
That leaves you with two options:
Enhance your code to support Google signed OIDC Identity Tokens.
Use a custom HTTP header that supports your custom token format.
Note: I do not recommend using HS256. HS256 is a symmetric algorithm which means the secret must be known to both sides in order to validate the payload. RS256 is an asymmetric algorithm which uses private/public key pairs. To verify only requires the public key. This is one of the strong design features of Google's use of private keys for service accounts and identities. If you switch to Google's method, all of the hard work is done for you.
You have to specificy the audience of your Cloud Run service, like that
task = {
"http_request": { # Specify the type of request.
"http_method": tasks_v2.HttpMethod.POST,
"url": url, # The full url path that the task will be sent to.
"oidc_token": {
"service_account_email": "service-task#xxxxx.iam.gserviceaccount.com",
"audience": base url of Cloud Run, no /sub/path
}
}
I have been trying (with little success) to have a google cloud function be triggered via an http request from a google sheet (google apps script) and it seemingly won't work. A few important things:
The function should only run if the user comes from my organization
The user should not have to be invited to the GCP project
I know this can be done very easily in google colabs and Python. The following script will let a user in my organization who is not in the GCP project trigger the cloud function:
import requests
import google.auth
from google.auth.transport.requests import Request
from google.colab import auth
credentials, project_id = google.auth.default()
request = Request()
credentials.refresh(request=request)
GCF_URL = ''https://project_location-project_id.cloudfunctions.net/name-of-your-funciton''
resp = requests.get(GCF_URL, headers={'Authorization': f'Bearer {credentials.id_token}'})
This will work and trigger the cloud function for any users inside my organization but does not work for my personal email for example.
Now, I would like to replicate this behvaiour inside a google apps script such that an end user with access to that sheet can trigger the cloud function as long as they are a member of my organization.
I have tried some things I have seen online such as this example:
function callExternalUrl() {
var url = 'https://project_location-project_id.cloudfunctions.net/name-of-your-funciton';
var oauthToken = ScriptApp.getOAuthToken(); // get the user who's logged into Google Sheet's OAuth token
const data = {
oauthToken, // 1. add the oauth token to the payload
activeUser: param.user // 2. this here is important as it adds the userinfo.email scope to the token
// any other data you need to send to the Cloud Function can be added here
};
var options = {
'method' : 'get', // or post, depending on how you set up your Cloud Function
'contentType': 'application/json',
// Convert the JavaScript object to a JSON string.
'payload' : JSON.stringify(data)
};
const response = UrlFetchApp.fetch(url, options);
Logger.log('Response Code: ' + response.getResponseCode());
}
This gives a 403 error but if I change it up so that it gives the OAuth on the correct format like this:
function callExternalUrl() {
var url = 'https://project_location-project_id.cloudfunctions.net/name-of-your-funciton';
var oauthToken = ScriptApp.getOAuthToken(); // get the user who's logged into Google Sheet's OAuth token
var response = UrlFetchApp.fetch(url, {
headers: {
Authorization: 'Bearer ' + oauthToken
}
});
// const response = UrlFetchApp.fetch(url, options);
Logger.log('Response Code: ' + response.getResponseCode());
}
I get a 401 (i.e. the authorization failed). Now, it seems that I simply have to get the correct authentication from the users to send in this request for it to work. I have seen this github repo that focuses on getting OAuth2 from google apps scripts (https://github.com/gsuitedevs/apps-script-oauth2), but I can't seem to get that to work either, it would have to be adapted to cloud in some way I am unaware of.
I have read
Securely calling a Google Cloud Function via a Google Apps Script
which is very similar but it did not seem to get to the root of the problem, any input on how to make this process possible?
Is it possible to use an Authorization: Bearer … header to make a request through Identity Aware Proxy to my protected application? (Using a service account, of course. From outside GCP.)
I would like to not perform the OIDC token exchange, is this supported?
If so, does anyone have any examples?
So far, I have the following but it doesn't work:
iat = time.time()
exp = iat + 3600
payload = {'iss': account['client_email'],
'sub': account['client_email'],
'aud': '/projects/NNNNN/apps/XXXXXXX',
'iat': iat,
'exp': exp}
additional_headers = {'kid': account['private_key']}
signed_jwt = jwt.encode(payload, account['private_key'], headers=additional_headers,
algorithm='RS256')
signed_jwt = signed_jwt.decode('utf-8')
This produces: Invalid IAP credentials: JWT signature is invalid.
this is not currently supported. IAP is expecting a signature generated by the Google accounts infrastructure using its private key, so that's why the signature check is failing. Could you tell me more about why you'd like to avoid the OIDC token exchange? --Matthew, Google IAP Engineering