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.
Related
I have a firebase app that is running on mobile devices and now I need to create the web version of it.
At the moment I have integrated firebase ui to give to users the ability to login. Since I need to show a wizard if the user isn't already registered, how I can check if user exists in the database so I can decide if show the wizard or not and then add it to the database?
It sounds like you're looking for the fetchSignInMethodsForEmail method, which returns the list of providers with which a specified email address has previously been signed in to Firebase.
The typical flow is:
Get the email address of the user
Call fetchSignInMethodsForEmail to get a list of providers
If the list is empty, go to your account creation flow
If the list has a single value, show the sign in screen for that provider
If the list has multiple values, show a screen where the user can pick from those providers
I consider you are using firebase auth and firestore, but this is the logic
firebase.auth().onAuthStateChanged(async (user)=> {
if (user == null){
//the user is not signed in, navigate to login screen
}else{
//the user is signed in, try to check if he exists in the database or register him
//let say you are using firestore roo
let user_ = await firebase.app().auth().currentUser
firebase.firestore().collection("collections_of_users").doc(user_.uid).get().then(snap =>{
if(snap.exists){
//the user exists
}else{
//register him, and before make sure his email is verified by checking if emailVerified === true
firebase.firestore().collection("collections_of_users").doc(user_.uid).set({user_mail: user_.email, user_name: "xxx"})
}
})
}
});
I am trying out firebase auth. I want to store a photo-url and a username when the user signs up. My code for signing up -
const email = signupForm['email'].value;
const password = signupForm['password'].value;
auth
.createUserWithEmailAndPassword(email, password)
.then((cred) => {
console.log(cred);
alert('done');
signupForm.reset();
})
.catch((error) => {
console.log(error);
alert(error);
});
Could someone tell me how to add username and photo-url in signup. I know I can make a collection in a firebase db but I read somewhere that username and photo-url can also be saved in firebase auth
Firebase Authentication doesn't have a user property that stores username. You would have to store that on your own, in a database, and make sure the string is unique.
You can store a profile picture URL using updateProfile(). It will not store the image for you - you can only provide a URL. If you need to store the image file itself, tou should probably consider using another product for that, such as Cloud Storage.
Typically, if you have custom information to store per user, you should do that in a database using the unique ID of the user signed in. This gives you full control, rather than depending on what Firebase Auth provides (which is not much). The purpose of Auth is to validate the user's identity, not store per-user information.
I'm doing a web application using Angular 8 and AngularFire.
On my custom website, a user with the admin privilege, can create accounts for other people.
To do this, I have read that I need to:
Create a second auth reference and use that to create users.
Reason: if we use our default auth reference, when the createUserWithEmailAndPassword() method creates the new user, the
user is signed in automatically, overriding our current session (the
logged in user on our custom website).
I have tried:
async createUserWithEmailAndPassword(email: string, password: string): Promise<string> {
// 1. A new auth session
let secondaryApp = firebase.initializeApp(environment.firebaseConfig, 'Secondary');
// 2. Create the account
const user = await secondaryApp.auth().createUserWithEmailAndPassword(email, password);
// 3. Sign out the user created
await secondaryApp.auth().signOut();
// 4. Make the auth session null to ensure that it's deleted
secondaryApp = null;
if (user.user.uid) {
// User created
return Promise.resolve(user.user.uid);
} else {
// Error
return Promise.reject();
}
}
Error received (happens when I call the method a second time):
FirebaseError: Firebase: Firebase App named 'Secondary' already exists
(app/duplicate-app).
My thoughts/steps were:
Create a new auth session.
Create the user.
Sign out the user created.
Delete the auth session created.
All clear and ready to create a new account in any moment.
What I'm doing wrong?
My goal is to be able to create accounts while I'm logged in on my custom website.
An educated guess is that you get that error message the second time you call your own createUserWithEmailAndPassword method. The reason for that is that you've already created an app named Secondary on the previous call, and didn't remove that app.
The easiest solution is to check if the Secondary app already exists before creating it.
let secondaryApp = firebase.app("Secondary");
if (!secondaryApp) {
secondaryApp = firebase.initializeApp(environment.firebaseConfig, 'Secondary');
}
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.
I am building a webpage using AngularJS and Firebase. I want to use facebook login to connect information on the webpage with the user. Firebase has a version of simple login which I guess is supposed to simplify the login process.
My problem is that I want to access information about the logged in user in a lot of places on my webpage but I can't find a good way to do it.
This is how I started out:
var userInfo = null;
var ref = new Firebase('https://<yourfirebase>.firebaseIO.com/');
var auth = new FirebaseSimpleLogin(ref, function(error, user) {
if(error)
alert("You are not logged in");
else if(user)
{
//store userinfo in variable
userInfo = user;
}
else
//user logged out
});
//do something with userInfo
alert(userInfo.name);
My first thought was to run this at the top of my page and then use the info about the user. The problem is that the code using userInfo (as in e.g. the alert) will always run before the userInfo variable has been filled and userInfo will return undefined/null.
I then proceeded to always create a new firebasesimplelogin object when i want to retrieve user data. Which of course isn't very good. Especially since every created FirebaseSimpleLogin object will be called again whenever another is called or a user logs out, for example.
So my question is, how do I use FirebaseSimpleLogin to handle and use my user information in the best way?
I would have liked some function to getUserInfo() or check isLoggedIn() for example. How do you do this properly?
You can take a look at this example for thinkster. It's based on using simple login with userid/password. http://www.thinkster.io/angularjs/4DYrJRxTyT/7-creating-your-own-user-data-using-firebase.
You can create a function like getLoggedinUser that runs in $rootScope that will allow you to find the user throughout the application.
UPDATE:
Around the middle of October 2014, firebase made some big changes. This method might still work, but it's better to take advantage of the newer version of firebase, specifically getauth and onauth. These methods will allow you to do the same thing without running on the rootScope. https://www.firebase.com/docs/web/guide/user-auth.html#section-login
Please make a constant to use it everywhere in your App like that
.constant('facebookUrl', 'https://rjrestaurantapp.firebaseio.com');
Then in the controller inject this constant "facebookUrl & "$state" as shown below...
.controller('loginCtrl', function($scope,facebookUrl,$state){
and then you only need to give name of the state where you want to redirect after facebook authentication..
var ref = new Firebase(facebookUrl);
$scope.fbLogin = function () {
ref.authWithOAuthPopup("facebook", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
$state.go('restaurant');
}
})
}})
You can see the information in authData object after successfull authentication using facebook ....
please read this doc carefully https://www.firebase.com/docs/web/guide/login/facebook.html
The above is the example of simple login using firebase and for retrieving data for each logged in user, you have to store user information at the time of signin as you know that firebase makes every child with a unique ID .. then you only need to use the offline features of firebase that will find which user is offline and according to that remove the node with the same ID which one is offline, you can find examples in the MANAGING PRESENCE section of the below link ..
https://www.firebase.com/docs/web/guide/offline-capabilities.html