I have two services (APIs) deployed on GCP Cloud Run. Call them service-one.myDomain.com and service-two.myDomain.com. I would like service-one to be authenticated in calling service-two independently of what any user is doing.
I've read and implemented the instructions from GCP Cloud Run docs on Authenticating service-to-service (https://cloud.google.com/run/docs/authenticating/service-to-service) but service-one.myDomain.com is unsuccessful in calling service-two.myDomain.com receiving a 401:Unauthorized response.
Any thoughts on how to get service-one to successfully call service-two?
Here's my setup:
IAM and service accounts:
On google IAM, I created two service accounts and granted them both the "Cloud Run Invoker" (roles/run.invoker) role:
service-one#myproject.iam.gserviceaccount.com
service-two#myproject.iam.gserviceaccount.com
Inside Cloud Run I changed the service account from the "Default compute service account" to the service accounts I created. I assigned service-one#myproject.iam.gserviceaccount.com for service-one.myDomain.com and service-two#myproject.iam.gserviceaccount.com for service-two.myDomain.com
OIDC Auth token:
In service-one.myDomain.com I make a call to the metadata server to get a token (jwt) from the following url:
http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://service-two.myDomain.com with a request header set as {'Metadata-Flavor': 'Google'} The request is successful and the token I receive is decoded to have the following payload:
{
"alg": "RS256",
"kid": "9cef5340642b157fa8a4f0d874fe7543872d82db",
"typ": "JWT"
}
{
"aud": "https://service-two.mydomain.com",
"azp": "100959068407876085761",
"email": "service-one#myproject.iam.gserviceaccount.com",
"email_verified": true,
"exp": 1572806540,
"iat": 1572802940,
"iss": "https://accounts.google.com",
"sub": "100953168404568085761"
}
Http request:
Using the token I make a request from service-one.myDomain.com to an http endpoint on service-two.myDomain.com. I set the request header with {'Authorization': 'Bearer {token}'} ({token} is value of token).
Http Response:
The response is a 401 Unauthorized and my logs show the response headers to include:
{'WWW-Authenticate': 'Bearer error="invalid_token" error_description="The access token could not be verified"'}
With a content of:
"
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL <code>/health</code>.</h2>
<h2></h2>
</body></html>
"
I'm stumped.... any ideas on what I'm missing to get service-one to authenticate to service-two?
The answer was to use the gcp cloud run generated Url as the audience in the OIDC token request. And relatedly the "aud" field in the jwt.
My discovery was that Service-to-Service authentication in cloud run does not support custom domains (myDomain.com). I was using my custom domain.
(I feel like a bonehead) thanks #guillaumeblaquiere
Related
I have used the AWS Cognito for the Authentication of my Web Application. The Authentication Provider is being the Intuit. The whole Authentication Part works fine and I'm being able to get the Access Token, ID Token & Refresh Token from the AWS Cognito after the Authorization from Intuit is successful. But, when the Access Token Obtained from the AWS Cognito is used as the Authorization Bearer Token with the Intuit API ( https://quickbooks.api.intuit.com/v3/company/**************/query?query=select%20*%20from%20CompanyInfo&minorversion=63 ), the error occurs. Below is the response I get (Screenshot Attached).
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse time="2022-04-06T05:36:37.063-07:00" xmlns="http://schema.intuit.com/finance/v3">
<Fault type="AUTHENTICATION">
<Error code="3200">
<Message>message=AuthenticationFailed; errorCode=003200; statusCode=401</Message>
</Error>
</Fault>
</IntuitResponse>
I tried using the Intuit's Access Token Directly by getting it from the Intuit Playground and used it with the same API, which gives the successful expected response.
When the JWT Token obtained from the AWS Cognito is decoded with https://jwt.io/ , below is the decoded values of it ...
{
"sub": "<<HIDDEN FOR PRIVACY REASONS>>",
"cognito:groups": [
"Intuit-Users"
],
"token_use": "access",
"scope": "phone openid profile email",
"auth_time": 1649248040,
"iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_jnCqrZ6om",
"exp": 1649334440,
"iat": 1649248040,
"version": 2,
"jti": "<<HIDDEN FOR PRIVACY REASONS>>",
"client_id": "<<HIDDEN FOR PRIVACY REASONS>>",
"username": "<<HIDDEN FOR PRIVACY REASONS>>"
}
https://i.stack.imgur.com/Cgi7I.png
As mentioned in https://developer.intuit.com/app/developer/qbo/docs/learn/scopes there is a scope of "com.intuit.quickbooks.accounting" which grants access to the QuickBooks Online Accounting API, which focuses on accounting data. As I can see in the above decoded Access Token Data, I cannot find the scope "com.intuit.quickbooks.accounting" in the scopes property. Can this be an issue? If so, how to add the scope for this in AWS Cognito? Or else what could be the issue? Can anyone provide a solution?
NOTE : The Ultimate Need is to make the Cognito Returned Access Token Authorized with Intuit to work with QuickBooks API.
I am trying to fetch the data studio assets to manage the permissions based on that data through APIs.
I am using oAuth2 access token generated using the service account as I want to automate this process and not rely on user consent for authorization every time. oAuth2 authorization using service account
Steps I have followed:
Created service account in Google cloud and enabled Google Workspace Domain-wide Delegation
Delegating domain-wide authority to the service account through Google Workspace account
For the following scopes:
https://www.googleapis.com/auth/datastudio
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/userinfo.profile
openid
Created and signed JWT
Used JWT token to get the oAuth2 access token to make Datastudio API calls.
Using the following snippet to generate the signed JWT.
import jwt
import time
import json
import requests
iat = int(time.time())
exp = iat + 3600
claim_set = {"iss": "datastudio-manager#data-project.iam.gserviceaccount.com",
"scope": "https://www.googleapis.com/auth/datastudio https://www.googleapis.com/auth/datastudio.readonly https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
"aud": "https://oauth2.googleapis.com/token", "exp": exp, "iat": iat}
encoded = jwt.encode(claim_set, private_key, algorithm="RS256")
response = requests.post("https://oauth2.googleapis.com/token", params={
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": encoded
})
print(response.json()["access_token"])
Using token generated from above step to make API call.
curl -H "Authorization: Bearer <access_token>" https://datastudio.googleapis.com/v1/assets:search?assetTypes=report
Response of the above request is {} with status 200 and when I am trying to view permissions for a particular asset it is giving me the following response.
API endpoint: https://datastudio.googleapis.com/v1/assets/<asset_id>/permissions
{
"error": {
"code": 403,
"message": "The caller does not have permission",
"status": "PERMISSION_DENIED"
}
}
Is authentication using access token generated through the above oAuth2 method supported for Data studio APIs?
Any leads are much appreciated, thanks in advance!
I have created project in google console
Enable the Dialogflow API
Created OAuth v2 credential
Using this credentials i called access token api to generate token
https://accounts.google.com/o/oauth2/v2/auth?
scope=https://www.googleapis.com/auth/dialogflow&
access_type=offline&
include_granted_scopes=true&
response_type=code&
state=state_parameter_passthrough_value&
redirect_uri=http://localhost&
client_id= **i placed client id here**
I received access token and passed it to Dialog flow API
https://dialogflow.googleapis.com/v2/projects/**PROJECT-ID**/agent/sessions/123456:detectIntent
Header
Content-Type : application/json; charset=utf-8
Authorization : Bearer **ACCESS_TOKEN**
Body
{
"query_input": {
"text": {
"text": "I know french",
"language_code": "en-US"
}
}
}
Still i am getting this error
"error":{"code": 401, "message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",…}
i am not able to identify where i went wrong
Please help thanks in advance
The code that i was passing in api was the OAuth Code(Thanks John Hanley)
API to generate Access token from OAuth Code
Post : https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded
{
"code":"OAuth Code",
"client_id":"Client ID",
"client_secret":"Client Secret",
"redirect_uri":"http://localhost",
"grant_type":"authorization_code"
}
In response you receive this
Response
{
"access_token": "Token",
"expires_in": 3599,
"refresh_token": "Refresh Token",
"scope": "https://www.googleapis.com/auth/dialogflow",
"token_type": "Bearer"
}
Pass this access token in header of google API
I want to implement the Google Cloud speech to text using a service account. What i have try is i have set the environment variable to that json and send the post request to this url 'https://speech.googleapis.com/v1/speech:longrunningrecognize'.
Code:
req = requests.post(url, data={
"audio":{
"content":enc
},
"config":{
"audioChannelCount":2,
"enableSeparateRecognitionPerChannel":True,
"enableWordTimeOffsets":True,
"diarizationConfig":{
"enableSpeakerDiarization": True,
"minSpeakerCount": 1,
"maxSpeakerCount": 2
},
}})
Error:
403
{
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"status": "PERMISSION_DENIED"
}
}
The error message indicates that you are not authenticating correctly. The way to do this is to pass an authentication token as a Bearer Token header in your request.
The following documentation explains how to generate the required credentials and pass them with the request, this provides an overview of service accounts Service accounts overview
Creating a service account instructions Creating service accounts
Once you have created the service account you generate the credentials which are stored in json format, these are then passed as a Bearer Token
I built a sample container(sample of Go, https://cloud.google.com/run/docs/quickstarts/build-and-deploy) and deployed to cloud run (I unchecked "allow unauthenticated invocations").
However, when I open the endpoint URL of my service, I get a 401 page,
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>401 Unauthorized</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Unauthorized</h1>
<h2>Your client does not have permission to the requested URL <code>/</code>.</h2>
<h2></h2>
</body></html>
According to the official document (https://cloud.google.com/run/docs/securing/authenticating#developers), I used this command.
curl -H "Authorization: Bearer $(gcloud config config-helper --format 'value(credential.id_token)')" [SERVICE_URL]
My user account has "roles/run.invoker"
When I checked "allow unauthenticated invocations", I got an expected result.
Is there anything to open the endpoint?
Please make sure gcloud version is at least 243.0.0 by "gcloud --version"
Please use "gcloud components update" to update if gcloud is too old
[Updated] Using gcloud in Cloud Shell to call authentication-required Cloud Run service is also working now.
Thanks!
I managed to connect from a cloud function, finally, after 15-minute wrestling with the same 401 error when invoking a cloud run service that does not allow unauthenticated invocations:
import google.auth
auth_req = google.auth.transport.requests.Request()
id_token = google.oauth2.id_token.fetch_id_token(auth_req, URL)
headers = {'Authorization': 'Bearer ' + id_token,
"Content-Type": "application/json; charset=utf-8"}
response = requests.post(URL, json=request_json, headers=headers)