expo react native: firebase.auth().onAuthStateChanged is not called - javascript

I've been looked at numerous similar questions on stack-overflow and/or GitHub but none of the solutions offered have solved my problem. As it is stated in the title, the onAuthStateChanged handler is not called.
I have a Sign up form where user create an account by providing (among other things) his email and password. Then I signup the user with firebase:
firebase.auth().createUserWithEmailAndPassword(user.email, user.password)
But later on, when I reload the application, onAuthStateChanged is never called and
firebase.auth().currentUser returns null;
Here is my code (simplified):
useEffect(() => {
setAuthenticated(true);
const unsubscribe = firebase.auth().onAuthStateChanged((user) => { // <-- this is not called
if (user) {
// user is connected
}
});
return unsubscribe;
}, []);
Also in firebase console: Authentication --> Sign-in method --> Authorized domain, I have added my domains (including localhost).
This code used to work until yesterday...

Related

Firebase authentication: sendEmailVerification automatically verifies the user's email

I'm starting to use Firebase Authentication in my Next.js app.
I've enabled Email/Password authentication method and I've created the user directly from the Firebase console. I don't want to allow user sign up yet.
I've seen that the user email is not verified by default, so I started implementing the logic to do so with the provided sendEmailVerification method, just pretty simple code within the event of a button click:
const handleClick = async () => {
if (!user) {
console.warn(`Shouldn't request a verification email if user is not logged in!.`)
return
}
await sendEmailVerification(user)
}
But surprisingly, after that, the user's email gets automatically verified, and no email is even sent.
What am I missing?

signInWithCustomToken firebase - user not staying logged in

I am building an app (Main App) that authenticates through a seperate app (Auth App). I am able to signInWithCustomToken but the auth state does not persist between client browser refreshes even though onAuthStateChanged runs with the user after signInWithCustomToken.
Below is the authentication flow:
Users open the Main App, and click a button to open a popup that displays the Auth App.
window.open(AUTH_URL, 'window', 'width=400,height=600');
On the Auth App users create a Firebase account with email and
password.
firebase.auth().signInWithEmailAndPassword(email, password)
The Auth App makes a request to the Auth App's server to
generate a custom token.
// client side code
const idToken = await firebase.auth().currentUser.getIdToken()
const token = api.generateTokenAPIRequest(idToken);
// server side code
const generateTokenAPIRequest = (idToken) => {
const { uid } = await admin.auth().verifyIdToken(idToken);
return await admin.auth().createCustomToken(uid);
};
This custom token is passed back to the Main App via a postMessage.
window.opener.postMessage({ token }, APP_URL);
window.close();
When the Main App receives the authentication message, it signs the user in with the custom token.
window.onmessage = (e: MessageEvent) => {
if (e.origin !== AUTH_URL) { return; }
const { idToken } = e.data;
if (!idToken) return;
firebase.auth().signInWithCustomToken(idToken)
};
I listen to firebase.auth().onAuthStateChanged. This function runs correctly with the new user account but does NOT run again when I refresh the page. It is as if the user is not being stored in the Main App's storage.
It appears this was caused by an unrelated bug where firebase.auth().signOut() was being called unexpectedly. However I wanted to call out a few items in case someone stumbles upon this.
Make sure your service account is the same one you are using on your authenticated app. You can generate an Admin service account through the Firebase Console.
The post message approach has problems where a hacker can grab the ID token, sign in with it and then do all the actions the users can do. Make sure you check the origin of the post message like I do above. There are additional measures that can probably be put in place. I believe this is how the typical "Sign in with Google" works.
There are approaches with sessions that have been posted on Medium. Namely https://dev.to/johncarroll/how-to-share-firebase-authentication-across-subdomains-1ka8 and https://dev.to/brianburton/cross-domain-firebase-authentication-a-simple-approach-337k
Supporting this use case is currently an open issue https://github.com/firebase/firebase-js-sdk/issues/2303
Happy coding!

firebase Stripe API failing on first payment process

I have successfully integrated stripe and firebase, using the Run Subscription Payments with Stripe.
Here's the flow:
When I click register, it brings me to the Stripe Page, using this function:
export async function createCheckoutSession(activtyStatus){
let user = firebase.auth().currentUser;
const checkoutSessionRef = firestore
.collection('customers')
.doc(userID)
.collection('checkout_sessions')
.add({
price: price,
success_url: "https://xxx/successPage",
cancel_url: "https://xxx/signin",
});
// Wait for the CheckoutSession to get attached by the extension
(await checkoutSessionRef).onSnapshot(function (snap) {
const { error, sessionId } = snap.data();
if (error) {
console.log(`An error occured: ${error.message}`);
}
if (sessionId) {
//live key
const stripe = window.Stripe('pk_livekeyxxxx');
stripe.redirectToCheckout({sessionId})
console.log("logged stripe")
}
});
}
If successful, it goes to to my SuccessPage, which just checks if the subscriptions collection was created in firebase or not.
so what happens is, if the payment is successful, it will just render /clients route, and if it isn't, it will redirect to createCheckoutSession function, which goes back to allow the customer to pay again. So when I enter the payment details on Stripe, SOMETIMES, it goes to clients and adds the subscription, and SOMETIMES, it redirects to the Stripe page, and makes me enter ti again, in which then it works.
is this a bug in my code, or is firebase and stripe just finicky?
It sounds like you might be losing your reference to your customer when they arrive at the success page, making it so that you can't inspect their new subscription.
Beyond options like authenticated sessions to load your user profile, you could consider setting up your success page to receive the Session ID, and optionally include your own customer ID. You should of course still ensure your customer is authenticated before showing any personal information, but this might help you to load the data.
Update: once you have a successful session, you can retrieve it from the API (or inspect your webhook data) to look at the subscription attribute (API ref) to find the created subscription id.

How to verify a user's ID token in firestore using Javascript?

I am building a react native application and am using Firebase, more specifically firestore, in order to manage my data. My current objective is to implement an auto login feature on my app, where if the user exits the app, I want them to stay signed in, unless they manually hit the Sign Out button before exiting the app. Here is my current process of doing this:
When the user logs into the app, I sign them in by:
firebase.auth().signInWithEmailAndPassword(email, password).
I then get their idToken by:
let authIdToken = "";
firebase
.auth()
.currentUser.getIdToken(true)
.then(function (idToken) {
authIdToken = idToken
})
.catch(function (error) {
console.log(error)
});
I then want to save this token into the phone, so when the user opens the app again, I can fetch this token and check its validity. If it is valid, then I can log the user in using their idToken. In react native, I can do this by doing:
AsyncStorage.setItem(
"userData",
JSON.stringify({
token: token,
})
);
Now when the app loads up:
const startScreen = props => {
useEffect(() => {
const tryLogin = async () => {
const userData = await AsyncStorage.getItem("userData");
const transformedData = JSON.parse(userData);
const { token } = transformedData;
await firebase
.auth()
.verifyIdToken(token, true)
.then((payload) => {
console.log(true)
})
.catch((error) => {
if (error.code == "auth/id-token-revoked") {
// Token has been revoked. Inform the user to reauthenticate or signOut() the user.
console.log("revoked")
} else {
console.log("error")
}
});
};
tryLogin();
}, []);
The Issue: When I try to verify the token this way, I am met with the following error: firebase.auth().verifyIdToken is not a function.
I read through the documentation and am unsure of how else to verify this token using JS. How do I verify it? Let me know if my verification process is incorrect and how it should be done. I am new to using firestore and doing authentication in general and hope to learn how to do it the right way.
Another helpful note: This is how I am configuring my firestore: !firebase.apps.length ? firebase.initializeApp(firebaseConfig) : {};
Thanks!
I then want to save this token into the phone, so when the user opens the app again, I can fetch this token and check its validity.
This is completely unnecessary. Firebase Auth with persist the signed in user, and automatically refresh the token without you having to do anything. All you need to do is listen to when updates to the token are made available, and act on the new token as needed. You can establish an ID token listener using onIdTokenChanged as shown in the linked API documentation:
firebase.auth().onIdTokenChanged(function(user) {
if (user) {
// User is signed in or token was refreshed.
}
});
Once you have this token, you know that the user is successfully signed in. There is nothing left to do. There is no need to use it to sign in.
Also, you can't verify the token on the frontend. The verifyIdToken method you're looking at is for the Admin SDK only, which only runs on the backend. The idea is that you get the token on the fronend, then pass it to the backend as described in the documentation for the Admin SDK. The backend uses this to securely determine if the user on the frontend is who they say they are.
Since you didn't say if you have a backend or not, dealing with this token might not be necessary at all. If you just want to know when the user is signed in (even if they are just returning to the page after being away, then you can skip everything above and just use an auth state observer. Again, Firebase Auth persists information about the user so you don't have to sign them in again. The observer will tell you when the automatic sign-in is complete, or if they are not signed in at all.

Delete user with Vue.js and Firebase

I cant remove account from firebase in vue.js. I used firebase docs.
Here is button to delete:
<template>
[...]
<div class="text-center">
<button type="button" class="btn text-white my-4" #click="$emit('deleteUser')">Delete account</button>
</div>
[...]
</template>
Here is method:
<script>
[...]
import firebase from "firebase"
import {router} from '../main'
export default {
[...]
},
methods: {
[...]
deleteUser () {
//const userRef = firebase.auth().currentUser;
this.usersRef.remove().then(function() {
// User deleted.
console.log("User deleted")
router.push('/')
}).catch(err => {
this.error = err.message
// An error happened.
console.log("User NOT deleted")
});
}
};
</script>
Someone can help? Account is still, and cant remove. Zero info in console.
If you want to delete a user existing in Firebase authentication you have two possibilities:
1/ Using the JavaScript SDK (since your app is made with Vue.js)
You call the delete() method, as follows:
const user = firebase.auth().currentUser;
user.delete()
.then(() => {
//....
})
.catch(err => {
if (err.code === "auth/requires-recent-login") {
//Re-authenticate the user and call again the Vue.js method
} else {
//....
}
})
Note however, that this method "requires the user to have recently signed in. If this requirement isn't met, ask the user to authenticate again and then call firebase.User.reauthenticateWithCredential". An error with the auth/requires-recent-login code is "thrown if the user's last sign-in time does not meet the security threshold".
So, only the logged-in user can call this method from a front-end, in order to delete his/her own account.
2/ Using the Admin SDK
You can use the Admin SDK's deleteUser() method, for example within a Cloud Function.
In this case, there is no need to have the user logged-in since this is executed in the back-end and it is therefore possible to delete any user. For example, you could have a Callable Cloud Function triggered by an admin user.

Categories