currentUser not work or not exist in firebase 9 - javascript

import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
const user = auth.currentUser;
user return null !!
and this not
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
console.log(user.email);
// ...
} else {
// User is signed out
// ...
}
});

What you're seeing seems like the expected behavior to me.
When you access auth.currentUser, it gives you the current user when that code runs. Firebase automatically restores the user's credentials when a page loads and this requires a call to the server. This means that when your auth.currentUser runs, that call may not have completed yet, and thus the current user is null.
When on the other hand you use onAuthStateChanged, Firebase only calls your code when it has completed the first check of the user's credentials. So now your code runs after the user has been signed in, and user is no longer null.
This is one of the main reasons it's almost always better to use an auth state listener.

You could use either of these:
this.user$ = authState(auth);
OR
this.user$ = new Observable((observer: any) =>
onAuthStateChanged(auth, observer)
);
OR
this.user$ = user(auth);
and get the Promise version like so:
const user = await this.user$.pipe(take(1)).toPromise();

Related

Keep logged in between reloads - Firebase (Sign in with Google)

How do you keep a user logged in with Sign in with Google between reloads?
Here is my login function (Firebase):
const loginWithGoogle = async () => {
try {
const provider = new firebase.auth.GoogleAuthProvider();
const res = await firebase.auth().signInWithPopup(provider);
$user = res.user;
$isLoggedIn = true;
}
catch (error) {
console.log(error);
}
}
Although $isLoggedIn and the $user object save to LocalStorage (I'm using SvelteJS), the user does not actually stay logged in after reloading.
After reloading, admin users are no longer able to write to the database and I get the error firestore: PERMISSION_DENIED: Missing or insufficient permissions.
(Here are my firestore rules)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read;
allow write: if (request.auth != null && (request.auth.uid == "someAdminID" || request.auth.uid == "otherAdminID"));
}
}
How would I stay logged in after reloading? Or is there some way to automatically log in again if the user had not previously logged out?
On most browser environments Firebase automatically persists the user credentials in local storage, and restores them when the app/page reloads. This requires it to make a call to the server however, a.o. to check if the account was disabled, and thus it isn't completed right away when the page loads.
To react to when the authentication state is restored (or otherwise changes), you'll want to use an auth state listener as shown in the first code snippet in the documentation on getting the currently signed in user:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
// 👈 This is where you can also query the database as the user for the first time
} else {
// User is signed out
// ...
}
});

Firebase auth + firestore (what is the proper way to "link" users)

I'm making smth with backed for the first time in my life, so I'm sorry in advance, I'm making a web chat app. I think I managed to deal with authentication (it seems to be working) and now I want to make connect somehow the authentication user names with chat users... so I tried to
const docRef = await addDoc(collection(database, 'users'), {
name: user.displayName,
});
console.log('Document written with ID: ', docRef.id);
} catch (e) {
console.error('Error adding document: ', e);
}
but it says user not defined because userCredentials is in the scope of authentications functions...
If I paste this code into some function where userCredentials can be found, it says there is some problem with await word...
I want to take userCredential that logged in and use it in the chat app... so I need to link somehow the auth db and the firestore db? or is it done completely differently? Thank you
Could you give a bit of advice? Thank you (edited)
If you want to use the user's name, you first need to make sure that your code only runs once the user is signed in. In Firebase you can do that as shown in the documentation on getting the currently signed in user:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// 👇 your application specific code is below
const docRef = await addDoc(collection(database, 'users'), {
name: user.displayName,
});
console.log('Document written with ID: ', docRef.id);
} else {
// User is signed out
// ...
}
})
I'd actually recommend not using addDoc, but basing the document ID on the UID of the user, like this:
if (user) {
// 👇 get UID from user
const uid = user.uid;
// Use as document ID 👇 👇 👇
const docRef = await setDoc(doc(database, 'users', uid), {
name: user.displayName,
});
Since document IDs are unique within a collection, this automatically ensures that each user can only have one document in the users collection. It also makes it easier to find a user's document when needed, and makes it easier to ensure users can only edit their own document.

How to store firebase authentication session

I have a web application made in vue with firebase authentication. The problem here is that I need to login every time I reload the page.
Firebase Authentication by default stores the user's credentials in local storage of the browser, and restores it from there when the page reloads. This requires a call to the server though, which means that if you access Firebase.auth().currentUser immediately as the page loads, it might not be set yet.
To prevent having this problem, use an auth state listener as shown in the first snippet of the documentation on getting the current user.
For v8 and earlier of the SDK that'd be:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
For v9:
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});

Firebase authentication in ReactJS project - user null inside onAuthStateChanged

I'm tearing my hair out trying to setup email/password authentication through firebase.
I've got my firebase configuration setup like so
// Firebase App (the core Firebase SDK) is always required and must be listed first
import firebase from "firebase/app";
//authenticaion module
import "firebase/auth";
// Add the Firebase products that you want to use
import "firebase/firestore";
var firebaseConfig = {
// I've got my api key and other info copied from the console in here
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
export const db = firebase.firestore();
export const app = firebase.auth();
I've got a sign-in form created that calls the following function:
import { app } from "../firebase/firebaseConfig";
const createAccount = () => {
app
.createUserWithEmailAndPassword(email, password)
.then((user) => {
console.log("user created");
console.dir(user);
})
.catch((error) => {
console.log("something went wrong");
});
};
First problem: I'm not seeing the "user created" message, even though I can see the user being created in the Firebase Authentication console. I'm also not seeing the "something went wrong" message that would indicate an exception occurred.
Second problem: I'm trying to do a re-direct when the user signs in. To do this, I've setup a listener on the Firebase auth object as suggested in the docs:
firebase.auth().onAuthStateChanged((user) => {
console.log("Inside listener");
console.dir(user);
});
The problem is, I'm seeing the console message so the function is triggering but 'user' is always null in here even though the user is being created.
Firebase version: 8.2.1
Can anyone see what I'm missing here?
It is normal that in an auth state listener your callback first gets called with null, as that is typically the initial user authentication state as the page is loaded.
Firebase tries to automatically restore the user's authentication state, but this may take some time. Only once the state is restored will it again call your auth state listener with the then active user account.
I found a solution in case anyone runs into a similar issue - the button that was triggering the submit was inside a html form and I was not calling event.preventDefault() and so the page was re-rendering and I believe this was causing the auth callback to work incorrectly. Working code -
const createAccount = (event) => {
event.preventDefault();
app
.createUserWithEmailAndPassword(email, password)
.then((user) => {
console.log("user created");
console.dir(user);
})
.catch((error) => {
console.log("something went wrong");
});
};

Firebase Auth setCustomClaims() not working

I am facing a problem with setting custom claims for Firebase Authentication service's token. I am using Cloud function to set the custom claims for Hasura. The cloud function executes upon new user create event to set the custom claims. Here's my code running in cloud function
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.processSignup = functions.auth.user().onCreate(user => {
// create custom claims for hasura
const hasuraClaims = {
"x-hasura-default-role": "user",
"x-hasura-allowed-roles": ["user"],
"x-hasura-user-id": user.uid
}
// attach claims to user auth object
return admin.auth().setCustomUserClaims(user.uid, hasuraClaims)
.then(_ => {
functions.logger.info('SUCCESS: Custom claims attached');
})
.catch(err => {
console.log('ERROR: ', err);
})
})
In my frontend web page, I am running the following code to get the idToken
// subscribe to user state change
firebase.auth().onAuthStateChanged(async user => {
console.log('Firebase auth state changed');
if (user) {
// User is signed in.
window.User = user;
let idToken = await user.getIdTokenResult();
console.log('idToken: ', idToken);
}
})
I don't know what I'm doing wrong, but the token doesn't contain the custom claims that I've set in my Cloud function processSignup(). I know that the function executed without error because I can check my function logs and find the info entry SUCCESS: Custom claims attached.
Can anyone please help me solve this problem?
Updating claims does not trigger an onAuthStateChanged (the auth state of being logged in or not has not changed, but the users' claims have) and tokens are minted and then used for ~1h.
You are calling getIdTokenResult but not forcing a refresh, try:
let idToken = await user.getIdTokenResult(true);
which will force a new token to be fetched from the server and will (hopefully) include your custom claims.

Categories

Resources