Why is authentication to Cloud Functions different than Cloud Endpoints? - google-cloud-platform

In order to authenticate with Cloud Endpoints for OpenAPI, I have to construct a Python requests session using google-auth as follows:
from google.auth.transport.requests import AuthorizedSession
creds = google.auth.jwt.Credentials.from_service_account_file(
creds_path, audience=my_audience)
session = AuthorizedSession(creds)
But when I want to authenticate to a Cloud Function, I have to do it a little different:
creds = google.oauth2.service_account.IDTokenCredentials.from_service_account_file(creds_path, target_audience=function_url)
session = AuthorizedSession(creds)
And all that is when I use a service account file, such as when running from my local machine, or on GKE. But when it's used on App Engine, there's another variant:
Calling Cloud Endpoints -
boostrap_creds, _ = google.auth.default()
creds = google.auth.jwt.Credentials.from_signing_credentials(boostrap_creds, my_audience)
session = AuthorizedSession(creds)
Calling Cloud Function --
IAM_SCOPE = 'https://www.googleapis.com/auth/iam'
OAUTH_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
bootstrap_credentials, _ = google.auth.default(scopes=[IAM_SCOPE])
signer_email = bootstrap_credentials.service_account_email
signer = bootstrap_credentials.signer
creds = google.oauth2.service_account.IDTokenCredentials(
signer, signer_email, token_uri=OAUTH_TOKEN_URI, target_audience=function_url)
session = AuthorizedSession(creds)
Why is there a difference, and what does it mean?

In the Cloud Endpoints when you use: google.auth.jwt.Credentials to authenticate a user, a client application sending JSON Web Token (JWT) in the authorization header of the HTTP request to your backend API. There are two components to the token, a public and private string. The private string is used when signing the request, and never sent across the wire. The Extensible Service Proxy (ESP) validates the token for your API, so you don't add any code in your API to process the authentication. Those Access tokens, are not intended to carry information about the user. They simply allow access to certain defined server resources. Endpoints uses plain OAuth 2.
In the Cloud Function when you use: google.oauth2.service_account.IDTokenCredentials to autenticate uses oAuth2 ID token. ID Token is a token granted by the OpenID Provider that contains information about End-User, in this situation about service account. This information tells client application that the user is authenticated, and can also give information like their username.
You can pass an ID Token around different components of your client, and these components can use the ID Token to confirm that the user is authenticated and also to retrieve information about them. Functions uses more advanced OpenID Connect.
If you want to know more about OAuth:
Differences between OAuth 1 and 2.
OpenID connect

Related

Access Django Rest API using Azure access token and SimpleJWT access token

Need just hint, tried all possible ways.
Any approach is highly appreciated.
Problem statement: access jwt authenticated django rest api using azure ad access token in postman and local app. django app is hosted on azure app service.
Challenge: pass two token with different header values in authorisation header such that azure token is also reader with django jwt token.
A. All possible authorisation in postman.
B. Different authorization keys and header values in django jwt settings
I've deployed my django application on azure app service.
I'm using JWT authentication for all rest API's.
I've an azure directory and service principal linked to azure web app.
In postman,
I can get access token from azure active directory(using clientID, Secret, resource, etc.) and use the same token to call django rest api.
I can easily access unauthenticated API just by using azure access taken in authorization bearer header.
For JWT authenticated API, I'm not able to use them (crud operation) as none of my approach is working.
Azure access token header value : Bearer
Django JWT token header value: Bearer, Token, JWT.
---- EDIT ----
Django application will server as a backend to client applications. Thus client application have to generate azure token and provide while calling django app API. But django application API's are also authenticted with JWTAuthentication, thus 2 tokens have to provided.
Problem
Both Tokens have to be provided in 'Authorisation' key to use with HTTP_AUTHORISATION.
INFORMATION
JWT packages: simplt_jwt
simplt_jwt,django version: latest
client: react-js webapp, swift ios mobile app
resources: azure app service, azure active directory with service plan
django website is used as a backend for webapp and mobile app.
To elaborate, some images are added:
Need to use this architecture (api endpoint with jwt authentication):
Call an API with JWT authentication header value in (Bearer, Token, JWT), and have to provide Azure access token withheader value as (Bearer).
Both Tokens have to be provided in authorisation header.
[api endpoint with jwt authentication][1]
[1]: https://i.stack.imgur.com/y0Uvf.png
Called an API(wihout django JWT authentication) using only azure access token and was able to get response.
Correct me if I'm using some wrong approach.
Add another custom backend and verify your Azure token by its public key:
https://docs.djangoproject.com/en/4.1/topics/auth/customizing/
And add it next to your SimpleJWT auth backend.
In your settings.py file:
REST_FRAMEWORK = {
...
'DEFAULT_AUTHENTICATION_CLASSES': (
...
'rest_framework_simplejwt.authentication.JWTAuthentication',
# add your azure backend here
'your_app.auth_azure_backend.AzureAuthentication',
)
...
}
from django.contrib.auth.backends import BaseBackend
class AzureAuthentication(BaseBackend):
def authenticate(self, request, token=None):
azure_token = request.headers['AzureToken'] # you can use custom headers or just use `Authentication` with Bearer token. Django will go through every backend to verify it.
decoded = jwt.decode(azure_token, public_key, algorithms=["RS256"])
# return user instance based on decoded data from Azure
If you can decode without error that means your token is generated by Azure AD.
You can follow this question to get your public key https://learn.microsoft.com/en-us/answers/questions/793793/azure-ad-validate-access-token
So I found a solution, if wrong please provide feedback.
I have create an authentication class inheriting JWTAuthentication class. And reading custom headers in request.headers. this way I can provide multiple tokens in a request.
Actually, My application is hosted on azure app service. So have to authenticate send also application have some inbuilt authentication to manage user access, thus need token for the same.

Should you use Client Credentials Grant Type with authenticating server-to-server?

I'm creating an api service ("My Api") where the end users are other apis ("Client"). This is my first application where the Client is not an actual person, so I want to make sure I'm going through the authentication flow correctly.
I'm using AWS Cognito and have based the authentication flow off the "Client credentials grant" section of this post.
The flow I have right now is:
Client registers with My Api
My Api creates an app client on AWS. I have a simple dashboard that will display the client_id and client_secret to the Client (My Api exposes an endpoint to rotate client_secrets)
Client sends the following POST to my AWS oauth2 domain
curl -X POST \
https://[DOMAIN_NAME].auth.[REGION].amazoncognito.com/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'authorization: Basic BASE64(client_id:client_secret)' \
-d 'grant_type=client_credentials&scope=[SCOPE]'
Client receives access_token in the form of a jwt from AWS
Client sends access_token in authorization header to My Api
My Api verifies the access_token is valid
My Api provides access to resources for the applicable scope and client_id
It seems strange that I have to create an app client on AWS Cognito for each Client. Is that normal when you're authenticating using client credentials instead of an authorization code?
If that's the case, can someone direct me to what the pricing is for each each app client? Is it in the "Users who sign in directly with their User Pool credentials or with social identity providers:" section on this page?
After some time to think about this, this is what I would do (preface: this is definitely not AWS/Banking level authentication). The code below is in postgres.
I would design the database schema to accommodate multiple tenants see this paper by Google for ideas. Each User (eg, employee of an Organization) will have a Cognito User which will be linked to the User.
CREATE TABLE organizations (
org_id uuid
);
CREATE TABLE users (
user_id uuid,
cognito_uid uuid,
org_id uuid REFERENCES organizations(org_id)
);
CREATE TABLE secret_stuffs (
secrets varchar
);
I would then create an api_keys table.
// We only want user to have two keys max
CREATE TYPE api_key_type AS ENUM (
'primary',
'secondary'
);
CREATE TABLE api_keys (
PRIMARY KEY (
user_id,
key_type
)
org_id uuid REFERENCES organizations(org_id),
user_id uuid REFERENCES users(user_id),
key_type api_key_type,
private_key varchar
);
// You'd probably want to create a composite index with user_id and private_key fields since we'll create a function that access both
I would lock down the api_keys and secret_stuffs table (ie, not grant access to any role) and create a SECURITY DEFINER function that takes the user_id and private_key as inputs, checks that that row exists in your api_keys table and returns whatever you need from the secret_stuffs table.
I had the same challenge months ago when we had to authenticate requests from server side applications. And based on my research, Implicit flow and authorization code flow is applicable for Front-End login authentication, and Client Credentials flow is for machine-to-machine. I also setup a separate Database to map the the Client App ID for each of the server side applications that we plan to authenticate.
I found this tutorial on how to use OAuth 2.0 Client credentials Flow. It was discussed in good details with example on how to test in postman.
Authorization: Basic BASE64(CLIENT_ID:CLIENT_SECRET)

What is the URL to authenticate Gsuite users using curl?

I want to authenticate Gsuite users in order for them to be able to create groups from my company's application, I have to do so using CURL, what URL should I send a post request to?
For example, if I want to login a user to Google plus, I would hit this url
CURLOPT_URL => "https://www.googleapis.com/plus/v1/people/me?access_token=" . $access_token,
What url is for Gsuite?
If your goal is to retrive the information about a user in G Suite:
CURLOPT_URL => "https://www.googleapis.com/admin/directory/v1/users/john#example.com?access_token=" . $access_token;
Note: Please consult the Directory API on how delegation is performed. This is required. Normal Access Tokens will not work without Domain-wide Delegation enabled.
Your credentials (Access Token) will need the correct scopes:
https://www.googleapis.com/auth/admin.directory.group
https://www.googleapis.com/auth/admin.directory.user
Your credentials will need the correct delegation.
Python example:
SCOPES = [
"https://www.googleapis.com/auth/admin.directory.group",
"https://www.googleapis.com/auth/admin.directory.user"
]
key_file = 'google-directory-api-service-account.json'
SERVICE_ACCOUNT_EMAIL = 'directory#development-123456.iam.gserviceaccount.com'
ADMIN_EMAIL = 'gsuite-admin#example.com'
credentials = service_account.Credentials.from_service_account_file(
key_file,
scopes = SCOPES)
credentials = credentials.with_subject(ADMIN_EMAIL)
Domain-wide Delegation
See the bottom of this answer for common errors that I have seen when setting up G Suite access.
If your goal is to retrieve information stored within a Google OAuth 2.0 Token:
These urls expects a Google OAuth 2.0 Access Token. The alt=json specifies returning JSON.
Examples that you can test in a command prompt:
curl -k "https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=ACCESS_TOKEN"
curl -k "https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=ACCESS_TOKEN"
There is also the v3 endpoint for :
curl -k "https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=ACCESS_TOKEN"
Common problems when setting up API access to G Suite:
Access Not Configured. Admin Directory API has not been used in project 123456789012 before or it is disabled.
Go to the Google Cloud Console. Enable the API for Admin SDK.
Not Authorized to access this resource/api.
You have not setup Domain-wide delegation correctly.
Client is unauthorized to retrieve access tokens using this method
You tried to setup Domain-wide delegation on an existing service account. You need to create a new service account that does not have any IAM Roles assigned.

Making Authorized Calls to an Google Cloud Endpoints API

My Current Setup
Google Cloud Endpoints hosted on Google App Engine.
Google Echo Tutorial (https://cloud.google.com/endpoints/docs/frameworks/python/get-started-frameworks-python)
Python local server making requests to the echo API.
The echo tutorial is up and running. I can make calls to open endpoints and the one requiring an API key using a python script on my machine. I have not been able to make an authorized API call with a Google ID token. None of the Google examples have worked so far.
From my understanding, the workflow should be
Use a key file to authorize the service account to generate a JWT.
Use the JWT to generate a Google ID token.
Google Example: https://cloud.google.com/endpoints/docs/openapi/service-account-authentication#using_a_google_id_token (Key File)
The code fails. Function get_id_token() return res['id_token'] fails with no id_token in res.
Has anyone gotten the example to work? Does anyone have an example of making an authorized API call to an Endpoint API with a Google ID token from a service account?
The main issue was generating the JWT and the code that works for me is below. I have yet to find a better way to do this that works. If you know of a better way please submit your answers below or add a comment. The code that generates the Google ID Token from JWT is exactly from Google documentation here (https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/clients/service_to_service_google_id_token/main.py) get_id_token function.
def generate_jwt(audience, json_keyfile, service_account_email):
"""Generates a signed JSON Web Token using a Google API Service Account.
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/endpoints/getting-started/clients/google-jwt-client.py
"""
# Note: this sample shows how to manually create the JWT for the purposes
# of showing how the authentication works, but you can use
# google.auth.jwt.Credentials to automatically create the JWT.
# http://google-auth.readthedocs.io/en/latest/reference/google.auth.jwt.html#google.auth.jwt.Credentials
signer = google.auth.crypt.RSASigner.from_service_account_file(json_keyfile)
now = int(time.time())
expires = now + 3600 # One hour in seconds
payload = {
'iat': now,
'exp': expires,
'aud': 'https://www.googleapis.com/oauth2/v4/token',
# target_audience must match 'audience' in the security configuration in your
# openapi spec. It can be any string.
'target_audience': audience,
'iss': service_account_email
}
jwt = google.auth.jwt.encode(signer, payload)
return jwt

How can I verify a Google authentication API access token?

How can I verify a Google authentication access token?
I need to somehow query Google and ask: Is [given access token] valid for the [example#example.com] Google account?
Short version
It's clear how an access token supplied through the Google Authentication Api :: OAuth Authentication for Web Applications can be used to then request data from a range of Google services. It is not clear how to check if a given access token is valid for a given Google account. I'd like to know how.
Long version
I'm developing an API that uses token-based authentication. A token will be returned upon provision of a valid username+password or upon provision of a third-party token from any one of N verifiable services.
One of the third-party services will be Google, allowing a user to authenticate against my service using their Google account. This will later be extended to include Yahoo accounts, trusted OpenID providers and so on.
Schematic example of Google-based access:
The 'API' entity is under my full control. The 'public interface' entity is any web- or desktop-based app. Some public interfaces are under my control, others will not be and others still I may never even know about.
Therefore I cannot trust the token supplied to the API in step 3. This will be supplied along with the corresponding Google account email address.
I need to somehow query Google and ask: Is this access token valid for example#example.com?
In this case, example#example.com is the Google account unique identifier - the email address someone uses to log in to their Google account. This cannot be assumed to be a Gmail address - someone can have a Google account without having a Gmail account.
The Google documentation clearly states how, with an access token, data can be retrieved from a number of Google services. Nothing seems to state how you can check if a given access token is valid in the first place.
Update
The token is valid for N Google services. I can't try a token against a Google service as means of verifying it as I won't know which subset of all Google's services a given user actually uses.
Furthermore, I'll never be using the Google authentication access token to access any Google services, merely as a means of verifying a supposed Google user actually is who they say they are. If there is another way of doing this I'm happy to try.
For user check, just post
get the access token as accessToken and post it and get the response
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=accessToken
you can try in address bar in browsers too, use httppost and response in java also
response will be like
{
"issued_to": "xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"audience": "xxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"user_id": "xxxxxxxxxxxxxxxxxxxxxxx",
"scope": "https://www.googleapis.com/auth/userinfo.profile https://gdata.youtube.com",
"expires_in": 3340,
"access_type": "offline"
}
The scope is the given permission of the accessToken. you can check the scope ids in this link
Update:
New API
post as below
https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123
Response will be as
{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser#gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}
For more info, https://developers.google.com/identity/sign-in/android/backend-auth
Ok, most answers are valid but not quite right. The idea of JWT is that you can validate the token without the need to contact the issuer everytime. You must check the id and verify the signature of the token with the known public key of the certificate google used to sign the token.
See the next post why and how to do this.
http://ncona.com/2015/02/consuming-a-google-id-token-from-a-server/
you can verify a Google authentication access token by using this endpoint:
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=<access_token>
This is Google V3 OAuth AccessToken validating endpoint, you can refer from google document below: (In OAUTH 2.0 ENDPOINTS Tab)
https://developers.google.com/identity/protocols/OAuth2UserAgent#validate-access-token
function authenticate_google_OAuthtoken($user_id)
{
$access_token = google_get_user_token($user_id); // get existing token from DB
$redirecturl = $Google_Permissions->redirecturl;
$client_id = $Google_Permissions->client_id;
$client_secret = $Google_Permissions->client_secret;
$redirect_uri = $Google_Permissions->redirect_uri;
$max_results = $Google_Permissions->max_results;
$url = 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token='.$access_token;
$response_contacts = curl_get_responce_contents($url);
$response = (json_decode($response_contacts));
if(isset($response->issued_to))
{
return true;
}
else if(isset($response->error))
{
return false;
}
}
Use the below endpoint to get user info such as name, email, photo etc.
https://www.googleapis.com/oauth2/v3/userinfo?access_token=<access token>
Use the below endpoint to get token info, such as expiry time, token scope etc.
https://www.googleapis.com/oauth2/v3/tokeninfo?access_token=<access token>
Google oauth code flow response in addition to access_token also returns id_token that contains useful for validation info in encrypted form.
One thing that makes ID tokens useful is that fact that you can pass
them around different components of your app. These components can use
an ID token as a lightweight authentication mechanism authenticating
the app and the user. But before you can use the information in the ID
token or rely on it as an assertion that the user has authenticated,
you must validate it.
Validation of an ID token requires several steps:
Verify that the ID token is a JWT which is properly signed with an appropriate Google public key.
Verify that the value of aud in the ID token is equal to your app’s client ID.
Verify that the value of iss in the ID token is equal to accounts.google.com or https://accounts.google.com.
Verify that the expiry time (exp) of the ID token has not passed.
If you passed a hd parameter in the request, verify that the ID token has a hd claim that matches your Google Apps hosted domain.
https://developers.google.com/identity/protocols/OpenIDConnect#validatinganidtoken link has code samples for validation of ID tokens.
See also https://security.stackexchange.com/questions/37818/why-use-openid-connect-instead-of-plain-oauth.
As per Google's documentation, you should use Google's AP Client Library that makes this (token verification, claim extraction etc.) much easier than writing your own custom code.
From a performance perspective, the token should be parsed locally without making a call to Google again. Off-course Google's public key is needed and retrieval of that key is done using a caching strategy, implemented in the Google's client library from #1 above.
FYI only. Google also uses a JWT token. See image below for reference.
Here's an example using Guzzle:
/**
* #param string $accessToken JSON-encoded access token as returned by \Google_Client->getAccessToken() or raw access token
* #return array|false False if token is invalid or array in the form
*
* array (
* 'issued_to' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
* 'audience' => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
* 'scope' => 'https://www.googleapis.com/auth/calendar',
* 'expires_in' => 3350,
* 'access_type' => 'offline',
* )
*/
public static function tokenInfo($accessToken) {
if(!strlen($accessToken)) {
return false;
}
if($accessToken[0] === '{') {
$accessToken = json_decode($accessToken)->access_token;
}
$guzzle = new \GuzzleHttp\Client();
try {
$resp = $guzzle->get('https://www.googleapis.com/oauth2/v1/tokeninfo', [
'query' => ['access_token' => $accessToken],
]);
} catch(ClientException $ex) {
return false;
}
return $resp->json();
}
I need to somehow query Google and ask: Is this access token valid for example#example.com?
No. All you need is request standard login with Federated Login for Google Account Users from your API domain. And only after that you could compare "persistent user ID" with one you have from 'public interface'.
The value of realm is used on the Google Federated Login page to identify the requesting site to the user. It is also used to determine the value of the persistent user ID returned by Google.
So you need be from same domain as 'public interface'.
And do not forget that user needs to be sure that your API could be trusted ;) So Google will ask user if it allows you to check for his identity.
Try making an OAuth-authenticated request using your token to https://www.google.com/accounts/AuthSubTokenInfo. This is only documented to work for AuthSub, but it works for OAuth too. It won't tell you which user the token is for, but it will tell you which services it's valid for, and the request will fail if the token is invalid or has been revoked.
An arbitrary OAuth access token can't be used for authentication, because the meaning of the token is outside of the OAuth Core spec. It could be intended for a single use or narrow expiration window, or it could provide access which the user doesn't want to give. It's also opaque, and the OAuth consumer which obtained it might never have seen any type of user identifier.
An OAuth service provider and one or more consumers could easily use OAuth to provide a verifiable authentication token, and there are proposals and ideas to do this out there, but an arbitrary service provider speaking only OAuth Core can't provide this without other co-ordination with a consumer. The Google-specific AuthSubTokenInfo REST method, along with the user's identifier, is close, but it isn't suitable, either, since it could invalidate the token, or the token could be expired.
If your Google ID is an OpenId identifier, and your 'public interface' is either a web app or can call up the user's browser, then you should probably use Google's OpenID OP.
OpenID consists of just sending the user to the OP and getting a signed assertion back. The interaction is solely for the benefit of the RP. There is no long-lived token or other user-specific handle which could be used to indicate that a RP has successfully authenticated a user with an OP.
One way to verify a previous authentication against an OpenID identifier is to just perform authentication again, assuming the same user-agent is being used. The OP should be able to return a positive assertion without user interaction (by verifying a cookie or client cert, for example). The OP is free to require another user interaction, and probably will if the authentication request is coming from another domain (my OP gives me the option to re-authenticate this particular RP without interacting in the future). And in Google's case, the UI that the user went through to get the OAuth token might not use the same session identifier, so the user will have to re-authenticate. But in any case, you'll be able to assert the identity.
Check below URL. It works well. Its official document from Google itself.
Using one of the Google API Client Libraries (e.g. Java, Node.js, PHP, Python) is the recommended way to validate Google ID tokens.
https://developers.google.com/identity/sign-in/android/backend-auth#using-a-google-api-client-library