Authenticating function to function calls for firebase functions on server side - node.js

We are using firebase cloud functions in our application. We have 2 firebase functions: 1. A regular firebase cloud function that runs at regular intervals, 2. A firebase http function that is invoked by function #1 and can also be invoked from outside the application environment (in future). We have a use case where we have #1 firebase cloud function processes thousands of user records and performs some specific operations by invoking the #2 firebase http function. Our requirement now is to authenticate the invocation of the firebase http function. Both the firebase functions are in the same service account.
Following the steps in the documentation : https://cloud.google.com/functions/docs/securing/authenticating#authenticating_function_to_function_calls we tried to create an Id token in the calling function and included the ID token in an Authorization: Bearer ID_TOKEN header in the request to the http function.
Our code to obtain the id token. We tried using the firebase receiving function url, our firebase project url
const { URL } = require('url')
const targetAudience = new URL(url)
const client = await auth.getIdTokenClient(targetAudience)
const idToken = await client.request({ url })
Our code to verify the token
const tokenId = request.get('Authorization').split('Bearer ')[1]
const decodedIdToken = await admin.auth().verifyIdToken(tokenId)
We get back an idToken correctly, but when that token is verified, we always get this error :
Error while verifying Firebase ID token: { Error: Firebase ID token has incorrect "aud" (audience) claim. Expected "<firebase-project-name>" but got "https://<cloud-function-endpoint>". 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.
const targetAudience = new URL(url) - for the target audience we tried the firebase function URL of the http function, the name of the project, adding https to the name of the project, but we always keep getting the same error Firebase ID token has an incorrect "aud" claim
Any pointers on what we could we doing wrong? Has anyone tried to authenticate invocation to one cloud function when the calling function is another cloud function in the same service account.

try to generate the idToken this way
const serviceAccountEmail = '...';
const generateIdToken = async () => {
const tokens = await admin.options.credential?.getAccessToken();
const projectId =
const res = await fetch(`https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${serviceAccountEmail}:generateIdToken`, {
method: 'POST',
headers: {
authorization: `Bearer ${tokens?.access_token}`,
'content-type': 'application/json',
},
body: JSON.stringify({
delegates: [
],
audience: projectId,
includeEmail: true
}),
});
return res.json();
}
Roles needed on the service account
Service Account Token Creator
Service Account User
Google APIs enable on the project
IAM Service Account Credentials API

it seems the admin.auth().verifyToken is expecting the audience to be the projectId instead of the url of the function endpoint.
normally what I do when I want a function to be private(only invoke from other functions) I make it "require authentication(Manage authorized users with Cloud IAM.)" then I don't even have to verify the id token in my implementation.

Related

The account does not exist in this organization error during Microsoft SSO

I am trying to do a microsoft sso, where any user can use his microsoft account to sign in to the app. Now, if the user is part of the application users it works perfectly fine, but when I try to log in with my personal one, it gives me an error message saying "The account does not exist in this organization".
My endpoint for the sso:
const REDIRECT_URI = "http://localhost:3000/redirect";
const cca = new msal.ConfidentialClientApplication(config);
const scopes = ["user.read"];
router.get("/auth", (req, res) => {
// Construct a request object for auth code
const authCodeUrlParameters = {
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Request auth code, then redirect
cca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
return res.send(response);
})
.catch((error) => res.send(error));
});
router.get("/redirect", (req, res) => {
// Use the auth code in redirect request to construct
// a token request object
const tokenRequest = {
code: `${req.query.code}`,
scopes: scopes,
redirectUri: REDIRECT_URI,
};
// Exchange the auth code for tokens
cca
.acquireTokenByCode(tokenRequest)
.then(async (response) => {
res.send(response);
})
.catch((error) => res.status(500).send(error));
});
It gives me an error message saying, "The account does not exist in this organization".
The error message shows that your personal Microsoft account is not associated with the organization that the application is using for authentication. Microsoft provides two different authentication services: Microsoft Account (MSA) and Azure Active Directory (AAD).
By using Guest method, you can use your personal account by assigning the roles.
If your organization has set up the application to use AAD for authentication, then you need to have an AAD account in the same organization in order to log in.
Create an AAD account and map it with the same email address as your MSA. This will allow you to log into the application using your AAD account.
And if you don't want to create an AAD account, you can use a different email address (such as a work email address) that is already associated with the organization's AAD.
Steps to create Microsoft SSO
Register your application to access Microsoft Graph API, and you need to first register your application in the Microsoft Developer Portal and obtain a client ID and secret.
The first step in the authorization code grant flow is to redirect the user to the Microsoft authorization endpoint. The user must sign in and grant the permissions that your application requests.
After the user grants permission, Microsoft will redirect the user back to your application with an authorization code.
Your application can then exchange this code for an access token by making a POST request to the Microsoft token endpoint.
Once you have an access token, you can use it to call the Microsoft Graph API on behalf of the user. To do this, you'll need to add the access token to the Authorization header of your API request.
Access tokens are short-lived and need to be refreshed after a certain time period. You can use the refresh token to obtain a new access token without requiring the user to sign in again.
async function getAccessToken(code) {
const tokenRequestBody = querystring.stringify({
grant_type: 'authorization_code',
code: code,
redirect_uri: redirectUri,
client_id: clientId,
client_secret: clientSecret,
});
const tokenResponse = await axios.post('https://login.microsoftonline.com/common/oauth2/v2.0/token', tokenRequestBody);
return tokenResponse.data.access_token;
}
async function getUserProfile(accessToken) {
const userProfileResponse = await axios.get('https://graph.microsoft.com/v1.0/me', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return userProfileResponse.data;
}
async function main() {
const code = 'AUTHORIZATION_CODE';
const accessToken = await getAccessToken(code);
const userProfile = await getUserProfile(accessToken);
console.log(userProfile);
}
References taken from
Nodejs-webapp-msal
ADD

Can I call Google cloud run with id token client?

My backend service is deployed on cloud run.
It can only access by authorized request with IAM service account.
So i use google-auth-libray to request from frontend.
const client = await auth.getIdTokenClient(process.env.NEXT_PUBLIC_CALL_URL!);
const data = await client.request({
url: process.env.NEXT_PUBLIC_CALL_URL!,
});
this is my source.
But I want call with my login token.
So i attempt like this.
const client = await auth.getIdTokenClient(process.env.NEXT_PUBLIC_CALL_URL!);
const header = await client.getRequestHeaders();
const data = await axios.get(process.env.NEXT_PUBLIC_CALL_URL, {
header: {
Authorization: `${header}, Bearer mytoken`
}
})
But It's not working.
Can i call request from front-end like this?

How to verify accessToken in node/express using aws-amplify?

I am using AWS amplify for user authentication on my front-end React app.
My React app directly communicates with amplify without any backend(node server) interaction.
I have a REST API written in node/express. I want to secure that API using amplify.
Currently, I am planning to pass the access token from my react app to my node server. But I am unable to find a way through which I can verify this token on the backend using amplify.
Does aws-amplify package provide any function in which I can pass the access token to verify it?
Something like Auth.verifyToken(<access_token>)
Unfortunately, there is no such method available in official aws-amplify SDK.
After doing a lot of research I had to write my own middleware for that. This is not that difficult as it may seem but the only difficult part is to gather the right information from the huge AWS documentation.
I 've written this middleware to achieve the same, Hope this helps
import axios from 'axios'
import awsconfig from '../../aws-exports';
const COGNITO_URL = `https://cognito-idp.${awsconfig.aws_project_region}.amazonaws.com/`;
const authentication = async (req, res, next) => {
try {
const accessToken = req.headers.authorization.split(" ")[1];
const { data } = await axios.post(
COGNITO_URL,
{
AccessToken: accessToken
},
{
headers: {
"Content-Type": "application/x-amz-json-1.1",
"X-Amz-Target": "AWSCognitoIdentityProviderService.GetUser"
}
}
)
req.user = data;
next();
} catch (error) {
return res.status(401).json({
message: 'Auth failed'
});
}
};
export default authentication;
This middleware takes the authorization header and verifies the incoming accessToken using AWS Cognito REST API.
In order to get accessToken on your front-end you can do something like this:
const { accessToken: { jwtToken } } = await Auth.currentSession();
This jwtToken is your accessToken you can send this in your Authorization header and then verify this in the backend using the middleware I've written.
AWS actually has this documented pretty well. I have written a gist on a middleware I wrote to validate AWS Cognito tokens in my express.js server.
Essentially, when you create a User Pool in Amazon, AWS creates a JSON Web Key (JWK). The JWT contains a public key you can use to verify the signature of a JWT.
At a high level in Javascript:
import jwt from "jsonwebtoken";
const authToken = getJwtFromHeader(request);
// please remember to verify the "iss" claim and "exp" claim!
validateAuthToken(authToken);
// convert a jwk to a PEM for use by OpenSSL or crypto
const jwk = getJwkFromAWS();
const pem = jwkToPem(jwk);
// verify the signature of the token using the public key from AWS
await jwt.verify(authToken, pem, {algorithms: ['RS256']}, (err, decoded) =>{
console.log('decoded', decoded);
// TODO -- verify claims from decoded token
});
My GIST for a full express.js implementation:
https://gist.github.com/fourgates/92dc769468497863168417c3524e24dd
AWS Resources:
https://github.com/awslabs/aws-support-tools/tree/master/Cognito/decode-verify-jwt
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
Unfortunately, the aws-amplify SDK doesn't provide that functionality. For that reason, I've created an npm package to handle it.
How it works
The package exposes:
authenticate: An Express middleware that can be added to any route that needs to be authenticated against a Cognito User Pool.
authenticationError: An Express error handler that takes care of any authentication errors generated by the authenticate middleware.
JWTValidator: A class that can be instantiated to validate JWTs issued by Cognito. This is useful if you need a custom authentication and error handling logic. The authenticate middleware uses it internally.
Features included
JWT signature verification.
JWT claims verification.
Verify that the token is not expired.
Verify that the audience (aud) claim matches one of the valid
audiences provided in the configuration.
Verify that the issuer (iss) claim is valid for the configured user
pool.
Verify that the token_use claim matches one of the valid token uses
provided in the configuration.
Support for JWKs rotation as per described in the JWT signing key
rotation thread.
Ability to set custom pems for local testing without the need of
creating a User Pool.
Basic usage
// app.js
'use strict';
const express = require('express');
const { authenticate, authenticationError } = require('aws-cognito-express');
const app = express();
// Add the authentication middleware.
app.use(authenticate({
region: 'us-east-2',
userPoolId: 'us-east-2_6IfDT7ZUq',
tokenUse: ['id', 'access'],
audience: ['55plsi2cl0o267lfusmgaf67pf']
}));
// Protected route.
app.get('/articles', (req, res, next) => {
console.log('JWT payload: ', req.cognito);
});
// Add the authentication error handler.
app.use(authenticationError());
module.exports = app;
For more advanced use cases, please check the docs here.
Since 17 September 2021, we now have this repository (and associated npm package):
https://github.com/awslabs/aws-jwt-verify
https://www.npmjs.com/package/aws-jwt-verify
This allows us (external node applications, usually server side web facing applications) to verify JWTs signed by AWS, such as those emitted from AWS cognito.
Specifically, as the tokens are asymmetrically signed, this verified AWS account publisher of the node package refers to the AWS published JSON Web Key Set (JWKS), promoting a degree of trust in the code we use to verify the claims contained in JWTs as they may be presented as bearer tokens to our apps.
The sample code taken from: https://github.com/awslabs/aws-jwt-verify#express (in this case it uses express) appears to be quite user-friendly as well:
import express, { Request } from 'express';
import { CognitoJwtVerifier } from 'aws-jwt-verify';
const cognitoJWTVerifier = CognitoJwtVerifier.create({
userPoolId: 'your-user-pool-id', // mandatory, can't be overridden upon calling verify
tokenUse: 'access',
clientId: 'your-client-id',
});
app.get('/authenticated/route', async (req: Request & { verifiedCognitoJWT?: any }, res, next) => {
try {
const verifiedCognitoJWT = await cognitoJWTVerifier.verify(`${req.header('authorization')}`);
req.verifiedCognitoJWT = verifiedCognitoJWT;
next();
} catch (e) {
return res.status(401).json({ statusCode: 401, message: 'Unauthorized' });
}
}, getAuthenticatedRoute);
Note that you can optionally call hydrate() to pre-fetch the JWKS before your app begins listening for HTTP connections, but omitting this call just delays the first call to verify() which lazily fetches them.
And of course this example can be extended with proper typings, or built into a middleware etc. as required.

How to get users from azure active directory to azure function

I am developing a server less application using azure functions using Node js. Function app auth via azure active directory. My scenario is I want to get particular user data ex(email,username etc) by using his email or username during execution of one of http trigger function. There are several options that I already checked (ad and activedirectory libraries. These library seems not updated and I want to know is there any way to do this by using azure's Node Js SDK?
I am seeking a solution for without JWT. because, when 'function' need to get some user information function doesn't have any token.here user data requested by the function app not the user.Hence user data only use by the function app those info not exposed to user.
for Example: We can get user data by 'querying' user table when we need to get user data inside of some application running MySQL. If we specify email we can query.
I am asking same thing by using azure active directory.
You can get the access token using client credential flow(not related to specific user) in your function.
const APP_ID = '[COPIED_APP_ID]';
const APP_SECERET = '[COPIED_APP_SECRET]';
const TOKEN_ENDPOINT ='https://login.microsoftonline.com/[COPIED_TENANT_ID]/oauth2/v2.0/token';
const MS_GRAPH_SCOPE = 'https://graph.microsoft.com/.default';
const axios = require('axios');
const qs = require('qs');
const postData = {
client_id: APP_ID,
scope: MS_GRAPH_SCOPE,
client_secret: APP_SECERET,
grant_type: 'client_credentials'
};
axios.defaults.headers.post['Content-Type'] =
'application/x-www-form-urlencoded';
let token = '';
axios
.post(TOKEN_ENDPOINT, qs.stringify(postData))
.then(response => {
console.log(response.data);
})
.catch(error => {
console.log(error);
});
Reference:
HOW TO — Get an Access Token for Microsoft Graph API using Node.JS
Then you can use this token to call Microsoft Graph API.

How to create Firebase token on server for use with unit tests?

I need to authenticate a Firebase user using node so I can test some server side methods. For each protected request, I verify the Firebase token using:
firebase.auth().verifyIdToken(firebaseAccessToken).then(function(decodedToken) {
// forward request
})
So in my test I created a token with a uid from my Firebase database
firebase.auth().createCustomToken(uid).then(function(token) {
//add to header for requests
})
Later I read that custom tokens are not verified by the verifyIdToken method, only client generated ones.
I've looked at this answer - server side verification of tokens in firebase
So I added databaseAuthVariableOverride to the init json
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: [dbURL],
databaseAuthVariableOverride: {
uid: [uid]
}
});
Still getting the output in my tests
Error: expected 200 "OK", got 401 "Unauthorized"
And the firebase error -
Error: Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
So how do I emulate a user with my current setup?
Here's a Python script for generating Firebase ID tokens (not custom tokens).
python firebase_token_generator.py <UID>
There are probably easier ways to do this but you could call the Python script from Node.
You can generate a Firebase Id token from your custom token, then use that for verification. Eg:
const rp = require("request-promise");
// 'customToken' comes from FirebaseAdmin.auth().createCustomToken(uid)
function getIdTokenFromCustomToken(customToken) {
const url = `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=${API_KEY}`;
const data = {
token: customToken,
returnSecureToken: true
};
var options = {
method: "POST",
uri: url,
body: data,
json: true // Automatically stringifies the body to JSON
};
return rp(options)
// idToken is the firebase id token that can be used with verifyIdToken
.then(parsedBody => parsedBody.idToken)
.catch(function(err) {
// POST failed...
});
}

Resources