verifyIdToken method doesn't exist in Auth type using getAuth from firebase/auth - javascript

I'm trying to follow the example given here where it shows the following example to verify an ID token:
// idToken comes from the client app
getAuth()
.verifyIdToken(idToken)
.then((decodedToken) => {
const uid = decodedToken.uid;
// ...
})
.catch((error) => {
// Handle error
});
My code looks like this:
function createFirebaseAdminApp(config: AppOptions) {
if (getApps().length === 0) {
return initializeApp(config);
} else {
return getApp();
}
}
const options: AppOptions = {
credential: cert({
projectId: process.env.FIREBASE_PROJECT_ID,
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
privateKey:
process.env.FIREBASE_PRIVATE_KEY != undefined
? process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, "\n")
: "",
}),
databaseURL: process.env.FIREBASE_DATABASE_URL,
};
const firebaseAdmin = createFirebaseAdminApp(options) as FirebaseApp;
const adminAuth = getAuth(firebaseAdmin);
adminAuth
.verifyIdToken(token)
.then((decodedToken) => {
res.locals.decodedToken = decodedToken;
next();
})
.catch(() => {
next(new HttpError("Invalid token provided", 403));
});
But I keep getting
Property 'verifyIdToken' does not exist on type 'Auth'
I have the latest version of the firebase package, which I assume the example given by the docs is using considering it uses getAuth, so can't tell what I'm doing wrong. Also I'm trying to avoid mixing firebase-admin and firebase, not sure if this is correct, but if I mixed them I can't seem to avoid having to initialize too App instances.
Thanks for any help!

You are mixing up the Admin SDK and the client SDK.
The documentation you linked is for the Admin SDK only. Notice how it is organized in the section for Admin. It is not using getAuth anywhere in that page. The Admin SDK is initialized completely differently than the client SDK, and it does not work at all in browsers. It runs on secure backens only.
The client SDK that you're using doesn't have a function for verifying tokens. That is for secure backends only using the Adminn SDK.

Related

Transaction failure while testing firebase firestore

I am trying to write some tests for the cloud functions I have written, but I am facing a problem where a transaction never succeeds and ends up in an error:
9 FAILED_PRECONDITION: the stored version (1648901730789554) does not match the required base version (0)
Problematic code is the following:
await firebase.db().runTransaction((t) => {
return t.get(docRef).then((doc) => {
if (!doc.exists) {
console.log("no exist");
}
});
});
It is run several times (checked with debugger), and then the error messages is thrown...
And the firebase test env init:
export async function initialize() {
fb.initializeTestApp({
projectId: "my-test-project",
auth: { uid: "alice", email: "alice#example.com" }
});
const testEnv = await initializeTestEnvironment({
projectId: "demo-project-1234",
firestore: {
rules: fs.readFileSync("../firestore.rules", "utf8"), // Load rules from file
// host and port can be omitted if they can be discovered from the hub.
},
});
const alice = testEnv.authenticatedContext(uid);
const db = (alice.firestore() as unknown) as firestore.Firestore;
firebase.db = () => db;
return testEnv;
}
Am I doing something wrong?
Note: I currently have only one test that runs, nothing else. And firebase emulators are running, without any other app accessing it.
After some more research, I have figured out that I was mixing #firebase/testing and firebase-admin, and the matter should not be used for unit testing backend functions according to this post.
On a side note, it seems there already is a function to create a doc iff it does not exist:
docRef.create(...).then(() => log("ok")).catch(() => log("Not ok"))

Firebase Realtime database showing strange behavior

I am using react-native-firebase in an ejected expo app and trying to build a presence detection system in my chat app which will detect that if the message recipient is online and if not when was he/she was last online. The data will be stored as follows in firebase realtime database:
{
lastSeen:[{
[userId]:{
state: boolean
time: serverTimeStamp
}
}]
}
The problem is that firebase console never shows the data and only if recipient is online then app shows this data (even though its not visible in console) but if user is offline then nothing is returned and no error generated. I have set read and write to true in realtimeDB rules. Here is the code I am using:
import database from "#react-native-firebase/database";
export const updateUserLastSeen = (userId) => {
const userStatusDatabaseRef = database().ref("/lastSeen/" + userId);
console.log("updatelast", userId);
userStatusDatabaseRef
.set({
state: true,
time: database.ServerValue.TIMESTAMP,
})
.then(() => console.log("online"))
.catch((e) => console.log(e));
// database()
// .ref(".info/connected")
// .on("value", function (snapshot) {
// if (snapshot.val() == false) {
// return;
// }
userStatusDatabaseRef
.onDisconnect()
.set({
state: false,
time: database.ServerValue.TIMESTAMP,
})
.then(function () {
console.log("disconnect configured");
// userStatusDatabaseRef.set({
// state: true,
// time: database.ServerValue.TIMESTAMP,
// });
});
// });
};
export const checkUserLastSeen = (userId, setUserLastSeen) => {
console.log("check last", userId);
database()
.ref("/lastSeen/" + userId)
.on("value", (snapshot) => {
setUserLastSeen(snapshot.val());
console.log("User data: ", snapshot.val());
});
console.log("after check last");
};
I tried both the code from firebase docs and rnfirebase docs. In above code, none of the "then" or "catch" functions get called in updateUserLastSeen but in checkUserLastSeen "on" is invoked only if bearer of userId is online. Also, I am using realtime db only for this purpose and cloud firestore for other data storing and its working fine.
Any help would be appreciated. Thanks.
If neither then nor catch of a write is called, it typically means that the client is not connected to the server.
I recommend checking to make sure your app has a network connection, and that you've configured the (correct) URL for your database.

Google Firebase authentication in ReactNative App

I was developing an app which I like implements Firebase as Authenticating system.
My problem comes when I try to set up the Authentication with Google provider when I try to modify the colletion of firestore where the users are saved. My code is the following:
export const loginWithGoogle = () => {
const navigation = useNavigation();
useEffect(() => {
setTimeout(() => {
navigation.navigate('/RegisterScreen');
}, 10000);
}, []);
return () => {
return firebase
.auth()
.signInWithPopup(Providers.google)
.then(async result => {
//console.log(result.credential.accessToken);
const user = result.user;
console.log(user);
//This 2 lines below doesn't work to get the colletion.
db.('users').setItem('userid', user!.uid);
collection.(db,'users').setItem('photoURL', user!.photoURL);
//TODO if userid exists IN USERS db then use update IF NULL use set
await db.collection('users').doc(user!.uid).update({
// id: user.uid,
name: user!.displayName,
email: user!.email,
phone: user!.phoneNumber,
photoURL: user!.photoURL,
});
})
.then(() => {
navigation.navigate('ProtectedScreen');
})
.catch(err => {
console.log(err);
});
};
};
So I guess that my error comes from unknowledge of how to manage data saved on firestore.
If you can help take thanks in advance !
There are some thing we need to clear here:
You can just merge the data. There is no need to read/get it from Firestore to check if it is there and save it onyl if it's not. You will be charged for reads and writes. In the end it's cheaper to always just write without checking if something exists.
Also this code here:
db.('users').setItem('userid', user!.uid);
collection.(db,'users').setItem('photoURL', user!.photoURL);
especially with the db.( and collection.( doens't look good. Even if it is it's not for getting data but for saving it.
Could you pls clarify witch Firebase SDK you use: version 8 or 9. Also pls check a little bit the docs here.

Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object

I'm following Firebase's instructions and my functions is as follows:
import { DataSource, DataSourceConfig } from "apollo-datasource";
import { KeyValueCache } from "apollo-server-caching";
import firebase from "firebase";
import admin from "firebase-admin";
import "#firebase/firestore";
import { Request } from "apollo-server-env";
export class FirebaseDataSource<TContext = any> extends DataSource {
context!: TContext;
db: firebase.firestore.Firestore;
constructor({
firebaseConfig,
serviceAccount,
databaseURL,
}: {
serviceAccount: any;
firebaseConfig: firebase.app.App;
databaseURL: string;
}) {
super();
this.context;
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
if (!admin.apps.length) {
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL,
});
}
if (!this.db) {
this.db = firebase.firestore();
}
}
async initialize(config: DataSourceConfig<TContext & { request: Request }>) {
this.context = config.context;
}
async user_updateRestaurantFavorites(data: {
uid: string;
someId: string;
add: boolean;
}) {
const collectionRef = this.db.collection("users");
const documentRef = collectionRef.doc(data.uid);
let favorites;
if (data.add) {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayUnion(
data.someId
),
});
} else {
favorites = await documentRef.update({
favorites: admin.firestore.FieldValue.arrayRemove(
data.someId
),
});
}
return favorites;
}
}
export default FirebaseDataSource;
I dubugged it and I do pass the uid, add, and someId correctly.
someId is a string and add is a boolean (true)
When I run this, I get:
Firestore Function DocumentReference.update() called with invalid data. Unsupported field value: a custom object (found in field favorites in document users/XXXXXXX)
I am just running their own function with a simple string.
Below is an image of my firestore showing the user record does indeed have an empty array ready to accept strings
What am I doing wrong?
You're mixing up the web client and admin client SDKs. You can't use the FieldValue objects exported by firebase-admin when calling methods exported by firebase. The error message is coming from the web client SDK, and it's effectively telling you that you passed an object that it doesn't understand (from the Admin SDK).
You should pick one or the other, and completely remove the one you aren't using in order to avoid problems. If this runs on a backend, you should only use the Firebase Admin SDK, and skip the web client altogether. If you do this, you will need to assign this.db using the admin SDK, probably as this.db = admin.firestore().
Firebase can only store primitive types, maps and array of same. In your case you are saving the result of admin.firestore.FieldValue.arrayUnion(...) for the property favorites.
My guess is that the result is not returning a supported type. I have not used FieldValue before ... is that the correct way to use the API?
It simply means that you need to send exact data which was received by the query. Partial object not allowed
db.collection("users").where("name", "==", somename).limit(1).get().then(query => {
console.log(query);
const thing = query.docs[0];
console.log(thing.data());
let tmp = thing.data();
tmp.current_game_play = tmp.current_game_play + 1;
console.log(tmp);
thing.ref.update(tmp);
});

Firebase user is authenticated, so why is uploading to storage denied?

In a react-native project, I'm using both react-native-firebase and the firebase sdk.
react-native-firebase does not allow use of firebase storage to upload image blobs, which is why im using the vanilla Firebase javascript SDK to do this part. For distinction's sake, in my code & this post, im identifying the firebase javascript sdk as 'FIREBASE', and the react-native-firebase as 'firebase'.
I had to initialize my firebase app (even though react-native-firebase doesn't require this for its function, firebase does), App.js constructor & imports:
import * as React from 'react';
import AppNavigation from './src/navigation';
import { Provider } from 'react-redux';
import { store, persistor } from './src/store/index.js';
import firebase from 'firebase/app';
import { PersistGate } from 'redux-persist/integration/react';
export default class App extends React.Component {
constructor (props) {
super(props);
const firebaseConfig = {
apiKey: '{apiKey}',
authDomain: 'project-ID.firebaseapp.com',
databaseURL: 'https://project-ID.firebaseio.com',
projectId: 'project-ID',
storageBucket: 'project-ID.appspot.com',
messagingSenderId: '9999999999'
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
}
I implement firebase and FIREBASE in an action (firebase for auth/firestore, and FIREBASE for storage):
import * as types from '../actions/types';
import RNFetchBlob from 'rn-fetch-blob';
import firebase from 'react-native-firebase';
import * as FIREBASE from 'firebase/app';
import 'firebase/storage';
import { Platform } from 'react-native';
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
export const registerUser = (registration) => {
const { email, pass, username, img } = registration;
return (dispatch) => {
dispatch({ type: types.REGISTER_USER });
console.log('starting registration process...');
// check username is unique
firebase
.firestore()
.collection('users')
.where('username', '==', username)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty !== true) {
// back to registration form
registrationFail(dispatch, 'Username already taken. Try again.');
console.log("Registrant's username already exists");
} else {
console.log('Registrants username is unique');
// continue with registration
firebase
.auth()
.createUserWithEmailAndPassword(email, pass)
.then((userCredential) => {
// successful user creation, now authenticated
// write to img storage
uploadImg(dispatch, img, userCredential.user.uid)
.then((imgUrl) => {
// on success, write to firestore
uploadImgSuccess(dispatch, 'Profile image upload successful...');
// write rest of data to firestore
firebase
.firestore()
.collection('users')
.add({
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
username: email,
uid: userCredential.user.uid,
profileImg: imgUrl,
email: email,
})
.catch((err) => {
console.log('Registration failed. Error: ' + err.message);
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => {
// Image Profile NOT Uploaded
uploadImgFail(dispatch, err);
});
})
.catch((err) => {
// unsuccessful user creeation
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => registrationFail(dispatch, err.message));
};
};
const uploadImg = async (dispatch, uri, uid, mime = 'image/png') => {
console.log('Starting image upload...');
dispatch({ type: types.UPLOAD_IMG, info: 'Uploading profile image...' });
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
// let downloadPath = '';
const imageRef = FIREBASE.storage().ref(uid).child('profileImg');
fs
.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then((url) => {
console.log('Returning Download URL: ' + url);
uploadImgSuccess(dispatch, 'Image upload successful...');
})
.catch((err) => {
uploadImgFail(dispatch, 'Image upload failed: ' + JSON.stringify(err));
});
};
but when I go through uploadImg(), I get an error:
{
"code_": "storage/unauthorized",
"message":"Firebase Storage: User does not have permission to access 'someReference/someChild',
"serverResponse":{"Code":403, "message": "permission denied."}
}
Here is Firestore rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Here is Storage rule:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
I don't understand what's going on or why. The user is authenticated during react-native-firebase's createUserWithEmailAndPassword() and is even allowed to upload data to Firestore. My only guess is this possibly has something to do either with using both firebase & FIREBASE, or with the way i've setup firebase with FIREBASE. I've used both together in a previous test project and it worked successfully with the help of a forked project rn-fetch-blob (the maintained version of react-native-fetch-blob), however I had no security rules in place bc of testing, so...
Any ideas for solving this?
Yes, you have guessed it right, your FIREBASE instance is unaware of the auth being done by the firebase since the firebase handles the native aspects and FIREBASE is just a JS thing. so both of the instances have their own lives and their own prospects with own attributes to identify the user and provide authorization.
To resolve this, try authorizing the user either by the vanilla JS SDK or use the rn-firebase for the whole task.
I would recommend using react-native-firebase, it has good support for entire firebase stack.
https://rnfirebase.io/docs/v5.x.x/storage/reference/storage
EDIT:
Vanilla JS firebase SDK should not be used in mobile applications especially when it is native since firebase will treat the client as web client and going forward you will not be able to leverage all possible mobile things from firebase such as dynamic links, push notifications and analytics.
Hope this helps! Cheers!

Categories

Resources