I am attempting to delete a sub collection when the parent document is deleted.
I have created the following cloud function to do so:
import { firestore, logger } from 'firebase-functions';
import { recursiveDelete } from '../utils/db';
export const deleteListItems = firestore.document('owners/{owner}/lists/{list}').onDelete(snap => {
const collPath = snap.ref.collection('items').path;
logger.info('Why do you hate me?');
logger.debug({ collPath });
return recursiveDelete(collPath);
});
---------
db.ts
---------------------------------
import * as functions from 'firebase-functions';
const firebase_tools = require('firebase-tools');
export const recursiveDelete = (path: string): Promise<any> => {
console.debug({ project: process.env.GCLOUD_PROJECT });
console.debug({ token: functions.config().fb.token });
return firebase_tools.firestore.delete(path, {
project: process.env.GCLOUD_PROJECT,
recursive: true,
yes: true,
token: functions.config().fb.token,
});
};
From my logging, I can see that the path I'm trying to delete is correct, owners/G-470728052545159171/lists/anime/items. I can also see that the project and token values are present.
I am, however, seeing the following error in the cloud functions logs:
deleteListItems
FirebaseError: Deletion failed. Errors: Failed to fetch documents to delete >= 3 times..
at Timeout.<anonymous> (/workspace/node_modules/firebase-tools/lib/firestore/delete.js:272:32)
at listOnTimeout (internal/timers.js:554:17)
at processTimers (internal/timers.js:497:7)
I ideally want the delete to be performed at the list doc level, but I thought that, that doc being deleted already might have been causing the error, which clearly it isn't.
I tried running the equivalent command in my console:
firebase firestore:delete -r -y owners/G-470728052545159171/lists/anime/items
This works as expected, even if the list doc (anime), is already deleted, so I'm not understanding why the one in the function isn't working.
EDIT: Set DEBUG env var
Debug output:
2:05:30.175 PM
deleteListItems
[2021-04-17T18:05:30.171Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
2:05:30.176 PM
deleteListItems
[2021-04-17T18:05:30.175Z] > authorizing via --token option
2:05:30.176 PM
deleteListItems
[2021-04-17T18:05:30.176Z] [iam] checking project watchlist-bot for permissions ["datastore.entities.delete","datastore.entities.list","firebase.projects.get"]
2:05:30.246 PM
deleteListItems
[2021-04-17T18:05:30.244Z] > refreshing access token with scopes: ["email","https://www.googleapis.com/auth/cloud-platform","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","openid"]
2:05:30.246 PM
deleteListItems
[2021-04-17T18:05:30.245Z] >>> HTTP REQUEST POST https://www.googleapis.com/oauth2/v3/token
2:05:30.246 PM
deleteListItems
<request body omitted>
2:05:30.478 PM
deleteListItems
[2021-04-17T18:05:30.477Z] <<< HTTP RESPONSE 400 {"cache-control":"no-cache, no-store, max-age=0, must-revalidate","date":"Sat, 17 Apr 2021 18:05:30 GMT","pragma":"no-cache","expires":"Mon, 01 Jan 1990 00:00:00 GMT","content-type":"application/json; charset=utf-8","vary":"X-Origin, Referer, Origin,Accept-Encoding","server":"scaffolding on HTTPServer2","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","alt-svc":"h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
2:05:30.581 PM
deleteListItems
[2021-04-17T18:05:30.581Z] <<< HTTP RESPONSE 401 {"www-authenticate":"Bearer realm=\"https://accounts.google.com/\", error=\"invalid_token\"","vary":"X-Origin, Referer, Origin,Accept-Encoding","content-type":"application/json; charset=UTF-8","date":"Sat, 17 Apr 2021 18:05:30 GMT","server":"ESF","cache-control":"private","x-xss-protection":"0","x-frame-options":"SAMEORIGIN","x-content-type-options":"nosniff","server-timing":"gfet4t7; dur=4","alt-svc":"h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"","accept-ranges":"none","transfer-encoding":"chunked"}
2:05:30.581 PM
deleteListItems
[2021-04-17T18:05:30.581Z] <<< HTTP RESPONSE BODY {"error":{"code":401,"message":"Request had invalid authentication credentials. 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"}}
2:05:30.583 PM
deleteListItems
[2021-04-17T18:05:30.583Z] [iam] error while checking permissions, command may fail: FirebaseError: HTTP Error: 401, Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
It seems there is an issue with my token, which I suspected, but I'm not sure why. I generated the token using firebase login:ci as they specified.
Then I set it using firebase functions:config:set fb.token=***.
As you can see in your log messages, your current token generated by login:ci is incorrectly missing the "https://www.googleapis.com/auth/cloud-platform" scope. Try updating your firebase-tools instance that you used to create the token and try generating a fresh token.
Regarding why this same error doesn't appear on your local system: On your system, firebase-tools makes use of your full account credential (to deploy code, etc) rather than a credential with targeted scopes.
Related
I am building a Rest API using Node JS as backend. My API needs to fetch the data from the Microsoft Teams. To do that, I am first trying to generate the access token following this link, https://spin.atomicobject.com/2021/10/08/microsoft-graph-api-node/. But it keeps returning 400 errors.
First I logged into Azure and created an app under App Registrations. Then I created an client secret for the app and also set the API permissions as follow.
In the Node JS backend, I am trying to generate the access token using the code below.
const msClientId = `xxx`;
const msTenantId = `xxx`;
const msClientSecret = `xxx`
async function generateAccessToken () {
try {
const msalConfig = {
auth: {
clientId: msClientId,
clientSecret: msClientSecret,
authority: `https://login.microsoftonline.com/${msTenantId}`,
}
}
const cca = new msal.ConfidentialClientApplication(msalConfig);
const authResponse = await cca.acquireTokenByClientCredential({
scopes: [ `User.Read` ]
});
return authResponse.accessToken;
} catch (e) {
return e.message;
}
}
When I run the code, it is returning the following error.
network_error: Network request failed. Please check network trace to determine root cause. | Fetch client threw: Error: HTTP status code 400 | Attempted to reach: https://login.microsoftonline.com/xxx/oauth2/v2.0/token
What is wrong with my code and how can I fix it?
Have you tried the .default scope?
Like:
scopes: ['https://graph.microsoft.com/.default']
The endpoint you are using in authority is incorrect, could you please use the API:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Ref doc: https://learn.microsoft.com/en-us/graph/auth-v2-service#token-request
I have a single userPool under which I have two client apps.
Using: amazon-cognito-identity-js, aws-sdk
Previously before (Jan 21, 2022 IST), when we signed in using one client app and then used the refresh token api to refresh the access token by passing another client app Id we were getting the error NotAuthorizedException: Invalid Refresh Token.
But since Jan 21, 2022 IST we are receiving a different error for the same scenario - NotAuthorizedException: Refresh Token has different Client.
I also tried with an updated amazon-cognito-identity-js library but the issue persists.
Node.js version: v12.18.4
SDK version number: amazon-cognito-identity-js#1.31.0, aws-sdk#2.177.0
To Reproduce (observed behavior)
Let us consider a userPoolId - UserPoolA
and two client app within the userpool - clientApp1 and clientApp2.
Login using clientApp1.
Use the refresh token received from step (1) to refresh the access token but this time pass the clientId as clientApp2
Initial Error Message: NotAuthorizedException: Invalid Refresh Token (Expected)
New Errror Message: NotAuthorizedException: Refresh Token has different Client (Unexpected, happening since January 21, 2022 IST)
Expected behavior
When we signed in using one client app and then used the refresh token api to refresh the access token by passing another client app Id we should be getting the error NotAuthorizedException: Invalid Refresh Token.
Additional context
Code Snippet:
// Let us consider a userPoolId - UserPoolA
// and two client app - clientApp1 and clientApp2.
const aws = require(aws-sdk);
const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider();
// For logging in:
const params = {
AuthFlow: "ADMIN_NO_SRP_AUTH"
ClientId: **clientApp1**
UserPoolId: **UserPoolA**
AuthParameters: {
USERNAME: username,
PASSWORD: password
}
}
cognitoidentityserviceprovider.adminInitiateAuth(params, function (err, data) { }
// For refreshing the token:
const params = {
AuthFlow: 'REFRESH_TOKEN_AUTH',
ClientId: **clientApp2** ,
UserPoolId: **UserPoolA**
AuthParameters: {
REFRESH_TOKEN: **refreshToken received from step 1 Login.**
}
}
cognitoidentityserviceprovider.adminInitiateAuth(params, function (err, data) { }
P.S: We hadn't updated any version of the aws package before we started observing this change in message
Stack:
Google Sign-in (Vanilla JS - client side),
Firebase Functions (ExpressJS)
Client-Side:
My Firebase function express app uses vanilla javascript on the client side. To authenticate I am making use of Firebase's Google SignIn feature client-side javascript web apps, found here.
// Firebase setup
var firebaseConfig = {
apiKey: "AIza...",
authDomain: "....firebaseapp.com",
databaseURL: "https://...-default-rtdb.firebaseio.com",
...
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.NONE);
function postIdTokenToSessionLogin(idToken, csrfToken) {
return axios({
url: "/user/sessionLogin", < ----- endpoint code portion found below
method: "POST",
data: {
idToken: idToken,
csrfToken: csrfToken,
},
});
}
// ...
// On sign-in click
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth()
.signInWithPopup(provider)
.then(async value => {
const idToken = value.credential.idToken;
const csrfToken = getCookie('_csrf');
return postIdTokenToSessionLogin(idToken, csrfToken);
}).then(value => {
window.location.assign("/user/dashboard")
}).catch((error) => {
alert(error.message);
});
Note I am using value.credential.idToken (most sources imply to use this, but haven't found an example saying use this specifically)
Directly after calling signInWithPopup, a new account is created in my Firebase Console Authentication matching the gmail account that was just signed in.
Server-side:
Once I authenticate, I create an axios request passing in the {user}.credential.idToken and following the server-side setup here (ignoring the CSRF - this just doesn't want to work).
In creating the session, I use the following code in my firebase functions express app, the endpoint which is router.post('/sessionLogin', (req, res) => (part of /user route prefix):
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
const idToken = req.body.idToken.toString(); // eyJhbGciOiJSUzI1NiIsImt...[936]
admin
.auth()
.createSessionCookie(idToken, {expiresIn}) < ----------- Problem line
.then((sessionCookie) => {
// Set cookie policy for session cookie.
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
}).catch((error) => {
console.error(error);
res.status(401).send('UNAUTHORIZED REQUEST!');
});
On the createSessionCookie call, I get the following error & stack trace:
Error: There is no user record corresponding to the provided identifier.
at FirebaseAuthError.FirebaseError [as constructor] (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:44:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:90:28)
at new FirebaseAuthError (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:149:16)
at Function.FirebaseAuthError.fromServerError (C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\utils\error.js:188:16)
at C:\Users\CybeX\Bootstrap Studio Projects\future-design\functions\node_modules\firebase-admin\lib\auth\auth-api-request.js:1570:49
at processTicksAndRejections (internal/process/task_queues.js:93:5)
This is part of the sign-in flow with a existing Gmail account.
What is causing this?
After many hours of searching, Googling - I have seen the light.
For some additional context, this error featured heavily in my struggle "Firebase ID token has invalid signature." - I will get to that in a second.
Further, another issue I also faced was using a local auth emulator for web client-side (javascript), see this for setup.
TL;DR to solve the immediate problem
Client-side remained largely the same, however the documentation provided by Firebase was inaccurate/misleading - thanks to this post, I found the solution. Thus, it follows...
Which is the ID Token? (Client-side):
The examples from here (to allow signInWithPopup), the response (if successful) results in
...
.signInWithPopup(provider)
.then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
Looking for an idToken, I found one using result.credential.idToken but no where on the internet on if this was infact the correct token to use.
I ran into this error using the provided idToken above:
Firebase ID token has incorrect "aud" (audience) claim. Expected
"[insert your **projectId**]" but got
"59895519979-2l78aklb7cdqlth0eob751mdm67kt301.apps.googleusercontent.com".
Make sure the ID token comes from the same Firebase project as the
service account used to authenticate this SDK.
Trying other tokens like result.credential.accessToken responded with various verification errors - what to do?
Mention earlier, this solution on Github suggested to use firebase.auth().currentUser.getIdToken() AFTER you have signed in. An example (building on my previous code) is to do the following:
...
.signInWithPopup(provider)
.then((result) => {
// current user is now valid and not null
firebase.auth().currentUser.getIdToken().then(idToken => {
// send this ID token to your server
const csrfToken = getCookie('_csrf');
return postIdTokenToSessionLogin(idToken, csrfToken);
})
})
At this point, you can verify your token and createSessionCookies to your heart's desire.
BUT, a secondary issue I unknowingly created for myself using the Authentication Emulator.
To setup for client-side use:
var auth = firebase.auth();
auth.useEmulator("http://localhost:9099");
To setup for hosting your firebase functions app (assuming you are using this with e.g. nodejs + express, see this for setup, ask in comments, can provide more details if needed)
Using Authentication Emulator caused the following errors AFTER using the above mentioned "fix". Thus, DO NOT RUN the local authentication emulator (with Google sign-in of a valid Google account) as you will consistently get.
Firebase ID token has invalid signature. See
https://firebase.google.com/docs/auth/admin/verify-id-tokens for
details on how to retrieve an ID token
You can use all your local emulators, but (so far in my experience) you will need to use an online authenticator.
const admin = require('firebase-admin');
require('dotenv').config()
// const serviceAccount = require("../service-account-file.json");
const check = process.env.GOOGLE_APPLICATION_CREDENTIALS;
//GOOGLE_APPLICATION_CREDENTIALS="/home/byordani93/tuDiagram/service-account-file.json"
console.log(check, 'mate')
admin.initializeApp({
projectId: 'tudiagram',
credential: admin.credential.applicationDefault(check),
databaseURL: 'https://tudiagram.firebaseio.com'
});
module.exports = admin;
then msg
> (node:3218) UnhandledPromiseRejectionWarning: Error: Credential
> implementation provided to initializeApp() via the "credential"
> property failed to fetch a valid Google OAuth2 access token with the
> following error: "Error fetching access token: Error while making
> request: The "options.agent" property must be one of type Agent-like
> Object, undefined, or false. Received type string. Error code:
> ERR_INVALID_ARG_TYPE".
>
I tried making the env value a string and not a string as well as
checking to see if the process.env was being detected and still
nothing not sure at this point I have googled to no avail please give
some pointers or tips **hint hint maybe a solution haha :)
Try this one
import * as firebase from 'firebase-admin';
const serviceAccount = require("../../serviceAccountKey.json")
firebase.initializeApp({
credential: firebase.credential.cert(serviceAccount),
databaseURL: 'https://xxxxxx.firebaseio.com'
});
changing the applicationDefault to cert
Our team also get similar message error.
So, we just fix as below:
Remove function in dashboard firebase.
Install Node.js
Install firebase-functions, firebase-admin, firebase-tools
Ref: https://firebase.google.com/docs/functions/get-started
Config dependencies in package.json, config the same version firebase-admin, firebase-functions as version you can see in step 3.
Remove yarn.lock
Deploy function.
I currently cannot read or write data using firebase-admin in nodejs.
Without logging turned on it just hangs forever and never returns an error and never completes.
With logging turned on however it gives the following (it continues to retry on a decaying schedule - but with the same result):
p:0: Browser went online.
0: set {"path":"/server/saving-data/fireblog/users","value":{"alanisawesome":{"date_of_birth":"June 23, 1912","full_name":"Alan Turing"},"gracehop":{"date_of_birth":"December 9, 1906","full_name":"Grace Hopper"}},"zg":null}
p:0: Buffering put: /server/saving-data/fireblog/users
p:0: Making a connection attempt
p:0: Failed to get token: Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "unable to verify the first certificate".
p:0: data client disconnected
p:0: Trying to reconnect in 0ms
0: onDisconnectEvents
When I catch the (internal to firebase-admin) https request I get the following:
{ Error: unable to verify the first certificate
at TLSSocket.<anonymous> (_tls_wrap.js:1094:38)
at emitNone (events.js:86:13)
at TLSSocket.emit (events.js:188:7)
at TLSSocket._finishInit (_tls_wrap.js:616:8)
at TLSWrap.ssl.onhandshakedone (_tls_wrap.js:446:38) code: 'UNABLE_TO_VERIFY_LEAF_SIGNATURE' }
I have tried generating a new private key from the console but it continues to return the same error. I have also added the require('ssl-root-cas').inject() as suggested elsewhere but it doesn't change the behavior. Also passing the actual json object to the admin.credential.cert function does not change the behavior.
I am currently on node v7.7.4.
Here is the code I have that produces the error:
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.cert('./serviceAccountKey.json'),
databaseURL: 'https://mtg-decks-bc92d.firebaseio.com'
});
// turn on logging
admin.database.enableLogging(true);
const db = admin.database();
const ref = db.ref('server/saving-data/fireblog');
const usersRef = ref.child('users');
usersRef.set({
alanisawesome: {
date_of_birth: 'June 23, 1912',
full_name: 'Alan Turing'
},
gracehop: {
date_of_birth: 'December 9, 1906',
full_name: 'Grace Hopper'
}
}).then(() => {
console.log('set complete');
});
Several other posts recommend adding the root certificates by calling require('ssl-root-cas').inject();. Can you try that?
Example: Error: unable to verify the first certificate in nodejs
I got this error because my local machines time zone was set manually so the token expiration etc was off.