How to authenticate Python client app for access to restricted Google Cloud function? - authentication

I've created a Google Cloud function and I would like to access it from a Python application I'm developing. I am able to access the function when there is no authentication required, but can't access the functions when I enable authentication.
Here is the service account key I'm using with stripped out info. The only role it is configured for is invoking cloud functions.
{
"type": "service_account",
"project_id": "XYZ",
"private_key_id": "XYZ",
"private_key": "XYZ",
"client_email": "XYZ",
"client_id": "XYZ",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "XYZ"
}
It seems that authenticated requests require a token that's included in the requests Authorization header, but I don't understand where to acquire this token.
I've tried using the approach outlined here with environment variables and the default auth method, but this doesn't work. I assume this is because the service account key is different from an OAuth token. (I have created a new service account the Cloud Functions invoker permission and am using that service account key). I receive the following error:
google.auth.exceptions.RefreshError: ('invalid_scope: Invalid OAuth scope or ID token audience provided.', '{"error":"invalid_scope","error_description":"Invalid OAuth scope or ID token audience provided."}')
How do I generate this token to authenticate the request from my Python script? Or is the approach with the service account recommended, but something else is going wrong?

I was able to get this working, though at time of writing there is a bug in the documentation that threw me off.
Access protected Cloud Functions is possible by using the IDTokenCredentials class within the google-auth library:
credentials = service_account.IDTokenCredentials.from_service_account_file(
SERVICE_ACCOUNT_JSON_FILE,
target_audience="https://function/url/here",
)
authed_session = AuthorizedSession(credentials)
response = authed_session.post(url)
I have my service account configured with the "Cloud Functions Invoker" role.

Your problem is almost certainly in the roles you gave that service account. Service accounts are finicky and the roles / permissions do not always act the way you think they will in my experience. Start by making a Service Account that has full permissions (project owner). Use that service account in your script then begin limiting the permissions from there. It sounds like you will need at a minimum cloud function "admin". If that works try another level down. Cloud function "developer" etc.

If you are using, for instance, App Engine or even other Cloud Functions to connect to your Cloud Function, you can use this: Function-to-function, the steps, basically are:
Grant the Cloud Functions Invoker.
In the calling function, you'll need to:
Create a Google-signed OAuth ID token with the audience (aud) set to the URL of the receiving function
Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the function.
import requests
//# TODO<developer>: set these values
REGION = 'us-central1'
PROJECT_ID = 'my-project'
RECEIVING_FUNCTION = 'my-function'
//# Constants for setting up metadata server request
//# See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
function_url = f'https://{REGION}-{PROJECT_ID}.cloudfunctions.net/{RECEIVING_FUNCTION}'
metadata_server_url = \
'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience='
token_full_url = metadata_server_url + function_url
token_headers = {'Metadata-Flavor': 'Google'}
def calling_function(request):
//# Fetch the token
token_response = requests.get(token_full_url, headers=token_headers)
jwt = token_response.text
//# Provide the token in the request to the receiving function
function_headers = {'Authorization': f'bearer {jwt}'}
function_response = requests.get(function_url, headers=function_headers)
return function_response.text
I have tested this solution and works as expected.
If you're invoking a function from a compute instance that doesn't have access to compute metadata (e.g. your own server), you'll have to manually generate the proper token:
Self-sign a service account JWT with the target_audience claim set to the URL of the receiving function.
Exchange the self-signed JWT for a Google-signed ID token, which should have the aud claim set to the above URL.
Include the ID token in an Authorization: Bearer ID_TOKEN header in the request to the function.
The Cloud IAP docs have sample code to demonstrate this functionality. The part you could be interested in should be this Authenticating from a service account

Related

Azure B2C Implicit Flow not working in ASP.NET when "Access Token" is disabled on the app registration

I'm testing authentication for a server based web app.
I think the best way to login the browser is with Implicit Flow to obtain only an id_token via an HTTP POST.
I've configured my OpenIdConnectAuthentiationOptions like:
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken,
ResponseMode = OpenIdConnectResponseMode.FormPost,
These should be the options to do what I am asking.
When I try to authenticate, I am given the error:
'AADB2C90057: The provided application is not configured to allow the
'OAuth' Implicit flow.
But, I have enabled Implicit Flows in the app registration in Azure AD B2C. However, I specifically am NOT enabling "Access Token," because obviously I am NOT requesting one.
I'm specifically following the instructions to select only "ID tokens," for web apps using hybrid authentication.
If I change the option to ResponseType = OpenIdConnectResponseType.CodeIdToken I have the same issues. Although, I think this response type also requests an Access Code.
If I enable both ID Tokens and Access Tokens, then the app logs in fine. Why do I have to enable "Access Tokens" when I am only requesting an ID Token?
The basis for what I am attempting is described here: https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp
Please check the Important note from OAuth 2.0 implicit grant flow | Microsoft Docs which is the same doc which can be obtained by clicking 'learn more about tokens.' hyperlink.
NOTE:
Which says we need to check both idtoken and accesstoken to obtain id token or access token or in combination of both, but the response depends on the responseType mentioned in the authentication request (you may read same document further for the details).
i.e.;
You will receive idtoken , if you have given responsetype =id_token
You will receive accesstoken if responseType=token
You will receive code and id token in case of responsetype is
id_token+code
Request an ID token as well or hybrid flow

Identity Server 4, External providers and Web API

I’m in process of developing system which consists from such parts:
- Some services under gateway (Ocelot)
- Mobile client (iOS)
- Identity Server 4
Mobile client hasn’t been prepared yet, so I use Postman for emulating requests from it. My problem is implementation of Authentication with External providers, like Google. It’s my first experience of using IS 4, so I have some misunderstanding and difficulties. Excuse me, if my question is too abstract or if I miss smth obvious.
I successfully deployed IS 4 using all this tutorials and it works with Password Credentials flow in a proper way: I request IS for access token, sending user credentials, it returns token and I can successfully use it for access to my API methods.
Situation with External Providers are different. I’ve overviewed this tutorial (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-3.1) and some other and add code from it to the IS project. I can successfully log in with Google, using a button on that IS4 web-page which goes with IS 4 Quickstart UI template. But no chance to work with API. As I understand in such workflow client-app should go for a token not to my IS as in example with a local user, but to the Google Auth provider. And I emulated it with Postman and got a strange access_token which has no data and it_token which contains username, email and so on. I try to use this id_token with requests to my API. The result is always 401.
Where I’m wrong? How should I build requests to API with token from Google? Or I have misunderstanding and there should be another flow: client goes to IS with specific request, IS goes to Google and then returns proper token to Client?
Here is configuration of authecation on the side of Web API app:
private void ConfigAuthentication(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.Audience = "k_smart_api";
});
}
Here is config of Google-Auth on the side of IdentityServer:
services.AddAuthentication().AddGoogle(opts => {
opts.ClientId = "My google client Id";
opts.ClientSecret = "my google client secret";
opts.SignInScheme = IdentityConstants.ExternalScheme;
opts.SaveTokens = true;
});
This is how I get Access Token:
postman exampple
The tokens you get back from Google, is only used to Authenticate the user in Identity Server. Then after Identity Server receives those tokens, it sign-in the user and create new tokens (ID+access) that are passed to your client. you should look at using the authorization code flow in your client to authenticate the user and to get the tokens. then use the access token received to access your API.
do remember that the tokens received from Google are not used to give access to your APIs.

IdentityServer4 with LDAP/AD authentication without UI

I'm currently working on a project where I'm trying to set up a service based on IdentityServer4 (https://github.com/IdentityServer/IdentityServer4) that authenticates users by querying a local Active Directory via LDAP.
To achieve that, I also included the IdentityServer4.LdapExtension (https://github.com/Nordes/IdentityServer4.LdapExtension) in my project. The working example from the repository works fine (https://github.com/Nordes/IdentityServer4.LdapExtension/tree/master/Sample/IdentityServer) - but the custom logic is part of the UI, and I need my service to operate without any UI.
Simply adding
.AddLdapUsers<ActiveDirectoryAppUser>(Conf.GetSection("ldap"), UserStore.InMemory)
as described in the documentation does not change the request pipeline, as the provided login/validation methods are never executed - they are only triggered with calls from the UI (AccountController). However, as I said, I don't want to integrate any UI in this service and rather use the interface which the Token-Endpoint already provides (POST request with client_id and client_secret, response with JWT).
Is there a way to integrate LDAP authentication without rewriting big parts that work out-of-the-box as desired?
From your question it sounds like you already have a username and password. Note client_id != username and client_secret != password. client_id is the identity for a client application.
The grant type you are trying to use is called Resource Owner Password when using the authorize endpoint or password when using the token endpoint.
This grant type is used to support legacy systems and is not recommended for new development.
The code that you want to executed to authenticate a user is in LdapUserResourceOwnerPasswordValidator.cs and it should be executed if you pass the correct parameters to the token endpoint:
POST /connect/token
client_id=yourclientid&
client_secret=yourclientsecret&
grant_type=password&
username=yourusername&password=yourusernamespassword
See token endpoint documentation: https://identityserver4.readthedocs.io/en/release/endpoints/token.html
You can use Identity Model to help you make the token request:
var response = await client.RequestPasswordTokenAsync(new PasswordTokenRequest
{
Address = "https://demo.identityserver.io/connect/token",
ClientId = "yourclientid",
ClientSecret = "yourclientsecret",
UserName = "yourusername",
Password = "yourusernamespassword"
});
This is documented here https://identitymodel.readthedocs.io/en/latest/client/token.html

Google Photos API - authentication

I'm trying to get list of my shared albums from Google Photos.
I found a enable Photos API in Google Developers Console.
HTTP GET:
https://content-photoslibrary.googleapis.com/v1/sharedAlbums?key=AIzaSyCkXXXXXXXXXXXXXZiOSe9IiyM8E
RESULT:
{ "error": { "code": 401, "message": "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.", "status": "UNAUTHENTICATED" } } 1
Configuration in developers console:
Please, what I'm doing wrong? Thank you.
Google API need an access token to make sure that the user has the permission to access the feature. Access token is just like cookie that should be send together with the request.
Usually you will need so many setup to get the access token with your own code. But there are a client library that can help you access Google API with small setup.
Access token also has a lifetime, so if you don't use the library you will need to manually refresh the token.
You need to configure OAUth 2.0 credentials (client ID and secret) and not an API key. More details are in the developer documentation here: https://developers.google.com/photos/library/guides/get-started#request-id
The Google Photos library API acts on behalf of a user, that's why you need to authenticate via OAuth 2.0. As part of this request you also need to specify a scope for your users to accept, see this page for more details: https://developers.google.com/photos/library/guides/authentication-authorization
I've been working on a python project to backup google photos library and album info. you can probably modify it to do exactly what you want. It is fully working but does not currently distinguish between shared and private albums.
https://github.com/gilesknap/gphotos-sync
In particular, see https://github.com/gilesknap/gphotos-sync/blob/master/gphotos/authorize.py which handles authentication and authorization for any Google service (it also handles storing the token and refreshing the token).

Use JSON Web Token and Firestore support for Bearer token

I want to have authentication and authorization support in Firestore while using a node.js app that talks to Firestore. Users interact via a URL (http.get with embedded tokens) and that interacts with a node.js app. The app accepts some input from user, then talks to firestore. The access token needed for the client to perform secure activities are all embedded in the GET url (the id + access token).
I have a flow here, and want to validate if this flow is right or I am missing something? I am unable to find the most definite document that can guide me on the steps to follow.
First off I generate JWT token part before generating the GET url:
The function used to generate the JWT token is as follows:
// generate JWT token
function getJWT() {
var token = jwt.sign({
exp: Math.floor(Date.now() / 1000) + (60 * 60) * constants.JWT_TOKEN_VALIDITY_HOURS,
admin: '​XXXXXXX'
}, constants.JWT_SECRET_TOKEN);
return token;
}
Bearer Tokens: ​I have heard in many forums that it is possible for me to send this JWT web token as a "Bearer token" in the authorisation header. It means firestore magically does all the authorization for me. Anything else I should be doing?
1) I believe I need to sign in using this custom JSON web token, and obtain an ID token. is that correct?
Sign in from a Firebase Client SDK
Use the Firebase Auth REST API to exchange the custom token to an ID token.
2) Then I need pass this ID token (not the JSON web token) to the Cloud Firestore endpoints when I am making a request for db access as an Authorization header set to Bearer {YOUR_TOKEN}.. Then you can access the Firestore REST API with the resulting ID token:
https://firebase.google.com/docs/firestore/use-rest-api#working_with_firebase_id_tokens
Imagine I embed into the header the bearer token also...using the JSON access token or the ID token I get from firestore
return this.http
.get(${OUR URL to app}, {
headers: new HttpHeaders().set('Authorization', Bearer ${JSON Web accessToken or ID Token})
})​
​User clicks on the URL that has this access token. They agree to terms and conditions and then I re-direct them to a cloud function that does some processing.​ This JSON Web token is passed along. I verify the JWT token also​ for authentication purpose using this code.​
function verifyToken(token) {
try {
var decoded = jwt.verify(token, constants.JWT_SECRET_TOKEN);
var admin = decoded.admin;
if (admin == "​XXXXXXXXX") {
return true;
}
return false;
} catch (err) {
console.log(err);
return false;
}
}
​any samples related to this will be helpful.​
related links I used
https://firebase.google.com/docs/firestore/use-rest-api#working_with_firebase_id_tokens
Firestore Custom Tokens
https://auth0.com/blog/how-to-authenticate-firebase-and-angular-with-auth0-part-1/
finally
Protocol specification for https.onCall
in https://firebase.google.com/docs/functions/callable-reference
Optional: Authorization: Bearer
A Firebase Authentication user ID token for the logged-in user making the request. The backend automatically verifies this token and makes it available in the handler's context. If the token is not valid, the request is rejected.
this is the official response I got from Google (but again works only if the user has an authentication request -- mean a valid firebase user I think), but what I want to know is using the Json web token itself can I achieve something like this.
Just to clarify, you'll need either a Firebase ID token or a Google
Identity OAuth 2.0 token to be passed on to the Cloud Firestore
endpoints as an Authorization header set to Bearer {YOUR_TOKEN}.
You may refer on the following links for more information on this:
REST API Firestore authentication with ID Token
https://firebase.google.com/docs/firestore/use-rest-api#authentication_and_authorization
Also, we don't have any samples related to this, but there's an
internal request to improve our REST documentation. I won't be able to
share you any details or timelines as to when it could materialize,
however, you may keep an eye out on our release notes or Firebase blog
for any updates we might have.
I am hopeful in stackoverflow I might get more samples related to this. But for now this is all what I got.