How can i test 'auth().signInWithCredential' in firebase/app? - firebase

I am writing test code using firebase/auth and google login with react-native
But, there are always bug in my test code
my UI component is below.
import { GoogleSignin } from "#react-native-google-signin/google-signin";
import auth from "#react-native-firebase/auth";
let userInfo;
try {
userInfo = await GoogleSignin.signIn();
} catch (error) {
console.error(error);
}
const googleCredential = auth.GoogleAuthProvider.credential(userInfo.idToken);
let testPromise;
try {
testPromise = await auth().signInWithCredential(googleCredential);
} catch (error) {
// In real code environment, there are no problem.
// But, In my test environment, the engine of the code always occur error in here
console.error(error);
}
The error message is below.
console.error
TypeError: (0 , _auth.default) is not a function
my mocking part of the test code is below
jest.mock("#react-native-firebase/auth", () => {
return {
GoogleAuthProvider: {
credential: jest.fn().mockReturnValue({ providerId: "fakeProviderId", secret: "fakeSecret", token: "fakeToken" }),
},
signInWithCredential: jest.fn(),
};
});

some tricks are in there.
i think the way i suggests is not best practice.
but, you can do this problem like this.
In react component...
import auth from "#react-native-firebase/auth";
import { firebase } from "#react-native-firebase/auth"; << KEY POINT
test component...
const mockedFbAuth = jest.fn();
jest.mock("#react-native-firebase/auth", () => {
return {
firebase: {
auth: jest.fn().mockImplementation(() => ({
signInWithCredential: mockedFbAuth.mockReturnValue({
additionalUserInfo: {
profile: {
email: "fakeCredentialEmail",
name: "fakeCredentialName",
},
},
}),
})),
},
};
});
above this way, i can solve my problem!

Related

How to get stripe customers in next js

I am using Stripe in my NextJs project and have tried to get customers list in my app but have not succeeded. If anyone knows how to get it, please instruct me on how to do that.
This is my code:
import { loadStripe } from "#stripe/stripe-js";
async function getStripeCustomers(){
const stripe = await loadStripe(
process.env.key
);
if (stripe) {
// there was a toturail for node.js like this.
console.log(stripe.customers.list())
}
}
useEffect(() => {
getStripeCustomers()
}, []);
I think you should do this logic in backend so create a route in api folder then try this code.
// api/payment/get-all-customers.js
import Stripe from "stripe";
export default async function handler(req, res) {
if (req.method === "POST") {
const { token } = JSON.parse(req.body);
if (!token) {
return res.status(403).json({ msg: "Forbidden" });
}
const stripe = new Stripe(process.env.NEXT_PUBLIC_STRIPE_SECRET, {
apiVersion: "2020-08-27",
});
try {
const customers = await stripe.customers.list(); // returns all customers sorted by createdDate
res.status(200).json(customers);
} catch (err) {
console.log(err);
res.status(500).json({ error: true });
}
}
}
Now from frontend send a POST request to newly created route.

Storing Firebase accessToken in Pinia store - Vue3

I'm fairly new to Vue and it's my first time using Pinia. I'm following this guide to set up Firebase, Pinia and Axios. The app I'm building uses FirebaseUI to sign a user in, via an email link - this all happens in the LoginPage component below:
(Please ignore all incorrectly types variables/functions - I'm just trying to get this working in the first place)
<script setup lang="ts">
import { onMounted } from "vue";
import { EmailAuthProvider } from "firebase/auth";
import { auth } from "firebaseui";
import { auth as firebaseAuth } from "../firebase/config";
import { useUserStore } from "../stores/user"
onMounted(async () => {
const uiConfig: auth.Config = {
signInSuccessUrl: "/",
signInOptions: [
{
provider: EmailAuthProvider.PROVIDER_ID,
signInMethod: EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD,
forceSameDevice: true,
},
],
callbacks: {
signInSuccessWithAuthResult: function (authResult) {
const store = useUserStore();
store.user = authResult;
return true;
},
},
};
const ui = new auth.AuthUI(firebaseAuth);
ui.start("#firebaseui-auth-container", uiConfig);
});
</script>
<template>
<div id="firebaseui-auth-container"></div>
</template>
When the user successfully signs in, the app updates the Pinia store user object, with the AuthResult return object from the signInSuccessWithAuthResult function. When debugger, I can see that the object being stored looks like the following:
{
additionalUserInfo: {...}
operationType: "signIn"
user: {
accessToken: "eyJhbGciOiJSUzI1N..."
auth: {...}
displayName: null
...
}
}
I.e. the accessToken is being stored. The user store is below:
import { defineStore } from 'pinia'
export const useUserStore = defineStore("userStore", {
state: () => ({
user: null as any
}),
getters: {
getUser(state) {
return state.user
}
}
})
In the app I have set up an axios interceptor, that appends the accessToken to any Axios request made by the app:
axiosInstance.interceptors.request.use((config) => {
const userStore = useUserStore();
if (userStore) {
debugger;
// accessToken is undefined
config.headers.Authorization = 'Bearer ' + userStore.user.user.accessToken;
}
return config;
});
When attempting the retrieve the accessToken from the user store at this point, it's gone. Most (if not all) of the other properties from the user object still exist, but not the access token, therefore I'm pretty sure I'm using the store correctly:
{
additionalUserInfo: {...}
credential: null
operationType: "signIn"
user: {
// accessToken is gone
apiKey: "..."
appName: "[DEFAULT]"
email: "..."
emailVerified: true
....
}
}
Can anybody explain where I'm going wrong with this, and why the accessToken is being removed from the store? It looks to me as though I'm using the Pinia store correctly, and I'm pretty sure that the interceptor is also correct. However it's likely that I'm going about storing the access token in the wrong way. I'd appreciate any help/advice about how to setup Firebase authentication correctly with Vue.
Edited to include value of the user store when debugging inside the interceptor.
It looks like accessToken might be in userStore.user.user.accessToken?
Im just finishing the same battle that you are in... IMO there are many ways that this setup can be configured... This is similar to why you might use callbacks in one place, and async await in another it depends on your project structure.
Heres a simple example that might help you clarify it.
first
create a firebase file to hold the config put this where ever your organization habits tells you to put it. Just remember so we can use it later.
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: "",
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
//initialize firebase auth
export const auth = getAuth(app);
Second - userStore
The user store does the legwork. We will use the actions when we want to interact with userauth from our ui.
import {
createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { auth } from "../firebase"; // the file we made above
import router from "../router";
export const useUserStore = defineStore("userStore", {
state: () => ({
userData: null,
loadingUser: false,
loadingSession: false,
}),
actions: {
async registerUser(email, password) {
this.loadingUser = true;
try {
const { user } = await createUserWithEmailAndPassword(
auth,
email,
password
);
this.userData = { email: user.email, uid: user.uid };
router.push("/");
} catch (error) {
console.log(error);
} finally {
this.loadingUser = false;
}
},
async loginUser(email, password) {
this.loadingUser = true;
try {
const { user } = await signInWithEmailAndPassword(
auth,
email,
password
);
this.userData = { email: user.email, uid: user.uid };
router.push("/");
} catch (error) {
console.log(error);
} finally {
this.loadingUser = false;
}
},
async logOutUser() {
try {
await signOut(auth);
this.userData = null;
router.push("/login");
} catch (error) {
console.log(error);
}
},
currentUser() {
return new Promise((resolve, reject) => {
const unsuscribe = onAuthStateChanged(
auth,
(user) => {
if (user) {
this.userData = { email: user.email, password: user.password };
} else {
this.userData = null;
}
resolve(user);
},
(e) => reject(e)
);
unsuscribe();
});
},
},
});
*** step3 setup the login / reg components in vue. ***
<div>
<form #submit.prevent="login">
<label>
Email:
<input type="email" v-model="email" required />
</label>
<br />
<label>
Password:
<input type="password" v-model="password" required />
</label>
<br />
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import { useUserStore } from "../stores/user";
export default {
data() {
return {
email: "",
password: "",
};
},
methods: {
async login() {
try {
await this.userStore.loginUser(this.email, this.password); //
} catch (error) {
console.error(error);
}
},
},
// because of below setup you can access this.userStore() singleton
setup() {
const userStore = useUserStore();
return {
userStore,
};
},
};
</script>
register is going to be simailar
<div>
<form #submit.prevent="register">
<label>
Email:
<input type="email" v-model="email" required />
</label>
<br />
<label>
Password:
<input type="password" v-model="password" required />
</label>
<br />
<button type="submit">Register</button>
</form>
</div>
</template>
<script>
import { useUserStore } from "../stores/user";
export default {
data() {
return {
email: "",
password: "",
};
},
methods: {
async register() {
try {
await this.userStore.registerUser(this.email, this.password);
} catch (error) {
console.error(error);
}
},
},
setup() {
const userStore = useUserStore();
return {
userStore,
};
},
};
</script>
now whenever you want to access the user it is in userStore.userData
if you dont have the userStore up yet just use the useUserStore() method and access it the same way you do from the setup in login / register view

Firebase google authentication need to double-click to log in

I try to login into firebase with google authentication (using React hooks), and I need to click on the button twice. I want the user to get into the homepage after the first click. What is wrong here?
googleLogin.js
import firebase from "firebase/app";
import { auth,app } from "../../../../config/firebase";
export const googleLogin = async (e, information, setInformation, signup, login, history) => {
var provider = new firebase.auth.GoogleAuthProvider();
firebase
.auth()
.signInWithPopup(provider)
.then( resp => {
let {user, credential,additionalUserInfo: userInfo} = resp;
let token = credential.accessToken;
if (userInfo.isNewUser) signupWithGoogle(user, credential, userInfo);
})
.then(()=>{
history.push('/')
})
.catch((error) => {
...
});
};
const signupWithGoogle = (user, credential, userInfo)=>{
app.firestore().collection('users').doc(user.uid).set({
firstName: userInfo.profile.given_name,
lastName: userInfo.profile.family_name});
const batch = app.firestore().batch();
const initData = [
{ Applied: { positionIds: [], title: 'Applied' } },
{ Contract: { positionIds: [], title: 'Contract' } },
{ Denied: { positionIds: [], title: 'Denied' } },
{ InProgress: { positionIds: [], title: 'In Progress' } },
{ ReceivedTask: { positionIds: [], title: 'Received Task' } },
];
initData.forEach((doc) => {
const docRef = app
.firestore()
.collection('users')
.doc( user.uid)
.collection('columns')
.doc(Object.keys(doc)[0]);
batch.set(docRef, Object.values(doc)[0]);
});
return batch.commit();
}
I had the same problem to login and the problem was in my HTLM code.
I was using a form.
To resolve it, I used a simpler code in HTLM.

How to store firebase token in Vuex store

I'm trying to store the accessToken that comes back from Firebase into the Vuex store in my Vue project when I run the code I get the following error:
Uncaught TypeError: Cannot read property '$store' of undefined
It looks like it's not picking up store.js in the code below, any ideas why that might be?
Thanks
Login.vue
mounted: function() {
Firebase.auth.onAuthStateChanged( user => {
if (user) {
user.getIdToken().then(function(idToken) {
this.$store.commit('setStoreToken', idToken)
return idToken
});
}
else {
.
.
.
}
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
loggedIn: false,
accessToken: '',
},
mutations: {
loggedIn () {
this.state.loggedIn = true
},
loggedOut () {
this.state.loggedIn = false
},
setStoreToken(state, accessToken) {
state.accessToken = accessToken
},
},
getters: {
getAccessToken: state => {
return state.accessToken
}
},
})
main.js
import store from './store.js'
new Vue({
store,
router,
render: h => h(App),
}).$mount('#app')
My guess:
user.getIdToken().then(function(idToken) {
this.$store.commit('setStoreToken', idToken)
return idToken
});
You dont use an arrow function, that means that this is binded to the function itself you can either use an arrow function or you create a variable infront of it like this const self = this
user.getIdToken().then((idToken)=>{
this.$store.commit('setStoreToken', idToken)
return idToken
});
Or you do it like this:
const self = this;
user.getIdToken().then(function(idToken) {
self.$store.commit('setStoreToken', idToken)
return idToken
});

Can't sign in with correct Email address and Password with Firebase

I create one web App for graduation research (developed with Vue.js, vue-router). I'm using Firebase Authentication to sign in. Even though using the correct Email Address and password, I can't sign in and the site redirect from 'localhost:8080/signin' to 'localhost:8080/signin?' .
This is developed with Vue(2.6.10) and firebase.
(ellipsis)
input(type="text" placeholder="your#email.com" v-model="email")#MailAddress
(ellipsis)
input(type="password" placeholder="password" v-model="password")#Password
(ellipsis)
import firebase from "firebase";
export default {
name: "Signin",
data() {
return {
email: "",
password: ""
};
},
methods: {
signIn() {
firebase
.auth()
.signInWithEmailAndPassword(this.email, this.password)
.then(
() => {
alert("Success");
this.$router.push("/");
},
err => {
alert(err.message);
}
);
}
}
};
I expect to redirect to 'localhost:8080/'
This works for me.
In my Vue component:
import firebase from '../database';
async signIn () {
let result = await firebase.signIn(this.email, this.password);
if (result.message) {
this.error = result.message;
} else {
// Go to your route
}
}
In my database file:
const database = firebase.initializeApp(config);
database.signIn = async (email, password) => {
try {
await firebase.auth().signInWithEmailAndPassword(email, password);
return true;
} catch (error) {
return error;
}
};

Resources