Google Actions & Auth0 Account Linking problems - node.js

I'm working on my smarthome application for Google Home. I use this library: https://github.com/actions-on-google/actions-on-google-nodejs
For authentication I use Auth0 with Account Linking. This seems to work quiet well but the Bearer token in the Authorization header is malformated (it is not a valid JWT token).
e.g.: "authorization":"Bearer sIuaA9F2N9hn_aVIgVBAb2rcrpl1m8e5"
To validate the jwt-token I want to use jwks-rsa (https://auth0.com/docs/quickstart/backend/nodejs/01-authorization#validate-access-tokens)
So I tried to add the scope id_token in the Account Linking section but I don't receive the correct access token in the header.
So finally my question: How can I make my google application request the correct access token from Auth0?
UPDATE:
To login in I use email/password credentials from auth0.
Account Linking Screenshot
this.app = smarthome({
key: settings.smarthomeKey
});
this.app.onSync(async (body, headers) => {
console.log(JSON.stringify(headers));
return {...};
});
Headers received in the sync function:
{"x-real-ip":"xx.xxx.x.xxx","x-forwarded-for":"xx.xxx.x.xxx","host":"xxxxxxxxxxxxxxxx.localtunnel.me","x-forwarded-proto":"https","x-nginx-proxy":"true","connection":"close","content-length":"79","content-type":"application/json;charset=UTF-8","google-assistant-api-version":"v1","authorization":"Bearer aIxaA9F2N9hn_aVAgVBSb2rcrpl1m8e5","user-agent":"Mozilla/5.0 (compatible; Google-Cloud-Functions/2.1; +http://www.google.com/bot.html)","accept-encoding":"gzip,deflate,br"}

Related

Replicate Postman Oauth 2.0 using Python

I have this Authorization request that works.
How can I replicate it in Python?
I am using an Azure AD to authenticate the access.
Since you are working with python, your case is a : Oauth2 login for SSR web applications with Microsoft
Goal
Get an access_token from interactive login using the oauth2 authorization code grant
Steps
Here I will list all the steps required to do it with any language
Create a web with session with at least these endpoints
/ : home page
/callback : server route or path able to receive query params like /callback?code=123456. This along with your base domain will be called redirect_uri. Sample : http://localhost:8080/callback or http://acme.com/callback
Create and configure an app in Azure Dev Console. Same process is in Google, Facebook, Linkedin, etc. As a result you should have a clientId, clientSecret and a redirect url
Create a simple web with classic session in which if user is not logged-in, redirect (302) to this url:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=foo&response_type=code&redirect_uri=foo&response_mode=query&scope=offline_access%20user.read%20mail.read
clientid and redirect_uri are important here and should be the same of previous step
After that, browser should redirect the user to the platform login
If user enters valid credentials and accepts the consent warning, Microsoft will perform another redirect (302) to the provided redirect_uri but with special value: The auth code
http://acme.com/callback?code=123456798
In the backend of /callback get the code and send it to this new endpoint
Add a client_id & client_secret parameters
Add a code parameter with the code sent by microsoft
Add a redirect_uri parameter with previously used and registered on azure. Sample http://acme.com/callback or http://localhost:8080/callback
Add a grant_type parameter with a value of authorization_code
Issue the HTTP POST request with content-type: application/x-www-form-urlencoded
You should get a response with the precious access_token:
{
token_type: 'Bearer',
scope: 'Mail.Read User.Read profile openid email',
expires_in: 5020,
ext_expires_in: 5020,
access_token: 'eyJ0oVlKhZHsvMhRydQ',
refresh_token: 's_Rcrqf6xMaWcPHJxRFwCQFkL_qUYqBLM71UN6'
}
You could do with this token, whatever you configured in azure. Sample: If you want to access to user calendar, profile, etc on behalf of the user, you should have registered this in the azure console. So the clientid is related to that and human user will be prompted with something like this
Libraries
There is some libraries provided by microsoft (c#, nodejs) which will save you a little work. Anyway the previous explanation are very detailed.
Advice
Read about oauth2 spec: https://oauth.net/2/
Read about oauth2 authorization code flow login before the implementation with python
https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2
https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow
https://github.com/msusdev/microsoft_identity_platform_dev/blob/main/presentations/auth_users_msalnet.md
Check this to understand how configure the azure web console: https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app
Check my gist https://gist.github.com/jrichardsz/5b8ba730978fce7a7c585007d3fd06b4

How to validate Google access token locally using google oAuth libraries

I'm trying to use Google's APIs to modify data on my users' Google account through the use of an id_token for authentication and an access_token to actually use Google's APIs. I know I'm able to verify the authenticity of an id token like such:
import { OAuth2Client } from "google-auth-library";
const client = new OAuth2Client(GOOGLE_CLIENT_ID);
const ticket = await client.verifyIdToken({
token: idToken,
audience: GOOGLE_CLIENT_ID,
});
This verification happens locally on my device without needing to contact Google's servers each time a token needs to be verified.
I tried to figure out how to do the same for the access_token. The top answer on How can I verify a Google authentication API access token? post suggests that I should call an endpoint https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=accessToken to do the verification but that defeats my purpose of trying to do it locally.
What Google OAuth library/method can I use to verify an access token locally. Is it even possible?
Just to reiterate, I'm talking about the access_token, not the id_token.

Firebase ID token has incorrect "aud" (audience) claim when calling from Endpoint connected to Google Functions

I am using Google Endpoints as an API gateway which is running in a Google Run container service. The API path points to a Google Function (node js). The calls to the API gateway are from a web application (viz. browser).
One of the paths is: /login which authenticates a user in firebase using the firebase.auth().signInWithEmailAndPassword method. I get the token Id of the user and send it back in the response header (authentication bearer) back to the browser. This works as expected.
When other Requests are made (e.g /check) to the endpoint the token (in the header) is included. I wanted to check the validity of the token using the Firebase Admin method before processing any requests. The code in the Google Function that does this for one of the routes is as follows:
...
const decodeIdToken = async (req, res, next) => {
// Read the ID Token from the Authorization header.
const idToken = req.headers.authorization.split('Bearer ')[1];
try {
const decodedIdToken = await admin.auth().verifyIdToken(idToken);
req.decodedToken = decodedIdToken;
next();
return;
} catch (error) {
return res.status(403).json({
status: 'failure',
data: null,
error: error.message
});
}
};
// use decodeIdToken as middleware
app.post('/check', decodeIdToken, (req, res, next) => {
return res.json({
status: 'success',
data: req.decodedToken,
error: null
});
});
When I call (via Postman ) the routes by directly calling the Google Function trigger both the routes work. However, when I call the Google Endpoints which point to the Google Function I receive the following error while using the Firebase Admin object:
Firebase ID token has incorrect \"aud\" (audience) claim. Expected \"PROJECT-ID\" but got \"https://us-central1-PROJECT-ID.cloudfunctions.net/FUNCTION-NAME\". Make sure the ID token comes from the same Firebase project as the service account used to authenticate this SDK. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token
When setting the Firebase Admin object in NodeJs I tried the following:
const admin = require('firebase-admin');
admin.initializeApp();
as well as
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://PROJECT-ID.firebaseio.com"
});
Use the X-Apigateway-Api-Userinfo Header
The header's value is the base64 encoded payload of the JWT. There's no need to reverify as API Gateway already verified the token for you and has made the contents available for your use.
Example for Node.js devs:
Buffer.from(req.header("x-apigateway-api-userinfo"), "base64").toString();
If for whatever reason you do need access to the original JWT, it is available in the X-Forwared-Authorization header.
Unnecessary Extra Credit:
To explain the error, the reason you are getting the wrong Audience claim is because the JWT you are trying to verify is a different JWT generated by API Gateway. The original Authorization Header has been replaced with this JWT. Why? It is telling Cloud Functions "Hey Cloud Function, it's me API Gateway that's calling you and here's a signed JWT to prove it". Hence API Gateway's audience ends up being the Cloud Function resource url whereas Firebase's audience is the Project the Firebase sits in.
Just another example of weird inconveniences due to Google's implementation if you ask me; they could have definitely left the Auth header untouched and had API Gateway use a different header, but beggars can't be choosers. 🤷‍♂️
Reference API Gateway Documentation:
Receiving authenticated results in your API
API Gateway usually forwards all headers it receives. However, it overrides the original Authorization header when the backend address
is specified by x-google-backend in the API config.
API Gateway will send the authentication result in the X-Apigateway-Api-Userinfo to the backend API. It is recommended to use
this header instead of the original Authorization header. This header
is base64url encoded and contains the JWT payload.
The following worked does not work (see comment below):
In the openapi-functions.yaml add the security defintion as recommended by the docs
securityDefinitions:
firebase:
authorizationUrl: ""
flow: "implicit"
type: "oauth2"
# Replace YOUR-PROJECT-ID with your project ID
x-google-issuer: "https://securetoken.google.com/YOUR-PROJECT-ID"
x-google-jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com"
x-google-audiences: "YOUR-PROJECT-ID"
Then, against the path (/check in my case), add the security section as below:
/check:
post:
...
x-google-backend:
....
....
security:
- firebase: []
....
Refer to: https://cloud.google.com/endpoints/docs/openapi/authenticating-users-firebase
There isn't problem with your admin-sdk settings, it's the idToken which is actually a jwt token retured as idToken while sign in using firebase.
Your problem is you are trying to use the JWT token returned as idToken by one of the auth() functions like firebase.auth().signInWithEmailAndPassword These do return a JWT token, however the auth claims will likely be wrong and won't pass verification by verifyIdToken. Firebase tech support confirmed this.
You have to use the firebase.auth().currentUser.getToken() function. That token will pass verification.
const idToken=await firebase.auth().currentUser.getToken()

MSAL idToken does not have upn, profile or email data? need it in idToken for passport-azure-ad authorization

I have an angular8 app using the official msal package (not the one for angular, the main one) on the backend I'm authorizing using passport-azure-ad (official microsoft package)
I need to have the user's email address in the IdToken as I can't validate with bearer strategy against access token. I need some way to get that email address either through the UPN or directly.
I have tried 4 different accounts under different AD groups and so far only for 1 AD group it has actually showed an 'email' OR 'upn' field in the idToken.
Permissions configured in the app screenshot.
The options of the login request:
private loginRequest: Msal.AuthenticationParameters = {
scopes: ['user.read', 'email', 'profile', 'openid'],
extraScopesToConsent: ['email']
};
I have tried any variation I can think of, I tried having them starting with capital leters (Email, User.Read) like in the screenshot, I've tried swapping them around scopes & extraScopesToConsent, I've tried putting 'upn' anywhere but no luck.
Would it be possible to either:
A: Get that data somehow in the token
B: Get another token I can validate as 'bearer token' with my backend that has that data?
I ended up sending the email in the req body from frontend, then updated my passport strategy to use the request object.

authentication header vs query parameter in google cloud endpoints

I have tried everything, yet I cannot access my API using google cloud endpoints using a Authentication:Bearer header. According to Cloud Endpoints Docs:
When you send a request using an authentication token, for security reasons, we recommend that you put the token in the Authorization:Bearer header.
it also says:
If you cannot use the header when sending the request, you can put the authentication token in a query parameter called access_token.
I can perfectly access the API using access_token=" +idToken in my URL. However, when I try to send an HTTP request with the Authentication header like this:
const url =
"https://<PROJECTNAME>.appspot.com/getbalance";
axios
.get(url,{headers:{'Authentication':'Bearer '+idToken}})
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
I get this error:
JWT validation failed: Missing or invalid credentials
Is sending the token in a query parameter as safe as sending it in the header?
Your code example shows you setting an Authentication header, not an Authorization header. You should not typically use a query parameter as it will likely get logged in Cloud Console.
When using "Authorization: Bearer ", you would need to use an access token obtained through OAuth 2.0 authentication.
This can be illustrated if you use the Oauth Playground agains any of the Google APIs.
Keep in mind that if you want to access your Firebase database using the Oauth Playground, you would need to configure the client ID and client Secret of your Firebase project on the gear icon at the top right of the playground screen.
Also make sure to use these scopes:
https://www.googleapis.com/auth/userinfo.email
https://www.googleapis.com/auth/firebase.database
After completing all the steps, you will be able to make a REST request using the authorization header with the obtained access token.

Resources