I am developing multi page application, that uses firebase as a back-end.
On every page after firebase initialization I have the following code:
firebase.auth().signInAnonymously().catch(...);
firebase.auth().onAuthStateChanged(function(user)
{
if (user) {
// ...
} else {
// User is signed out.
// ...
}
// ...
});
which sign in every visitor anonymously.
The problem is, when user navigates to another page of my application or opens it in a new tab, or just refreshes the current page - new anonymous user is generated.
I can use client side cookies to store anonymous user id, but how can I later (after page refresh) use that stored anonymous user id, to sign in user with that id?
It was my mistake,
firebase store anonymous user authorization in local storage by default.
My code sample should be rewritten this way to work properly:
firebase.auth().onAuthStateChanged(function(user)
{
if (user) {
// User is signed in
// ...
} else {
// User is signed out.
firebase.auth().signInAnonymously().catch(/*...*/);
}
// ...
});
Related
I am able to anonymously create a user in firebase with the below code but when I try to log the current user's 'uid' in the console it returns 'undefined'. Please advise on my below code.
firebase.initializeApp(firebaseConfig);
firebase.auth().signInAnonymously();
console.log(userID);
If you want to log the UID of the user after they sign in, you have to ways to accomplish that:
firebase.auth().signInAnonymously().then((credential) => {
console.log(credential.user.uid);
});
This will log the console when the user actively logs in.
If you instead want to respond whenever the user signs in or out, you can do:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log(user.uid);
} else {
// No user is signed in.
}
});
This second way works even if you restart the app, as Firebase will in that case automatically restore the user's authentication state.
I'm working on the multitenat application using Angular and Firebase in combination with Google Cloud Identity Platform. The later one is part of the Firebase auth and allows me to group users under different tenants plus it adds the "tenantId" property to a user.
All my users and a sub-collection of a tenant document.
The challenge I have is how to obtain a currently logged in user via Firebase auth API in time to make the query to list all the users for a given tenant.
Imagine logged in user pasts in the browser's address bar URL http://tenant.domain.com/users. In order to retrieve this data I need to know the "tenantId" which has to come via currently logged in user which resolves later than I make my query.
I retrieve current user using the following code:
getCurrentUser() {
return new Promise<any>((resolve, reject) => {
var user = firebase.auth().onAuthStateChanged(function (user) {
if (user) {
resolve(user);
} else {
reject('No user logged in');
}
})
});
}
Basically I'm looking for away to have "tenantId" available at any time and place in the application.
I have a web application using html-js-css and a flask server.
My web app is a multi-pages app which apparently means that I have to Initialize firebase for each page in which i want to use it -.-
The problem is that every time I initialize firebase app, I lose the current user so while in my main page, after log-in, if I write:
const USER = firebase.auth().currentUser;
console.log(USER.uid);
I get my user ID, as soon as I move to another page and repeat the above code, I get the error:
TypeError: USER is null
Is there a way to either:
avoid Initializing the firebase-app at avery page
keep the CurrentUser (even storing it securely somewhere)
Thank you
Workaround:
I got this workaround working before Frank answer which is probably the best way to proceed. Instead I just stored the user id in an encrypted variable accessible to all pages.
Since the main.html page is always loaded, I store/removed the variable in a onAuthStateChanged listener there so as soon as the user is logged out, that variable is removed:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
cached_uid = JSON.stringify(user.uid);
cached_uid = btoa(cached_uid);
localStorage.setItem('_uid',cached_uid);
} else {
localStorage.removeItem('_uid');
}
});
then on the other pages:
function loadUID(){
var uid = localStorage.getItem('_uid');
if (!uid) return false;
uid = atob(uid);
uid = JSON.parse(uid);
return uid
}
I followed this to find this solution:
How to send variables from one file to another in Javascript?
You will need to initialize the Firebase app on each page, but that is supposed to be a fairly cheap operation.
To pick up the user on the new page, Firebase runs a check against the server to ensure the user token is still valid. Since this code calls a server, its result likely isn't available yet when your firebase.auth().currentUser runs.
To solve this, run the code that requires a user in a so-called auth state change listener:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
});
Also see the Firebase documentation on getting the currently signed in user.
In Firebase I need to check if a Facebook user exists without creating the user. Initially the user is anonymous, and they try to login with Facebook. I want this to fail if the Facebook account is not already linked to a user in my system. It won't be linked to the current user because they are anonymous,
If I use Auth.signInAndRetrieveDataWithCredential I expected a "auth/user-not-found" error, but instead the user is simply created. Is this a bug or expected?
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential).then( (userCredential) => {
let user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
If I use User.reauthenticateAndRetrieveDataWithCredential instead I get the error "auth/user-mismatch" which makes sense because user is currently anonymous. However, I was expecting "auth/user-not-found" may be thrown instead if the credential doesn't exist, but that doesn't happen.
I don't see a way to take my anonymous user, have them login with Facebook and then see if another user is already linked to that Facebook credential without creating the user if it doesn't exist.
If you're wondering why? My scenario is:
The system allows anonymous users
A user logs in, then converts to a logged in user by registering with Facebook.
App uninstall
App reinstall
User starts up the app and is initially anonymous.
They try and login with Facebook again. At this point I want to stop them from creating a user if they don't have one already. If they have a user ID already, the code works fine and changes their anonymous account ID to the original user ID which is good.
I found a solution! It wasn't too hard to implement, but it does seem hacky.
So we know that when using signInAndRetrieveDataWithCredential(cred) for facebook login, the account is created even if it does not exist yet. To solve this, we need to make sure that we handle the following three things:
Detect if the account is new
Delete the current account that was created by firebase
Throw an error to get out of the current flow and return to wherever you were before.
I just implemented and tested this solution, and it seems to work great:
// ... do your stuff to do fb login, get credential, etc:
const userInfo = await firebase.auth().signInAndRetrieveDataWithCredential(credential)
// userInfo includes a property to check if the account is new:
const isNewUser = _.get(userInfo, 'additionalUserInfo.isNewUser', true)
// FIRST, delete the account we just made.
// SECOND, throw an error (or otherwise escape the current context.
if (isNewUser) {
firebase.auth().currentUser.delete()
throw new Error('Couldn\'t find an existing account.')
}
// If the user already exists, just handle normal login
return userInfo.user
The reason I did this was to ensure that users had to go through the "create account flow" in my app. Your case would be really easy to implement as well, something like the following:
let credential = firebase.auth.FacebookAuthProvider.credential(event.authResponse.accessToken)
firebase.auth().signInAndRetrieveDataWithCredential(credential)
.then(userCredential => {
const isNewUser = userCredential.additionalUserInfo.isNewUser
if (isNewUser) {
firebase.auth().currentUser.delete()
// The following error will be handled in your catch statement
throw new Error("Couldn't find an existing account.")
}
// Otherwise, handle login normally:
const user = userCredential.user
app.debug("DEBUG: Existing user signed in:"+user.uid)
this.loginSuccess(user)
}).catch( (err) => {
app.error("ERROR re-signing in:"+err.code)
$("#login_status_msg").text(err)
})
You can use linkAndRetrieveDataWithCredential:
let credential = firebase.auth.FacebookAuthProvider.credential(
event.authResponse.accessToken);
anonymousUser.linkAndRetrieveDataWithCredential(credential).then( (userCredential) => {
// Firebase Auth only allows linking a credential if it is not
// already linked to another account.
// Now the anonymous account is upgraded to a permanent Facebook account.
}).catch( (err) => {
// Check for code: auth/credential-already-in-use
// When this error is returned, it means the credential is already
// used by another account.
})
You can use the method fetchSignInMethodsForEmail to check if an specific email is already associated to an specific provider or not. Doing this you will be able to check if one if the SighInMethods of the email associated to your user contains Facebook.com or not.
I show you below an example about how I manage this cases on my application. I'm using an RxJavaWrapper on my code, but you will understand the point of how to manage it:
RxFirebaseAuth.fetchSignInMethodsForEmail(authInstance, email)
.flatMap { providerResult ->
if (!providerResult.signInMethods!!.contains(credential.provider)) {
return#flatMap Maybe.error<AuthResult>(ProviderNotLinkedException(credential.provider))
} else {
return#flatMap RxFirebaseAuth.signInWithCredential(authInstance, credential)
}
}
.subscribe({ authResult ->
//Manage success
}, { error ->
//Manage error
})
First I check the providers associated to the email of the user(You can retrieve it from the provider)
If the list of SignInMethods contains my credential provider, I throw an error, if not, I call my signInWithCredential method to create the user.
Continue your workflow.
What I did to solve this problem without relying on the call to linkAndRetrieveDataWithCredential to fail and using the catch block to sign in the already existing user is to save the userID field that getCurrentAccessToken returns.
const { userID } = data;
this.props.setFacebookId(userID); // saves the userID on the server
I can later check if this userID already exists next time the user signs up with facebook.
From the Simple Web Login docs, it claims you can resume a session by doing the following:
var auth = new FirebaseSimpleLogin(chatRef, function(error, user) {
...
});
Is there an equivalent for AngularFire?
The $firebaseSimpleLogin constructor takes a firebase reference as its only parameter..
Specifically, what I'm trying to do is login once anonymously and resume that login on a full page refresh. Is that achievable?
Whether or not your auth session is preserved is determined by the parameters you pass into the login method. Whether you use FirebaseSimpleLogin or $firebaseSimpleLogin is irrelevant here.
var auth = $firebaseSimpleLogin(ref);
$rootScope.$on('$firebaseSimpleLogin:login', function(user) {
console.log('logged in', user.uid);
});
// automagically logs in the next time you call $firebaseSimpleLogin() after page refresh
auth.$login('anonymous', { rememberMe: true });
// does not automagically log in
auth.$login('anonymous');
UPDATE As of Angular 1.x and Firebase 2.x this syntax and behavior has changed.
From the remember attribute in Firebase docs:
If not specified - or set to default - sessions are persisted for as
long as you have configured in the Login & Auth tab of your App
Dashboard. To limit persistence to the lifetime of the current window,
set this to sessionOnly. A value of none will not persist
authentication data at all and will end authentication as soon as the
page is closed.
var auth = $firebaseAuth(ref);
$firebaseAuth.$onAuth(function(user) {
console.log('logged ' + (user? 'in' : 'out'), user && user.uid);
});
// stays logged in for length specified in the app dashboard
auth.$authAnonymously();
// stays logged in until the browser closes
auth.$authAnonymously({ remember: 'sessionOnly' });
// does not stay logged in on page refresh
auth.$authAnonymously({ remember: 'none' });