Protected Route With Firebase and Svelte - firebase

I'm trying to create a protected page, the profile page of my project. I want to throw people out if they are not logged in. I'm trying to do it as simply as possible. I find this tutorial, but is TypeScript and I couldn't get it to work. Link >
My way:
Profile page:
let auth = getAuth();
onMount(() => {
auth.onAuthStateChanged((user) => {
if (!user) {
goto('/signin');
}
});
});

The idea is to have a user store and use it with the combination of onAuthStateChanged
import authStore from '../stores/authStore';; // <~ stores.ts
import { onMount } from 'svelte';
let auth = getAuth();
onMount(() => {
//shift this method to a firebase.ts
auth.onAuthStateChanged((user) => {
if (user) {
authStore.set({
user,
});
} else {
authStore.set({
user: null,
});
}
});
});
// this block will watch changes on the store
$: {
if (!$authStore.user) {
if (browser) goto('/login');
} else {
if (browser) goto('/');
}
}

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.

How to create a reactive statement in Svelte with Firebase's onSnapshot?

I'm learning Svelte for the past 4 days and I'm trying to integrate it with Firebase.
I need to listen to a document named after the user's uid after the user logged in which I saved in a writable name userStore.
Note: My background was React and this can be done easily with useEffect
I need a way to call unsubscribe in the onDestroy statement... How can I do that?
onDestroy(() => {
unsubscribe();
});
This is my current code:
$: if ($userStore)
onSnapshot(doc(db, 'userInfo', $userStore.uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
userInfoStore.set(doc.data() as UserInfo);
}
});
I think onSnapshot() returns unsubscribe, so it should work like this
<script>
let unsubscribe
onDestroy(() => {
if(unsubscribe) unsubscribe();
});
$: if ($userStore) {
unsubscribe = onSnapshot(doc(db, 'userInfo', $userStore.uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
userInfoStore.set(doc.data() as UserInfo);
}
});
}
</script>
Is the component destroyed when the user logs out? Because the unsubcription should be called if the user logs out? I think in a component might not be the best place to handle the logic. This would be a way via a custom store
userInfoStore.js
export const userInfoStore = (() => {
const initialValue = {}
const {subscribe, set} = writable(initialValue)
let unsubSnapshot
return {
subscribe,
startListener(uid) {
unsubSnapshot = onSnapshot(doc(db, 'userInfo', uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
set(doc.data() as UserInfo);
}
});
},
stopListener() {
if(unsubSnapshot) unsubSnapshot()
}
}
})();
auth.js
onAuthStateChanged(auth, user => {
if(user) {
userStore.set(user)
userInfoStore.startListener(user.uid)
}else {
userInfoStore.stopListener()
}
})
App.svelte (main component)
Don't know how important that is or if the listener is stopped anyway when the page is closed
<script>
import {onDestroy} from 'svelte'
import {userInfoStore} './userInfoStore'
onDestroy(() => {
userInfoStore.stopListener()
});
</script>

How to refresh app when opened from deeplink?

I am struggling with a react native app. I would implement react native firebase dynamic link, but now I am a little lost. I use this method on HomeScreen which working perfectly every times when somebody opens the app.
async componentWillMount() {
try {
let url = await firebase.links().getInitialLink();
if(url) {
let api = "example.com/user/123456";
try {
this.setState({ data: "John Doe" });
this.props.navigation.navigate('Preview', {user: this.state.data })
}
catch {
}
}
}
catch {
}
}
But when the app is already opened this method doesn't work properly. Is there a way where I can trigger a function every time when somebody comes back to the opened app?
Just a tip, you should place your code in componentDidMount so that you do not block the initial (first) render.
You could use AppState to listen out for changes to apps being put in the background/foreground.
componentDidMount() {
this.showPreview();
AppState.addEventListener('change', this.onAppStateChange);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.onAppStateChange);
}
const onAppStateChange = appState => {
// You can check if appState is active/background/foreground
this.showPreview();
}
const showPreview = async (appState) => {
// You can check if appState is active/inactive/background
try {
let url = await firebase.links().getInitialLink();
if(url) {
let api = "example.com/user/123456";
try {
this.setState({ data: "John Doe" });
this.props.navigation.navigate('Preview', {user: this.state.data })
}
catch {
}
}
}
catch(e) {
console.error(e);
}
}

Redux-saga firebase onAuthStateChanged eventChannel

How to handle firebase auth state observer in redux saga?
firebase.auth().onAuthStateChanged((user) => {
});
I want to run APP_START saga when my app starts which will run firebase.auth().onAuthStateChanged observer and will run other sagas depending on the callback.
As I understand eventChannel is right way to do it. But I don't understand how to make it work with firebase.auth().onAuthStateChanged.
Can someone show how to put firebase.auth().onAuthStateChanged in to eventChannel?
You can use eventChannel. Here is an example code:
function getAuthChannel() {
if (!this.authChannel) {
this.authChannel = eventChannel(emit => {
const unsubscribe = firebase.auth().onAuthStateChanged(user => emit({ user }));
return unsubscribe;
});
}
return this.authChannel;
}
function* watchForFirebaseAuth() {
...
// This is where you wait for a callback from firebase
const channel = yield call(getAuthChannel);
const result = yield take(channel);
// result is what you pass to the emit function. In this case, it's an object like { user: { name: 'xyz' } }
...
}
When you are done, you can close the channel using this.authChannel.close().
Create your own function onAuthStateChanged() that will return a Promise
function onAuthStateChanged() {
return new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
resolve(user);
} else {
reject(new Error('Ops!'));
}
});
});
}
Then use call method to get the user synchronously
const user = yield call(onAuthStateChanged);
This could be handled in the Saga such as the following for Redux Saga Firebase:
// Redux Saga: Firebase Auth Channel
export function* firebaseAuthChannelSaga() {
try {
// Auth Channel (Events Emit On Login And Logout)
const authChannel = yield call(reduxSagaFirebase.auth.channel);
while (true) {
const { user } = yield take(authChannel);
// Check If User Exists
if (user) {
// Redux: Login Success
yield put(loginSuccess(user));
}
else {
// Redux: Logout Success
yield put(logoutSuccess());
}
}
}
catch (error) {
console.log(error);
}
};
here is how you would run the onAuthStateChanged observable using redux-saga features (mainly eventChannel)
import { eventChannel } from "redux-saga";
import { take, call } from "redux-saga/effects";
const authStateChannel = function () {
return eventChannel((emit) => {
const unsubscribe = firebase.auth().onAuthStateChanged(
(doc) => emit({ doc }),
(error) => emit({ error })
);
return unsubscribe;
});
};
export const onAuthStateChanged = function* () {
const channel = yield call(authStateChannel);
while (true) {
const { doc, error } = yield take(channel);
if (error) {
// handle error
} else {
if (doc) {
// user has signed in, use `doc.toJSON()` to check
} else {
// user has signed out
}
}
}
};
please note that other solutions that don't utilize channel sagas are not optimal for redux-saga, because turning an observable into a promise is not a valid solution in this case since you would need to call the promise each time you anticipate a change in authentication state (like for example: taking every USER_SIGNED_IN action and calling the "promisified" observable), which will negate the whole purpose of an observable

Firebase Authentication on Ionic 2

I am trying to implement login with firebase on Ionic 2 with the following code.
export class MyApp {
rootPage:any = Login;
isAuthenticated = false;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
firebase.initializeApp({
apiKey: "AIzaSyC94rD8wXG0aRLTcG29qVGw8CFfvCK7XVQ",
authDomain: "myfirstfirebaseproject-6da6c.firebaseapp.com",
});
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.rootPage = Home;
} else {
this.rootPage = Login;
}
});
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
}
}
I realize that even when I am authenticated, I am always brought to the Login screen because it does not wait for onAuthStateChanged promise to be fulfilled and carries on with initializing the app, therefore, the Login screen instead of the Home screen is always shown.
But how should I change my code so that I can show Home when authenticated?
Remove the login from the rootPage declaration
export class MyApp {
rootPage:any;
...
}
You're setting the page to your LoginPage as the app initializes and before he can check if the user is loged.
For it to run the onAuthStateChange, when the app initializes you need to use Zone to create an observable and the run it.
import { NgZone } from '#angular/core';
zone: NgZone; // declare the zone
this.zone = new NgZone({});
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
this.zone.run(() => {
if (user) {
this.rootPage = Home;
unsubscribe();
} else {
this.rootPage = Login;
unsubscribe();
}
});
});
Hope it helps

Resources