Authorizing access to Google Cloud Functions with ID token from Identity Platform - firebase-authentication

Intro
So I have read official docs Authenticating for invocation which is about helping developer testing and I got that working, but this approach requires a SA and a generated token. It seems the docs mix up "authentication" (proving identity) and "authorization" (giving access) which is not making it easier to get the whole picture.
I want to authorize Google Cloud Function with the user's ID token generated from Identity Platform. The official Firebase docs says:
"When a user or device signs in using Firebase Authentication, Firebase creates a corresponding ID token that uniquely identifies them and grants them access to several resources, such as Realtime Database and Cloud Storage. You can re-use that ID token to authenticate the Realtime Database REST API and make requests on behalf of that user."
My setup
I got the following artifacts to test function authorization with user:
A local React app with npm 'firebase' and a login form calling firebase.auth().signInWithEmailAndPassword.
firebase is initialized with config fields apiKey and authDomain.
An Express API deployed to Cloud Functions with default permissions, but I've provided the cloudbuild file with --allow-unauthenticated as an attempt to only focus on authorization.
A local Postman request setup calling the Express API with authorization type=Bearer Token and token set to the ID token received in the React app's onAuthStateChanged from user.getIdToken()
The Postman request responds with 401 Unauthorized. Notice it says Unauthorized, not 403 Forbidden.
Research
When reading up on the topic, I came across the following approaches to solve my problem:
Fetch the user id from the token and push it to a custom backend service which does admin.auth().setCustomUserClaims and then do the function request. GC should then hopefully know about the token's new claims.
Also about claims; generate a new token (based on current ID token?) and set claims.aud to the URL of the function. The ID token I'm using has claims.aud=projectname which I'm not sure what means.
Verify token in function code by using firebase admin. But the authorization of access is still not performed, so this approach seems to miss something.
What is required?
I suppose authentication is ok, Google Cloud should recognize the bearer token (?) but I've also read that there's no built-in functionality for this. Anyway, the authorization part is less clear to me when it comes to function requests on user level.
To summarize:
How should we authorize an ID token from Identity Platform to Google Cloud Functions? Could any of the three above-mentioned approaches be used?

Related

What's the proper way to implement a "static" authorization token feature using OIDC (or OAuth2)

I am exploring possible solutions for creating something like "API Keys" to consume my API. The goal is to allow for users to generate one or many "API Keys" from the web app and use the static generated key from the CLI app.
The web app and the client app are already using standard OIDC with JWT tokens for authentication and authorization using RBAC (role-based access control). The CLI app can already authenticate the user through the standard browser flow (redirects the user to the browser to authenticate and exchange the token back to the client).
The "API Keys" solution I am trying to achieve should have some fine-grained options where it won't authenticate as the user, but will authorize the client on behalf of the user (something like the GitHub Personal Access Token).
To me it seems like a "solved problem" as multiple services provide this kind of feature and my goal is to do it the most standard way possible using the Oauth2/OIDC protocols but I can't find details on what parts of the protocols should be used.
Can anybody provide any guidance on how it is supposed to be done using the Oauth2/OIDC entities?
Can I achieve it by only using Role-based access control or do I need Resource-based access control?
It went through the path of creating a new client for each "API Key" created, but it didn't feel right to create so many clients in the realm.
Any guidance or links to any materials are appreciated.
Can anybody provide any guidance on how it is supposed to be done
using the Oauth2/OIDC entities?
OIDC is based on OAUth 2.0 so after user login you have id tokens, access token and refresh token on the backend side. To generate new access token without asking user for authentication data you should use refresh token: https://oauth.net/2/refresh-tokens/
Can I achieve it by only using Role-based access control or do I need
Resource-based access control?
resource-based access control is more flexible solution here, but if you business requirement is not complex, then role based might be enough.
It went through the path of creating a new client for each "API Key"
created, but it didn't feel right to create so many clients in the
realm.
It is one application so you should use one client with specific configuration for access token and roles/permissions for users.
Update:
We can use GitHub as an example:
User is authenticated during login
for OIDC code is exchanged for id token, access token and refresh token
session for user is set for web browser
User can request access token
in GitHub authenticated user can request github.com/settings/personal-access-tokens/new endpoint
request is accepted, because user is authenticated based on session
backend service responsible for returning access token can obtain new access token using refresh token from point 1.
access token is returned to GitHub user
To call your API in an OAuth way, CLI users must authenticate periodically. Resulting access tokens can be long lived, as for GitHub keys, if you judge that secure enough. The access token returned can be used exactly like an API key. There may be a little friction here between usability and security.
CONSOLE FLOW
The classic flow for a console app is to use the Native Apps Desktop Flow from RFC8252. This involves the user interactively signing in using the code flow, then receiving the response on a loopback URL. It is an interactive experience, but should only be required occasionally, as for GitHub tokens.
API KEYS
The access token returned is sent in the authorization header and you can use it as an API key. Access tokens can use a reference token format. to make them shorter and confidential, to prevent information disclosure. These will be more natural in a CLI.
API AUTHORIZATION
When your API is called, it must receive access tokens containing scopes and claims, to identify the user. This will enable you to authorize correctly and lock down permissions.
{
sub: 586368,
scope: repos_write,
topic: mobile,
subscription_level: silver
exp: ?
}
TOKEN REFRESH
Sometimes CLI access tokens are long lived, for convenience. A more secure option is for the CLI to use token refresh. It can then store a refresh token in OS secure storage, then renew access tokens seamlessly. My blog post has some screenshots on how this looks, and a desktop app that does not require login upon restart. The CLI needs to deal with expired access tokens and handle 401 responses.
DYNAMIC CLIENT REGISTRATION
Some developer portal scenarios use DCR. It is another option in your security toolbox. It could potentially enable a silent client per CLI user:
User runs a standard authentication flow with a DCR scope
This returns an access token that enables client registration
The resulting token is used to register a new client
This could potentially be a client ID and client secret used in a CLI
Afterwards, the user and client are bound together. Probably not immediately relevant, but worth knowing about.

OAuth2 flow in full-stack NestJS application

Yet another OAuth2 question that isn't quite covered elsewhere.
I'm using NestJS backend, React frontend, Passport and my own DB for authentication. Trying to add an
OAuth2 identity provider (Google).
I configured my NestJS app as an OAuth Client. After logging in I'm receiving the callback at which point I have an access_token and the requested user details extracted from the payload of the id_token. This is encapsulated in a class extending PassportStrategy(Strategy, 'google') and an AuthGuard('google') and much of it is handled automatically. Code here.
At this point however, I need to maintain an authenticated session between backend (NestJS) and frontend (React). I suppose I need a JWT, but I'm wondering about the best approach:
Can I use a token provided by the IdP (e.g. id_token or access_token)? So that I don't have to worry about issuing tokens myself. I.e. the frontend receives the token (either from backend, or the IdP directly), sends it on every request, backend verifies it with the IdP on every request (verifyIdToken or getTokenInfo of google-auth-library).
One drawback here is the extra network request every time. I'm not sure if there's a need for that because the IdP is only used to identify the user, not for access to other resources. Another drawback is that I need to store a refresh token and handle when the token expires (get new one, update it on the frontend).
So alternatively, could I just issue a JWT myself and not worry about checking in with the IdP? Once the JWT expires I'd need to prompt the user to log in again. In this case, I wouldn't need to store the IdP tokens. But is this good practice? One issue I can think of is that I won't detect if the user revokes access in the IdP (until the JWT expires).
I ended up issuing my own JWT tokens and managing User sessions in my app, as described in this article: OAuth2 in NestJS for Social Login (Google, Facebook, Twitter, etc)
probably my solution would be helpful.
you could access complete source code of my app that implemented with react typescript (redux toolkit rtk Query) and nestjs included of google oauth2 flow with passport.js. resource

difficulties making Cloud Run service the target of an Apps Script project--audience and scopes

I am unable to create an OpenID Connect identity token to send as bearer token for a cloud run request. From the apps script I cannot make a token using (ScriptApp.getIdentityToken()) which has an audience the Google front end will allow through. When I arrange for the script to send a token instead that I've made with gcloud print-identity-token--identical except for the audience--that invocation succeeds.
Note I believe this may the same issue as seen here: Securely calling a Google Cloud Function via a Google Apps Script.
Also google cloud authentication with bearer token via nodejs.
One workaround suggests restructuring the GCP/Apps Script projects. Others mostly use service accounts, and use service account keys. I believe it's possible using IAM and use of google auth for one to produce a usable SA identity token (short term service account credentials) but I can't demonstrate it.
I am working around this currently, but I'd like to understand the essential problem. I think it has something to do with the cloud run service's hosting project's Oauth consent screen, and the inability to add the associated web application client-id as a scope.
In the Cloud Run docs, there is a section about performing authenticated calls to Cloud Run from other services outside GCP. The process would be the following:
Self-sign a service account JWT with the target_audience claim set to the URL of the receiving service.
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 service.
Step 1 could be performed as described here while setting the aud claim to the URL of the receiving service. I believe ScriptApp.getIdentityToken() does not set the proper audience to the JWT
For step 2, I believe you should perform a POST request to https://oauth2.googleapis.com/token with the appropriate parameters grant_type and assertion. This is explained in the "Making the access token request" section here
The resulting token should be used in step 3
I just wrote an article on that topic and I provide an easy way based on the service account credential API. Let's have a look on it and we can discuss further if required.

Retrieve user access TOKEN from WSO2 Api Manager

I have a problem with retrieving the end-user access token from wso2, I need it to invoke the API that retrieves the list of all applications in the Wso2 Api Manager Store. I did a research on this site:
https://docs.wso2.com/display/AM210/apidocs/store/#!/operations#ApplicationCollection#applicationsGet,
but I don't know how I can generate user token (not application token).
On the other side I found the temporary solution, that returns a list of all applications invoking the API login, and then API that returns the app list found on this link: https://docs.wso2.com/display/AM210/Store+APIs, but it shows me only how to do it with a session authentication, NOT with JWT token auth.
thanks in advance.
Please follow the getting started guide[1]. For a token generation, you need client id and secret. To get that you need to register an application.
[1] - https://docs.wso2.com/display/AM210/apidocs/store/#guide

Firebase authentication for private server

I am developoing a flutter app and want to use Firebase auth service to enable my users to signup/login using:
email/pass
google
facebook
I have a lumen backend REST server with MySQL database.
Problem: Going through loads of firebase documentation I cannot understand the whole flow of how this should work.
I can successfully create users using the app and they appear in the firebase console, however, I don't know how to enable them to securely talk to my backend server.
I would expect Firebase to release an access and refresh tokens for me to use for my private communication between the app and backend, like AWS cognito does. Instead, it issues an "ID Token" that is JWT token and should be verified on backend. But what do I do once it is verified?
How do I link my users in my database to the authenticated user? What is the thing to store in the database to map to the authenticated user?
Do I have to generate custom tokens via the Admin SDK?
Or is the ID Token the thing that should be passed from client to backend on each request and then verified? But still, what do I put from this ID token to my database to link the authenticated user with their data?
Here's how I do it now. It works great.
Install Firebase admin sdk on your backend server, if you are using php, here is what I've followed and worked flawlessly: PHP Firebase Admin sdk
Aquire firebase idToken using firebase SDK in your client (app), I've used Firebase auth package for this.
Send idToken to your backend
Use Admin SDK to verify the idToken, if verification is successful it returns a Firebase user object. And you can perform various management actions on it (modify, delete, get different data etc.).
Get uid from the Firebase user object.
Store uid in your database.
Now each time this authenticated user makes a request to your backend server, you attach the idToken to the header of the request.
Each time you verify (see step 4) the idToken on your backend server and if the verification is successful you extract the uid to know which user to query in your database.
Any comments/improvements on this are welcome :)