Firebase authentication: sendEmailVerification automatically verifies the user's email - javascript

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?

Related

Returning an ID Token via Googles JavaScript GSI SDK

I'm currently integrating a frontend with a 3rd party backend that offers sign-in with social media. I'm using the Google JS SDK https://accounts.google.com/gsi/client which works fine with the one-tap login as it returns an ID Token which the backend requires. However, the downside is, if the user doesn't have a Google session, the prompt won't show.
If the user isn't logged in to Google, I've managed to prompt a login form and request a token on successful login, however, it only returns an access token. Is any way to request an ID token?
Example:
google.accounts.id.prompt(async notification => {
if (notification.getNotDisplayedReason() === 'opt_out_or_no_session') {
const tokenClient = google.accounts.oauth2.initTokenClient({
client_id: googleClientId,
scope: 'openid email profile',
})
tokenClient.callback = response => {
// response only has access token and no ID Token
}
tokenClient.requestAccessToken({ prompt: 'consent' })
}
window.addEventListener('load', () => {
google.accounts.id.initialize({
client_id: googleClientId,
callback: (user: CredentialResponse) => {
const { credential: idToken } = user
// I need idToken in the above requestAccessToken too
},
})
})
})
You are initializing two different namespaces in the example you have provided. The first one initializes oauth2 namespace, which starts the authorisation flow. This flow results in the acquisition of an access_token as you've realised.
The second one initializes the id namespace, which is responsible from the authentication flow. This returns an id_token, which is what you need indeed.
To keep using authentication flow beyond the capability of one-tap log in, you can render a Sign-In with Google button using the authentication initialisation. Simple initialise google.account.id.initialize() as you would. Then call one-tap prompt.
google.accounts.id.prompt();
Then in addition to that, you can render the button:
google.accounts.id.renderButton(document.getElementById("g-btn"), {
type: "standard",
logo_alignment: "left"
});
And
<div class="google-button" id="g-btn"></div>
Regardless of how the user decides to sign-in, it'll lead to the same callback method you've defined in the initialize() call.

Getting cognito email verified directly from your app by sending verification code to Cognito

I am making an app and I am using Cognito for auth. However, When I register, the account shows up in cognito but I receive a verification code and I have to manually confirm the account. I have a screen where I let the user add the code that they received to activate their account. Can anybody help me on what call do I send to Cognito in order to activate my account without having to manually confirm?
const signup = (event) => {
event.preventDefault();
if (formValidation()) {
UserPool.signUp(username, password, [], [], (err, data) => {
if (err) console.error(err);
console.log(data);
});
}
};
This is the function I am using to sign up users. How do I confirm the user?
You can use admin confirm sign up to confirm user without entering the verification code. As this is an admin method, the call will need to be authenticated with AWS credentials that have the right to perform this action.
Link to documentation:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html#adminConfirmSignUp-property

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.

Categories