Firebase getIdToken not working - javascript

I'm makeing tests with Firebase Authentication from javascript client and I'm trying to retrieve the idToken with retrieve id tokens on clients documentation
I think I'm forgetting something basic.
A user is logged in with Google
The code is just i've seen in other posts and in the documentation. And the result is in the comments.
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log(user); // this is shown. Firebase user and provider data
console.log(user.uid); // Shown
firebase.auth().user.getIdToken().then(function(idToken) {
console.log(idToken+'--'); // Nothing happens. No errors and the function not continues
});
console.log(user.uid); // Nothing happens
}
})
Thanks
EDIT:
if I add anything wrong nothing happens too. for example if I add an alert it shows the alert but if I have a mistake, for example alter() not shows any error. Added catch and nothing too
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
alter() // Nothing happens and the function stop
console.log(user); // this is shown. Firebase user and provider data
console.log(user.uid); // Shown
firebase.auth().user.getIdToken().then(function(idToken) {
console.log(idToken+'--'); // Nothing happens. No errors and the function not continues
}).catch(function(error) {
console.log(error+'--'); // Nothing
});
console.log(user.uid); // Nothing happens
}
})

firebase.auth().user doesn't have the user in that moment yet. You have to use the user from firebase.auth().onAuthStateChanged directly like this:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
console.log(user); // It shows the Firebase user
console.log(firebase.auth().user); // It is still undefined
user.getIdToken().then(function(idToken) { // <------ Check this line
console.log(idToken); // It shows the Firebase token now
});
}
});
You can only use firebase.auth().user after firebase.auth().onAuthStateChanged is completed and outside of its scope, otherwise it will be undefined.

firebase.auth().currentUser is synchronous. We can make it asynchronous by subscribing to the auth observable instead.
Depending on the library we’re using, the JavaScript SDK has onAuthStateChanged() and AngularFire2 has both authState() onAuthStateChanged().
// For AngularFire:
private afAuth: AngularFireAuth,
afAuth.authState.subscribe(user => {
if (user) {
user.getIdToken(true).then(idToken => {
// ...
});
}
});
// or
this.afAuth.auth.onAuthStateChanged(user => {
if (user) {
user.getIdToken(true).then(idToken => {
//...
});
}
});
// For the JS SDK
firebase.auth().onAuthStateChanged(user => {
if (user) {
user.getIdToken(true).then(idToken => {
// ...
});
}
});

For firebase versions greater than V8. We directly have to call the functions of auth.
const loginWithGoogle = () => {
const googleProvider = new GoogleAuthProvider();
signInWithPopup(auth, googleProvider)
.then((userCredentials) => {
if (userCredentials) {
setIsUserAuthenticated(true);
window.localStorage.setItem('authorization', true);
console.log(userCredentials);
userCredentials.user.getIdToken().then((token) => {
setToken(token);
console.log(token);
});
}
})
.catch((err) => {
console.log(err.message);
});
};

Related

Firebase - Why is the claim not added to the user attributes?

I'm adding the claim to a user's profile that he or she paid for something, though, after the payment this attribute isn't visible. I'm running the functions on an emulator on a local host.
This is the code I'm using:
If the paypal function has been handled succesfully through paypalHandleOrder, then the function addPaidClaim is invoked.
onApprove: (data, actions) => {
paypalHandleOrder({ orderId: data.orderID }).then(
addPaidClaim(currentUser).then(
alert("THANKS FOR ORDERING!"),
// currentUser.getIdTokenResult().then(idTokenResult => {
// console.log(idTokenResult.claims)
// })
)
.catch((err) => {
return err;
})
);}
addPaidClaim is a firebase cloud function, which goes as follows:
exports.addPaidClaim = functions.https.onCall((data, context) => {
// get user and add custom claim (paid)
return admin.auth().setCustomUserClaims(data.uid, {
paid: true,
}).then(() => {
return {
message: `Success! ${data.email} has paid the course`,
};
}).catch((err) => {
return err;
});
});
I've refreshed the page and checked the user attributes afterwards through console.log on the user to see if the attribute had been added, but this is not the case. I can't find attribute paid inside the idTokenResult object. What should I do? I also find it hard to make sense of what's happening inside the function addPaidClaim. It's not returning an error when I look at the logs on my firebase console, and not much information is given, besides that the function has been invoked.
Okay, I know this question is pretty old. But I found a way just yesterday after 3 days searching over the solution. After we set up a new claim for a new user using, we need to refresh the client's getIdTokenResult(true) in the app. These are the ways I did it in Flutter Dart until a new user with updated claim managed to use it:
FirebaseAuth auth = FirebaseAuth.instance;
Future<Map<String, dynamic>> signInWithGoogle() async {
Map<String, dynamic> output = {};
final googleUser = await googleSignIn.signIn();
if (googleUser == null) {
log("Firebase => Gmail account doesn't exist");
} else {
final googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
);
await auth.signInWithCredential(credential).then((values) async {
await userAuth(credential).then((value) =>
value.addAll(output));
});
}
return output;
}
Future<Map<String, dynamic> userAuth (OAuthCredential credential) async {
Map<String, dynamic> output = {};
await auth.currentUser!.reauthenticateWithCredential(credential);
await auth.currentUser!.reload();
await auth.currentUser!.getIdTokenResult().then((result) => {
if(result.claims!.isNotEmpty){
//check your claim here
} else {
//assign log here
}
});
return output;
}

Code acting like it is asynchronous when I don't think it is

I'm struggling to see why some code is behaving as it is.
Vue3 front end login page interacting with Google Firebase services (authentication and firestore db)
const onSubmit = () => {
.
.
.
// above missing section just gathers some form data
signInUser(form_email.value, form_passowrd.value)
.then(() => {
const user = firebase.auth().currentUser
if (user.emailVerified) {
checkUser(user.uid)
router.push({ name: 'Profile' })
}
else {
resendVerification.value = true
commandNotification('show', 'error', 'Unverified Account')
}
})
.catch((error) => {
if (error.code === 'auth/invalid-email') {
commandNotification('show', 'error', 'Invalid Email Address')
}
// followed by other checks
})
}
signInUser is the official firebase auth email and password sign in function that is imported into this module.
So, user is signed in, then I check to see if the user has verified their email address. If they have I do some further checks in a "users" firebase table, and then route user to their profile page.
The problem that I am having is that the router.push is happening before the check user function has completed. I've even put a 10 second setTimeout between the two lines and the router push comes immediately.
It seems checkUser is running asynchronously but I didn't think it was. I've not marked it async myself.
The checkUser function itself does the following
const checkUser = (userid) => {
db.collection("users").doc(userid).get()
.then((d) => {
if (d.exists) {
userStateStore.action(MutationType.user.updateUser,
{
id: d.data().id,
country: d.data().country,
handle: d.data().handle
}
}
else {
// user creation when they do not exist
}
})
}
I'm currently testing with a user that already exists. Because the route is being called immediately the profile data is not in the vuex store.
I'm looking for some pointers on how to approach debugging this, or an answer if you see a specific problem.
Many thanks in advance.
checkUser is async. It should return the promise that get() returns...
const checkUser = (userid) => {
return db.collection("users").doc(userid).get()
// ...
I'd cleanup the caller (without async/await) like this...
const onSubmit = () => {
return signInUser(form_email.value, form_passowrd.value)
.then(() => {
const user = firebase.auth().currentUser
return user.emailVerified ? checkUser(user.uid) : Promise.resolve(false)
}).then(checkUserResult => {
if (checkUserResult) {
router.push({ name: 'Profile' });
} else {
resendVerification.value = true
commandNotification('show', 'error', 'Unverified Account')
}
})
// ...
These could also be made tidier (and exactly equivalent) using async/await syntax.
It seems checkUser is running asynchronously but I didn't think it was. I've not marked it async myself.
Yes, .get() is asynchronous and even though you haven't used async/await you're instead using .then().
One solution could be to put the router.push() invocation within the callback of the .then() within checkUser().

How do i get displayName from Firebase?

I have been having trouble getting the display name out of Firebase.
below is the Sign Up process coding
const promise = auth.createUserWithEmailAndPassword(email, pass)
.then(
(user)=>{
// here you can use either the returned user object or
firebase.auth().currentUser. I will use the returned user object
if(user){
user.updateProfile({
displayName: textUsername.val(),
// photoURL: // some photo url
})
}
})
.then( function() {
console.log('User Name Set!')
})
.catch(function() {
console.log('error')
});
promise.catch(e => console.log(e.message));
})
firebase.auth().onAuthStateChanged(firebaseUser => {
if(firebaseUser) {
console.log(firebaseUser)
} else {
console.log('not logged in');
}
})
the console.log shows 'user name set!' and the console.log(firebaseUser) shows that the displayName is set in the database of the currentUser, and the name is what i expected.
However,
console.log(firebase.auth().currentUser)
gives null, and
if (user != null) {
console.log(user.displayName);
}
this also returns as null.
I have been looking for ways to get the data of the firebase dataset but I cannot manage to do that.
it will be great if anyone can give me any advice about it.
Thank you.
firebase.auth().currentUser.displayName returns the OAuth provider's display name. However, firebase.auth().currentUser resolves asynchronously and is empty/null for a moment on page load. If you put the console.log(firebase.auth().currentUser.displayName) inside the .onAuthStateChanged you'll see that.
I ended up using recursion to solve this. I wish there was a more elegant way but this was the only thing that worked for me. Please share if you have solved this with a different solution!!
I needed this for creating new users. When someone signed in it never seems to return null.
The setTimeout() slows the recursion down by .5 seconds so that you don't get an error by calling the function too many times. You can speed it up or slow it down by adjusting the milliseconds.
Working code:
auth.onAuthStateChanged(user => {
if (user) {
// Everything inside here happens if user is signed in
console.log(user)
// Greet the user with a message and make it personal by using their display name
injectDisplayName = () => {
if(firebase.auth().currentUser.displayName === null){
console.log('happens');
setTimeout(() => {
injectDisplayName()
}, 500)
} else {
document.getElementById('display-name-header').textContent = `Hello, ${firebase.auth().currentUser.displayName}`
}
}
injectDisplayName()
} else {
// Everything inside here happens if user is not signed in
}
})

Can not login after npm update Firebase

I updated firebase to "firebase": "^4.2.0". Seems like the user's object properties changed.
const loginGG = () => {
try {
firebase.initializeApp(clientCredentials)
} catch (e) {
console.log('firebase is already created')
}
return firebase.auth().signInWithPopup(new firebase.auth.GoogleAuthProvider())
.then(result => {
if(!result || !result.user) throw new Error('LOGIN ERROR')
const token = result.user.ze;
const user = result.user;
Cookies.set('tapId', token);
Cookies.set('tapUser', JSON.stringify(user));
history.go(-1);
return {
user,
token
}
})
.catch(function(error) {
console.log(error);
return {}
});
}
so I changed 'result.user.ie' to 'result.user.ze'. I know there is another way to generate the token because on firebase doc they say 'Use User.getToken() instead.' I tried and no success. Maybe someone who has experienced it can help me ?
.ze or .ie sound like non public fields, as you said, you could use the getToken method. which returns a promise resolving with the jwt token. something like
user.getToken().then(token => {/* do something with the token*/})
see the firebase docs:

Facebook login in React Native

I am developing an app in React Native and I want to implement logging in with Facebook.
I have an API in Node.js where I handle the logic for users to log in, etc.
I use passport.js to let users log in with either Facebook or traditional Email.
I am opening an URL in my API with SafariView which is just a regular "WebView" directly in my app.
I have tried using the following code:
class FacebookButton extends Component {
componentDidMount() {
// Add event listener to handle OAuthLogin:// URLs
Linking.addEventListener('url', this.handleOpenURL);
// Launched from an external URL
Linking.getInitialURL().then((url) => {
if (url) {
this.handleOpenURL({ url });
}
});
}
componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL);
}
handleOpenURL({ url }) {
// Extract stringified user string out of the URL
const [, user_string] = url.match(/user=([^#]+)/);
this.setState({
// Decode the user string and parse it into JSON
user: JSON.parse(decodeURI(user_string))
});
if (Platform.OS === 'ios') {
SafariView.dismiss();
}
}
openURL(url) {
if (Platform.OS === 'ios') {
SafariView.show({
url: url,
fromBottom: true,
});
} else {
Linking.openURL(url);
}
}
render() {
return (
<Button
onPress={() => this.openURL('https://mywebsite.com/api/auth/facebook')}
title='Continue with Facebook'
...
so I guess I will have to do the authentication on URL https://mywebsite.com/api/auth/facebook and then send the user to an url that looks something like OAuthLogin://..., but I am not entirely sure how to use it.
Can anyone help me move in the right direction?
import { LoginManager, AccessToken } from 'react-native-fbsdk'; // add this file using npm i react-native-fbsdk
Create function
const onFacebookButtonPress = async () => {
// Attempt login with permissions
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
if (result.isCancelled) {
throw 'User cancelled the login process';
}
// Once signed in, get the users AccesToken
const userInfo = await AccessToken.getCurrentAccessToken();
if (!userInfo) {
throw 'Something went wrong obtaining access token';
}
console.log('user info login', userInfo)
// Create a Firebase credential with the AccessToken
const facebookCredential = auth.FacebookAuthProvider.credential(userInfo.accessToken);
setGoogleToken(userInfo.accessToken)
// Sign-in the user with the credential
return auth().signInWithCredential(facebookCredential)
.then(() => {
//Once the user creation has happened successfully, we can add the currentUser into firestore
//with the appropriate details.
console.log('current User ####', auth().currentUser);
var name = auth().currentUser.displayName
var mSplit = name.split(' ');
console.log("mSplit ",mSplit);
let mUserDataFacebook = {
user_registration_email: auth().currentUser.email,
user_registration_first_name: mSplit[0],
user_registration_last_name: mSplit[1],
registration_type: 'facebook',
user_registration_role: "Transporter",
token: userInfo.accessToken,
user_image : auth().currentUser.photoURL,
};
console.log('mUserDataFacebook',mUserDataFacebook)
LoginWithGoogleFacebook(mUserDataFacebook) /// Call here your API
firestore().collection('users').doc(auth().currentUser.uid) //// here you can add facebook login details to your firebase authentication.
.set({
fname: mSplit[0],
lname: mSplit[1],
email: auth().currentUser.email,
createdAt: firestore.Timestamp.fromDate(new Date()),
userImg: auth().currentUser.photoURL,
})
//ensure we catch any errors at this stage to advise us if something does go wrong
.catch(error => {
console.log('Something went wrong with added user to firestore: ', error);
})
})
}
Call this function on button press onFacebookButtonPress()
For android need to setup and add facebook id in
android/app/src/main/res/values/strings.xml file
add these two lines.
YOUR_FACEBOOK_ID
fbYOUR_FACEBOOK_ID //Don't remove fb in this string value
/////////////add this code in AndroidMainfest.xml file
//////////This code add in MainApplication.java file
import com.facebook.FacebookSdk;
import com.facebook.appevents.AppEventsLogger;
/////////add code build.gradle file
implementation 'com.facebook.android:facebook-android-sdk:[5,6)'

Categories