Firebase Current User Undefined when Web Page is Refreshed - javascript

My scenario is simple. I created a Firebase web app and I connect using a Google account. The issue is that I need to log back in every time the page is refreshed, here are the steps :
Initialize Firebase
Signup with Google
Check the current user - it is an authentified Google user
Refresh the page
Initialize Firebase
Check the current user - it is undefined
The code is straightforward:
firebase.initializeApp(config);
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider);
...
public onAuthStateChanged(context: any, user: any) {
if (user) { ...
...
//currentUser is defined
get currentUser(): any {
return firebase.auth().currentUser;
}
Refresh the page
//currentUser is undefined
get currentUser(): any {
return firebase.auth().currentUser;
}
...
if(!currentUser) {
firebase.auth().signOut();
firebase.initializeApp(config);
}
I am aware of options in the persistence of the Firebase sessions but my understanding is that this behavior is not the default. Cf. the doc:
https://firebase.google.com/docs/auth/web/auth-state-persistence
I added this line to my code just in case, it makes no difference:
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
I also checked that the same happens with Anonymous authentication.

Every time you call signInWithPopup(...) it will show a pop up window and ask the user to sig in. For that reason you should only call this method once you detect that the user isn't signed in. The easiest way to do this, is to call it from your onAuthStateChanged callback:
firebase.initializeApp(config);
var provider = new firebase.auth.GoogleAuthProvider();
...
public onAuthStateChanged(context: any, user: any) {
if (user) {
console.log(user);
...
}
else {
firebase.auth().signInWithPopup(provider);
}
...
}

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
// ...
}
});

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
// ...
}
});

why is my firebase authentication display name not getting updated properly with react js?

I am trying to build firebase authentication for my react app..After I sign in I am trying to update the displayName and then redirect..On the redirected page I am trying to greet the user by fetching the display name saved while signing up with firebase..This page works properly immediately after I redirect but if I reload this page then it is not able to show the displayName and throws this error:
TypeError: Cannot read property 'displayName' of null
This is the function which gets triggered when signup button is clicked..
const signup = async () => {
try{
await firebaseApp.auth().createUserWithEmailAndPassword(email, password)
await firebaseApp.auth().currentUser.updateProfile({displayName:username})
console.log(firebaseApp.auth().currentUser)
if (!firebaseApp.auth().currentUser){
setLoading(true)
}
history.push('/home')
}catch (error){
alert(error.message)
}
}
This is the JSX of the page which is being redirected to by signup page:
<div className="greetings">
Good Evening {firebaseApp.auth().currentUser.displayName}
</div>
Why is this issue happening and how to resolve it?
firebaseApp.auth().currentUser is always null when a page first loads. It won't contain a User object until some time later, after the SDK is able to load and verify the auth token for that user. Instead of using currentUser, you should set up an auth state observer as shown in the documentation. This observer will get invoked as soon as the User object is known.
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
// ...
} else {
// User is signed out.
// ...
}
});
You can use the results of this observer function to know when the user is signed in or signed out over time. To learn more about how it works, read this blog post.

Firebase Custom Claims doesn't propagate

I'm working in an Angular6 app with angularfire2. I'm setting the roles as custom claims in user creation, but it doesn't seem to propagate.
When I'm creating the user I send the userid, businessid and role to a cloud function:
bid > businessid
urole > role
req.body.uid > userid
const customClaims = {
roles: { [bid]: urole }
}
admin.auth().setCustomUserClaims(req.body.uid, customClaims)
.then(result => {
res
.status(200)
.send()
})
The problem is when the call to cloud function finishes and I want to redirect the user to a route which requires the user to have the custom claim set, but it fails. After some debugging, I've found out that if run:
this.angularFireAuth.auth.currentUser.getIdTokenResult(true).then(result => {
return result.claims.roles
})
immediately after the call to the cloud function "result.claims.roles" is undefined, but if I refresh the page, "result.claims.roles" have the data I set before.
I've already tried the reload method, and getIdToken(true) but I'm getting the same problem.
Is there a way to avoid refreshing the page and get the custom claims?
Thank you!
When the user is signed in, they get an ID token that is valid for about an hour. If you set a custom claim, their (server-side) profile is updated immediately, but their ID token is not auto-updated. So you'll need to refresh their ID token to get the new custom claims.
As far as I know this ID token is only refreshed by calling getIdTokenResult if it has expired. If that's the cause, calling user.reload() and then getting the ID token should give you the updated claims.
For me it simply worked taking the advice from one of the comments:
// --------
// Frontend
// --------
// Triggering the cloud function
const url: string = 'url-to-your-cloud-function'
await this.http.post<unknown>(url, {}).toPromise();
// After cloud function was run and custom claim was set -> refresh the id token
// The 'currentUser' is a reference to the firebase user
await this.authService.currentUser.getIdToken(true);
// --------
// Cloud Function - createSubscription
// --------
const createSubscription = () => {
await admin.auth().setCustomUserClaims(userId, {
subscriber: true
})
}

How do I get the current user's access token in AngularFire2?

In AngularFire you were able to access the providers (e.g Google) accessToken for the authenticated user.
There does not seem to be a way to access this with AngularFire2?
On initial login say like this:
this.af.auth.subscribe(user=> {
if (user) {
console.log(user.google);
}
});
It will log out the idToken, accessToken, provider,
But (on a page refresh) subsequently will log out the standard details (uid, displayName etc....)
And the accessToken is not an accessible property?
Is there a way to access the current users accessToken?
getToken is deprecated now. You should use getIdToken instead:
this.af.auth.currentUser.getIdToken(true)
.then((token) => localStorage.setItem('tokenId', token));
The access token is only accessible when the user first signs in. From the Firebase migration guide for web developers:
With the Firebase.com Authentication API, you can easily use the provider's access token to call out to the provider's API and get additional information. This access token is still available, but only immediately after the sign-in action has completed.
var auth = firebase.auth();
var provider = new firebase.auth.GoogleAuthProvider();
auth.signInWithPopup(provider).then(function(result) {
var accessToken = result.credential.accessToken;
});
So it is indeed not available on a page refresh. If you want it to remain available, you will need to persist it yourself.
With AngularFire2, you can get the token like this :
this.af.auth.getToken() // returns a firebase.Promise<any>
If you want to get an ES6 Promise instead, just use
Promise.resolve()
.then(() => this.af.auth.getToken() as Promise<string>)
This works for me:
this.af.auth.getAuth().auth.getToken(false);//true if you want to force token refresh
You can put it into a Service and then you can get the token like this:
this.authService.getToken().then(
(token) => console.debug(`******** Token: ${token}`));
Getting the auth token from storage in angularfire2
JSON.parse(JSON.stringify(this.afAuth.auth.currentUser)).stsTokenManager.accessToken
As seen in this discussion:
https://github.com/angular/angularfire2/issues/725
With AngularFire2 : ( eg : registering user with email and password combo. )
import { AngularFireAuth } from 'angularfire2/auth';
model : any = {} ;
private afAuth : AngularFireAuth,
regWithEP () {
this.afAuth.auth.createUserWithEmailAndPassword(this.model.email, this.model.password).then((user) => {
/* IMPORTANT !! */
/* EXPLICIT CHECK IF USER IS RETURNED FROM FIREBASE SERVICE !! */
if (user) {
console.log(user);
/* Here user is available and is the same as auth.currentUser */
this.afAuth.auth.currentUser.getToken().then((token) => {
//token is available here
//set token to currentUser, signIn , re-direct etc.
});
}
});
}

Categories

Resources