I'm developing an app using Firebase Auth and Firestore.
I have the current code to create a game document:
export const createGame = async () => {
const title = makeid(6);
const myDoc = doc(db, "rides", title);
const payload = {
createdBy: auth.currentUser.uid
};
console.log(title, payload, auth.currentUser);
await setDoc(myDoc, payload);
return title;
};
This results in the following being printed to the console:
5YTm0R {createdBy: 'pLCzrgwSQSa9KxaW5OlU2l18CGY2'} UserImpl {providerId: 'firebase', proactiveRefresh: ProactiveRefresh, reloadUserInfo: {…}, reloadListener: null, uid: 'pLCzrgwSQSa9KxaW5OlU2l18CGY2', …}
As you can see from the log, the current user exists. It is an anonymous user, so isAnonymous is true when you expand the object.
However, the request fails, and when I look at the emulator's console, I see the following image:
The current user is being shown as null in the Firebase Emulator console whereas it is non-null in the application.
I'm wondering if there's a particular set of steps I need to take for Firestore to use the current user's authentication when making a request? Thanks!
Posting as community wiki:
As per #mikesol, the issue was resolved by upgrading to the newest Firebase version.
npm i firebase#latest
Related
Getting data from firestore emulator works fine, manually added documents.
export const getMaterials = async (companyID: string): Promise<Material[]> => {
const materials = await getDocs(collection(db,
`${COMPANIES}/${companyID}/materials`))
return materials.docs.map<Material>((doc) => <Material>{ id: doc.id, ...doc.data() })
}
Saving also return with success but the data is not shown in the emulator UI but return from the above function the records from the emulator and the newly added records.
The data disappear after refreshing the app and the emulator.
export const setMaterial = async (
companyID: string,
id: string
): Promise<any> => {
const docData = {
companyID: companyID,
id: id,
name: name
}
const ret = await addDoc(collection(db, `${COMPANIES}/${companyID}/materials`),
docData)
return ret
}
Deleting documents from the emulator keep on returning from the getMaterial function. Again until restarting the app and the emulator.
Any idea where these guest documents is saved and why it's not saving to the emulator.
related: Firebase Firestore Emulator UI is not showing any data, but data is being returned in app query
const config = {
apiKey: 'AIz....3Jk1',
projectId: 'local',
authDomain: 'app.localhost.dev',
}
Changing projectId from local to the current project getting from the command
firebase projects:list
Solved the problem
I've come across the same issue. Mine happened to be similar yet a bit different problem.
THE ISSUE
My app was part of project-1 and Firestore part of project-2.
My dumb mistake was trying to initialize Firestore - getFirestore() - with configuration for project-1 a connected it to emulator - connectFirestoreEmulator. That led to the same issues described by OP.
TL;DR
Check twice, if you are connecting to the right Firestore via getFirestore().
I couldn't find a similar question so I will ask here:
I'm using the Realm library and the react-native SDK to build an app.
I started by running the example app https://github.com/mongodb-university/realm-tutorial-react-native
I can start the app in xcode, login, and register, and even add a task, but every time I try to get the current user that is signed to the app it gives me an empty object.
For example I have tried to log the user (code is taken from the repo mentioned above - Auth Provider):
const signIn = async (email, password) => {
const creds = Realm.Credentials.emailPassword(email, password);
const newUser = await app.logIn(creds);
console.log('====', { newUser, creds, app: app.currentUser });
setUser(newUser);
};
The log I get is: LOG ==== {"app": {}, "creds": {}, "newUser": {}}
As I mentioned the app is WORKING and I can see the users and tasks added OK on the Realm UI app. What am I missing? How can I get the user?
(I would like to save it in a secured storage and in the app state to change the screens depending on the auth state of the user)
I'm trying to use firebase-admin sdk to update my users passwords manually, the idea is to use a onCreate trigger to achieve this by creating a new document in firestore (with the right rules obviously).
According to firebase documentation i don't need to use anything else than this to autenticate from my firebase functions environment:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
In order to test the function i just manually added the document right from the firebase console ui for firestore, and as i can see the trigger is just doing it's work, the problem is when updatin the user password using the firebase-admin sdk.
I'm getting the next error message from the logs:
Error updating user: { 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
this is the whole firebase cloud function if you want to see how it's implemented:
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
triggerNewDocument();
function triggerNewDocument() {
exports.updateUserData = functions.firestore
.document('updateUserPasswords/{userId}')
.onCreate((snap, context) => {
// Get an object representing the document
// e.g. {'name': 'Marie', 'age': 66}
const newValue = snap.data();
console.log(newValue);
// access a particular field as you would any JS property
const uid = newValue.uid;
const newPassword = newValue.password;
return updateUserPassword(uid, newPassword);
// perform desired operations ...
});
}
function updateUserPassword(uid, newPassword) {
admin.auth().updateUser(uid, {
password: newPassword,
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
return userRecord.toJSON();
})
.catch(function(error) {
return error;
});
}
Is there anything may i be missing here?, thanks in advance for any hint or help you could provide.
The whole issue was somehow the service account autogenerated with the admin-sdk was inactive.
Anyway i had to disable and enable the service account at least 3 times to make it work, hopefully this can be helpful for anyone having the same issue.
A simple mistake
I'm developing a Flutter app that uses Firebase Auth to handle authentication. However, some sections of the app use a WebView that shows content from the web version (which also uses Firebase Auth). My question is to how ensure that users that have signed in to the app are also signed in within the WebView.
There's nothing built into Firebase to automatically synchronize the authentication state from native code into a web view that is opened from this native code.
It should be possible to pass the ID token from the native code to the web view and use it there, but I've never tried that myself.
Some relevant links that I found:
How to pass Firebase Auth token to webView and register for notifications on Android (describes the same problem, but then with Android - and unfortunately without an answer)
Is there a way to keep the user signed in between native code and a WebView using Firebase Auth on Android? (unfortunately also without an answer)
Webviews and social authentication with React Native (blog post describing a workaround for this type of problem with Facebook login and react native)
How to do Authentication on native and pass to webView? (also with React Native, but this answer looks promising)
capacitor-firebase-auth npm module (plugin for Capacitor framework that propagates the token from native code to web view)
None of these are pre-built solutions for Flutter + WebView, but I hope that combined they allow you to build something yourself. If you do: please share it! :)
Here is solution for Firebase Auth with WebView in React Native:
import React from 'react'
import WebView from 'react-native-webview'
export default function HomeScreen(props) {
// props.user represents firebase user
const apiKey = props.user.toJSON().apiKey
const authJS = `
if (!("indexedDB" in window)) {
alert("This browser doesn't support IndexedDB")
} else {
let indexdb = window.indexedDB.open('firebaseLocalStorageDb', 1)
indexdb.onsuccess = function() {
let db = indexdb.result
let transaction = db.transaction('firebaseLocalStorage', 'readwrite')
let storage = transaction.objectStore('firebaseLocalStorage')
const request = storage.put({
fbase_key: "firebase:authUser:${apiKey}:[DEFAULT]",
value: ${JSON.stringify(props.user.toJSON())}
});
}
}
`
return <WebView
injectedJavaScriptBeforeContentLoaded={authJS}
source={{
uri: 'http://192.168.1.102:3000',
baseUrl: 'http://192.168.1.102:3000',
}}
/>
}
Similar logic might be required in Flutter (JS injection).
High Level
From Flutter mobile client, sign in to Firebase
Generate a unique Firestore document for the logged in user, setting whatever auth data you need to lookup via calls from the webView - eg, uid, email, etc
Pass that doc.id to the webView, and use that token value as a parameter for cloud functions being called from the webView, that require the logged-in user data
Code
Implementation requires 5 small JS blocks between Firebase cloud and the browser:
From Flutter mobile client, call cloud function to give you a unique token, where token will be a doc ID and data will have Auth User uid:
exports.getWebAppUserToken = functions.https.onCall(async (data, context) => {
let docRef = await firestore.collection('webTokens')
.add({uid : context.auth['uid']});
return {'webToken' : docRef.id};
});
Pass the token into the url called to open the webview, eg: http://app.com/appPage/<token>, and then extract token in browser:
getValidationToken() {
let href = window.location.href;
let lastIdx = href.lastIndexOf('/');
return href.substr(lastIdx + 1).trim();
}
Now from the browser you can call a cloud function using the token:
const authFuncCalledFromWeb =
firebase.functions().httpsCallable('authFuncCalledFromWeb');
const result = await authFuncCalledFromWeb(uiValidationToken);
Cloud function that uses the webToken to get uid for the request:
exports.authFuncCalledFromWeb = functions.https.onCall(async (data, context) => {
let webToken = data;
let uid = await getWebTokenUid(webToken);
// >>> do stuff that requires uid
});
Helper to lookup webToken:
getWebTokenUid = async function (webToken) {
let webTokenDoc = await firestore.collection(appData.Collctn.webTokens)
.doc(webToken).get();
let webTokenDocData = webTokenDoc.data();
return webTokenDocData['uid'];
}
=================
Here's a variation if you want to consider expiring the token:
<!-- begin snippet: js hide: true -->
let EXPIRES_INTERVAL = 1000 * 60 * 20;
exports.getWebAppUserToken = functions.https.onCall(async (data, context) => {
logr.enter(`getWebAppUserToken`);
const uid = appConfig.getLoggedInUid(context);
logr.i(`uid: ${uid}`);
// For Field.expires, consider that webToken will not be
// looked up until user clicks HTML submit action.
// So whatever interval we give, we should check in client
// On the other hand, user can only get this token through
// the app in a cloud func, so expires may not be nec.
let expiresTimestamp = dateUtil.getNowNumericTimestamp() + EXPIRES_INTERVAL;
let webTokenProfile = {
[Field._created] : dateUtil.getNowReadableTimestampPST(),
[Field.expires] : expiresTimestamp,
[Field.uid] : uid,
}
let docRef = await firestore.collection('webTokens')
.add(webTokenProfile);
let webToken = docRef.id;
return {'data' : webToken};
});
Normally, I'd use the firebase.auth.OAuthProvider('microsoft.com') and pass it to firebase.auth().signInWithPopup() but in my case I need to log into Microsoft first using their library and pass the credentials back to firebase. (why? because I need their accessToken/refreshToken handling which isn't done by firebase).
Anyway, I can't seem to find a way to take the microsoft-supplied credentials and pass them successfully back to firebase...
here's what's not working for me:
export async function msLogin() {
const userAgent = new MSAL.UserAgentApplication({ auth: { clientId: CLIENT_ID, } });
const auth = await userAgent.loginPopup({ scopes: ['profile'] });
const provider = new firebase.auth.OAuthProvider('microsoft.com');
const idToken = auth.idToken.rawIdToken;
const credential = provider.credential(idToken);
await firebase.auth().signInWithCredential(credential); // **** THIS FAILS
// ... with "Invalid IdP response/credential: http://localhost?id_token=eyJ0eXAiO...0e9BeTObQ&providerId=microsoft.com"
The microsoft popup part works great and I'm logged in successfully there and able to issue API calls. But I can't get firebase to accept those credentials to log in on firebase. I've enabled the Microsoft OAuth provider in the Firebase Authentication panel and I'm sure the CLIENT_ID's match...
Maybe I'm constructing the OAuthCredential wrong?
Help greatly appreciated!
ps: this is a continuation of this SO post.