Friday, 16 April 2021

Using auth to know that a user is logged in

My problem is this:

I have an entire site that was build with react and I connect to the site through the Nodejs, all the information of the site I basically take from the server side.

For the chat it is not possible to use a server side, because you need something called listeners, for the listeners you need to configure the firebase also in React and the listeners will work, for the chat you need to allow connected users to send messages, which will not be open to everyone. To know who is connected you need to use an option called auth.

I need the auth, for the rules in firebase I will only be able to allow connected users to access chat, and not leave the rules open to everyone.

Chat works when I set rules in firebase to true, but I want only registered users to be able to use chat, I made it rules. enter image description here

I'm getting an error of:

Uncaught Error in snapshot listener: FirebaseError: Missing or insufficient permissions.

The problem is that I already connect via the server side, but need to somehow connect through the react as well, I also need to connect via react to see who is connected, and only for those who are connected I want to allow chat to be used. Need to connect via react, because only through react I manage to get all the functionality that firebase has to offer. The auth can only be obtained if you connect to firebase directly through react, but it requires that in react I will also connect for the chat. (Need listeners, because this way a user can immediately receive a message sent to him by someone, and need auth to allow only registered users access to chat)

To do that I need to connect via react, and use something called auth, to see who is connected. But the whole site itself is built around logging in through the nodejs, and in nodejs it is not possible to use auth. The nodejs can not keep track of auth, the nodejs only returns information in the end in JSON.

I do not know how to enable in react the connection with the auth, because of what I said, transfer all the connection to react, will drag to fill changes on the site, but login through the nodejs does not allow the use of auth, because it is a server side, my server side returns only JSON data and not functionality Beyond that like auth.

I was thinking of doing login times, both through the react and through nodejs, but then I'm afraid it will not work, because it might create 2 different tokens and only allow one of them to work.

I have tried to do other solutions like privateRoute, but still it does not help because one has to somehow get the auth, which allows to know who is connected. I would love if someone could help me if this or offer me a solution, I think there should be something that can be done with it and solve the problem.

How do I tell a firebase that a registered user wants to access chat?

Code of what I have done so far:

Login via nodejs


app.post('/login', login);


exports.login = (req, res) => {
  const user = {
    email: req.body.email,
    password: req.body.password,
  };

  const { valid, errors } = validateLoginData(user);

  if (!valid) return res.status(400).json(errors);

  firebase
    .auth()
    .signInWithEmailAndPassword(user.email, user.password)
    .then((data) => {
      console.log(JSON.stringify(data));
      return data.user.getIdToken();
    })
    .then((token) => {
      db.collection('users')
        .where("email", "==", user.email)
        .get()
        .then((data) => {
          data.forEach((doc) => {
            let userHandle = doc.data().handle;
            db.collection("users")
              .doc(userHandle)
              .get()
              .then((doc) => {
                let user = doc.data();
                db.collection('users').doc(userHandle)
                  .set(user)
                  .then(() => {
                    return res.json({ token: token});
                  })
                  .catch((err) => {
                    console.error(err);
                    return res.status(500).json({ error: err.code });
                  });

              }).catch((error) => {
                console.error(error);
              });


          });
        })
        .catch((err) => {
          console.error(err);
          res.status(500).json({ error: err.code });
        });
    })
    .catch((err) => {
      console.error(err);
      return res
        .status(403)
        .json({ general: "Wrong credentials, please try again" });
    });
};

Using the nodejs login function

export const loginUser = (userData, history) => (dispatch) => {
//userData contains mail and password  
axios
    .post('/login', userData)
    .then((res) => {
      setAuthorizationHeader(res.data.token);
      //maybe add the auth here somehow
      dispatch(getUserData());
      dispatch
        ({
          type: LOGIN_USER,
          payload: res.data.handle
        });

      history.push('/');
    })
    .catch((err) => {
      dispatch({
        type: SET_ERRORS,
        payload: err.response.data
      });
    });
};

PrivateRoute in react

import React from 'react';
import { Route, Redirect } from 'react-router-dom';


const PrivateRoute = ({component: Component, ...rest}) => {
  return(
    <Route {...rest} component={(props) => {
        const user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : null;

        if(user){
            return <Component {...props} />
        }else{
            return <Redirect to={`/login`} />
        }

    }} />
   )

 }

export default PrivateRoute

The authentication in react to connect, but not chat, chat I'm not sure how to do

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import './App.css';
import MuiThemeProvider from '@material-ui/core/styles/MuiThemeProvider';
import createMuiTheme from '@material-ui/core/styles/createMuiTheme';
import jwtDecode from 'jwt-decode';
// Redux
import { Provider } from 'react-redux';
import store from './redux/store';
import { SET_AUTHENTICATED } from './redux/types';
import { logoutUser, getUserData } from './redux/actions/userActions';
// Components
import Navbar from './components/layout/Navbar';
import themeObject from './util/theme';
import AuthRoute from './util/AuthRoute';
import PrivateRoute from './util/PrivateRoute';

// Pages
import home from './pages/home';
import login from './pages/login';
import signup from './pages/signup';
import chat from './pages/chat';

import axios from 'axios';

const theme = createMuiTheme(themeObject);
axios.defaults.baseURL =
  'https://europe-west1-projectdemourl-b123c.cloudfunctions.net/api';

const token = localStorage.FBIdToken;
if (token) {
  const decodedToken = jwtDecode(token);
  if (decodedToken.exp * 1000 < Date.now()) {
    store.dispatch(logoutUser());
    window.location.href = '/login';
  } else {
    store.dispatch({ type: SET_AUTHENTICATED });
    axios.defaults.headers.common['Authorization'] = token;
    store.dispatch(getUserData());
  }
}

class App extends Component {
  render() {
    return (
      <MuiThemeProvider theme={theme}>
        <Provider store={store}>
          <Router>
            <Navbar />
            <div className="container">
              <Switch>
                <Route exact path="/" component={home} />
                <AuthRoute exact path="/login" component={login} />
                <AuthRoute exact path="/signup" component={signup} />
                <PrivateRoute path="/chat" exact component={chat} />
              </Switch>
            </div>
          </Router>
        </Provider>
      </MuiThemeProvider>
    );
  }
}

export default App;

Login to firebase, client side

import firebase from "firebase/app";
import "firebase/firestore";

const firebaseConfig = {
    apiKey: process.env.REACT_APP_FIREBASE_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID,
    appId: process.env.REACT_APP_FIREBASE_APP_ID,
    measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID
};

firebase.initializeApp(firebaseConfig);
export default firebase;

The functions I use for chat, in these functions I use firebase


export const getRealtimeUsers = (handle) => {

    return async (dispatch) => {
        const db = firebase.firestore();
        const unsubscribe = db.collection("users")
            .onSnapshot((querySnapshot) => {
                db.collection('friends')
                    .get()
                    .then((data) => {
                        let friends = [];
                        data.forEach((doc) => {
                            if (doc.data().isFriends) {
                                if (doc.data().userHandleReceive == handle) {
                                    friends.push(doc.data().userHandleSend);
                                }
                                else {
                                    friends.push(doc.data().userHandleReceive);
                                }
                            }
                        });
                        const users = [];
                        querySnapshot.forEach(function (doc) {
                            if (doc.data().handle != handle && (friends.indexOf(doc.data().handle) > -1) ) {
                                users.push(doc.data());
                            }
                        });
                    })
                    .catch((err) => {
                        console.error(err);
                    });
            });
        return unsubscribe;
    }
}

export const updateMessage = (msgObj) => {

    return async dispatch => {

        const db = firebase.firestore();
        db.collection('conversations')
            .add({
                ...msgObj,
                isView: false,
                createdAt: new Date()
            })
            .then((data) => {
                console.log(data);
            })
            .catch((error) => {
                console.error(error);
            });

    }
}



export const getRealtimeConversations = (user) => {
    return async dispatch => {

        const db = firebase.firestore();
        db.collection('conversations')
            .where('user_uid_1', 'in', [user.uid_1, user.uid_2])
            .orderBy('createdAt', 'asc')
            .onSnapshot((querySnapshot) => {

                const conversations = [];

                querySnapshot.forEach(doc => {
                    if (
                        (doc.data().user_uid_1 == user.uid_1 && doc.data().user_uid_2 == user.uid_2)
                        ||
                        (doc.data().user_uid_1 == user.uid_2 && doc.data().user_uid_2 == user.uid_1)
                    ) {
                        conversations.push(doc.data())
                    }

                });

                dispatch({
                    type: userConstants.GET_REALTIME_MESSAGES,
                    payload: { conversations }
                })
            })
    }
}

This is the main part of the code, another note I need to add the loginUser function in react option to auth

I want to clarify, I create a user authenticated via Nodejs, but React not see it authenticated.

I'm just missing something here, the problem is very very small, but I can not figure out where it is, and how to fix it



from Using auth to know that a user is logged in

No comments:

Post a Comment