What is causing the error "Objects are not valid as a react child" in React google OAuth? - node.js

Building my first Node/React ptoject and have followed a couple of guides for including Google Auth. In each attaempt, I am getting the error "Objects are not valid as a react child" but no further detail.
I've tried a the fixes that I could find when searching the error, but no joy. What am I missing that is causing this error? thanks :)
App.Js
import React from "react";
import { GoogleLogin } from "#react-oauth/google";
async function App() {
const responseMessage = (response) => {
console.log(response);
};
const errorMessage = (error) => {
console.log(error);
};
return (
<div>
<h2>React Google Login</h2>
<br />
<br />
<GoogleLogin onSuccess={responseMessage} onError={errorMessage} />
</div>
);
}
export default App;
Index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { GoogleOAuthProvider } from '#react-oauth/google';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<GoogleOAuthProvider clientId="CLIENT-ID">
<App />
</GoogleOAuthProvider>
</React.StrictMode>
);
reportWebVitals();

Related

How to stay logged in even after page refresh?

I am developing a fullstack app using React & Node.
The following is the home screen.
After a user logs in, the name of the user is displayed in the navbar and the response from the server (including the JWT) is saved in the local storage as shown in the following pictures:
Now, when I refresh the page the user is logged out. This should not happen because I am sending the token on every request header using axios global defaults as shown in the code snippet below:
frontend/src/App.js
import React from "react";
import { Container } from "react-bootstrap";
import Header from "./components/Header";
import Footer from "./components/Footer";
import Products from "./components/Products";
import { Route, Switch } from "react-router-dom";
import SingleProductView from "./components/SingleProductView";
import Cart from "./components/Cart";
import LoginForm from "./components/LoginForm";
import RegisterForm from "./components/RegisterForm";
import Profile from "./components/Profile";
import Shipping from "./components/Shipping";
import PaymentMethod from "./components/PaymentMethod";
import PlaceOrder from "./components/PlaceOrder";
import axios from "axios";
const {token} = localStorage.getItem("loggedInUser");
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
const App = () => {
return (
<>
<Header />
<main className="py-3">
<Container>
<Switch>
<Route path="/placeorder" component={PlaceOrder} />
<Route path="/payment" component={PaymentMethod} />
<Route path="/shipping" component={Shipping} />
<Route path="/profile" component={Profile} />
<Route path="/register" component={RegisterForm} />
<Route path="/login" component={LoginForm} />
<Route path="/product/:id" component={SingleProductView} />
<Route path="/cart/:id?" component={Cart} />
<Route path="/" component={Products} exact />
</Switch>
</Container>
</main>
<Footer />
</>
);
};
export default App;
What am I doing wrong? I want the user to stay logged in even after page refresh.
Axios will lose its custom (provided by you) default headers settings after page is refreshed.
So, you need to set it again after page refresh (i.e. at App Start). You can do it in a component which always gets mounted before other components do:
In most cases, App component would be the perfect place for that:
// App.jsx
useEffect(() => {
const { token } = localStorage.getItem("loggedInUser");
if (token) {
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
}
}, [])
Assuming, you are setting the Token for the first time in localStorage after the Authentication is successful. E.g. in Login component.
Another option could be to write a Request Interceptor for Axios and check if this header exists or not; if not, then set it back from localstorage and let the request proceed.
Using Request Interceptor:
axios.interceptors.request.use(
function (config) {
if (!config.headers.Authorization) {
const { token } = localStorage.getItem('loggedInUser')
if (token) {
axios.defaults.headers.common['Authorization'] = `Bearer ${token}` // this will be used in next requests
config.headers.Authorization = `Bearer ${token}` // this is for current request
}
}
return config
},
function (error) {
return Promise.reject(error)
}
)
Edit:
You are passing token to each AsyncThunkAction. So, all you need to do is initialize the redux state properly. So, in the loginSlice:
function getToken() {
const { token } = localStorage.getItem("loggedInUser");
if (token) {
return { token };
}
return null;
}
const initialState = {
status: "idle",
user: getToken(),
error: null,
};

Getting a different port ERROR in the console while uploading image from the frontend using react js to the Mongo db

I am trying to upload image from my frontend to the mongodb database. Everything works fine with the POSTMAN but whenever I try to upload image from the front end I'm getting a wrong port error in my console log. ( I'm trying to upload the image to the port number 3002 but getting API routing error where the port is showing in 9000. My front end is running on the port 9000 and the backend in the 3002.
This is the file using which i tried to upload the image.
import { Avatar, Input } from '#material-ui/core'
import { SettingsInputAntenna } from '#material-ui/icons'
import axios from 'axios'
import React, { useState } from 'react'
import { useStateValue } from '../StateProvider'
import './MessageSender.css'
import FormData from 'form-data'
const MessageSender = () => {
const [input,setInput]=useState('')
const [imageUrl,setImageUrl]=useState('')
const [image,setImage]=useState(null)
const [{user},dispatch]=useStateValue()
const handleChange=(e)=>{
if(e.target.files[0]){
setImage(e.target.files[0]);
}
}
const handleSubmit=async(e)=>{
e.preventDefault()
if(image){
const imgForm=new FormData()
imgForm.append('file',image,image.name)
axios.post('/upload/image',imgForm,{
headers:{
'accept':'application/json',
'Accept-language':'en-US,en;q=0.8',
'Content-Type':`multipart/form-data; boundary=${imgForm._boundary}`,
}
}).then((res)=>{
console.log(res.data)
const postData={
text: input,
imgName: res.data.filename,
user:user.displayName,
avatar:user.photoURL,
timestamp: Date.now()
}
console.log(postData)
savePost(postData)
}).catch(err=>{
console.log(err);
})
}
else{
const postData ={
text: input,
user: user.displayName,
avatar : user.photoURL,
timestamp: Date.now()
}
console.log(postData)
savePost(postData)
}
setImageUrl('')
setInput('')
setImage(null)
}
const savePost=async(postData)=>{
await axios.post('/upload/post',postData)
.then((res)=>{
console.log(res)
})
}
return (
<div className='messageSender'>
<div className='messageSender_top'>
<div className="avatar_post_class">
<Avatar src={user.photoURL}/>
</div>
<form>
<div className="post_text">
<input type="text" className='messegeSender_input' placeholder="Whats`s up with you ?" value={input} onChange = {(e)=>setInput(e.target.value)}/>
</div>
<div className="file_selection">
<input type="file" id="fileselectionid" className='messeageSender_fileSelector' onChange= {handleChange}/>
</div>
<div className="post_button">
<button onClick={handleSubmit} type='Submit' id="postbutton"> Post </button>
</div>
</form>
</div>
</div>
)
}
export default MessageSender
Axios.js
import axios from 'axios'
const instance=axios.create({
baseURL: 'http://localhost:3002'
})
export default instance
axios.get('http://localhost:3002/upload/image')
You aren't uploading to port 3002 because you're importing axios not ./axios.js
import axios from 'axios'
should be
import axios from '<path to axios.js>/axios'

Protected Route With Firebase

I used Firebase for User Auth in my Next.js Application. I want to protect a user on the client-side. I used firebase firebaseui react-firebaseUI to implement Google Auth. How to secure a Route in the Client-side itself
const uiConfig = {
signInFlow: "popup",
signInSuccessUrl: "/dashboard",
signInOptions: [firebase.auth.GoogleAuthProvider.PROVIDER_ID],
}
I want to protect the dashboard Route.
Thanks in advance :)
Router:
At first you'll need to maintain a router which would contain all your public and protected routes.
import React from 'react';
import {
Route,
Switch,
BrowserRouter,
} from 'react-router-dom';
import ProtectedRoute from './Protected';
import Foo from './Foo';
import Bar from './Bar';
const Routes = () => (
<BrowserRouter>
<Switch>
// public route
<Route exact path="/foo" component={Foo} />
// protected route
<ProtectedRoute exact path="/bar" component={Bar} />
// if user hits a URL apart from the above ones, render a 404 component
<Route path="*" component={NotFound} />
</Switch>
</BrowserRouter>
);
export default Routes;
Protected Route:
This is the protect route which would handle if a user should be allowed to access a protect page on your app based on it's auth status.
import React from 'react';
import { useAuthStatus } from '../hooks';
import Spinner from '../Spinner';
import UnAuthorised from '../UnAuthorised';
const ProtectedRoute = ({ component: Component }) => {
// a custom hook to keep track of user's auth status
const { loggedIn, checkingStatus } = useAuthStatus();
return (
<>
{
// display a spinner while auth status being checked
checkingStatus
? <Spinner />
: loggedIn
// if user is logged in, grant the access to the route
// note: in this example component is Bar
? <Component />
// else render an unauthorised component
// stating the reason why it cannot access the route
: <UnAuthorised />
}
</>
);
};
export default ProtectedRoute;
Firebase Auth Status:
Well, this is the custom hook to keep track of user logging in and logging out.
import { useEffect, useState } from 'react';
import { firebase } from '../config';
export const useAuthListener = (): void => {
// assume user to be logged out
const [loggedIn, setLoggedIn] = useState(false);
// keep track to display a spinner while auth status is being checked
const [checkingStatus, setCheckingStatus] = useState(true);
useEffect(() => {
// auth listener to keep track of user signing in and out
firebase.auth().onAuthStateChanged((user) => {
if (user) {
setLoggedIn(true);
}
setCheckingStatus(false);
});
}, []);
return { loggedIn, checkingStatus };
};

react authentication flow verification

I have some API in nodejs and a React App for client side. I try to create the auth system for my API/backoffice with a jwt token, I use jsonwebtoken for create and verify token on server side but I have some doubt for client side...now on login I save the token on localstorage, then with React-Router "onUpdate" I check if local storage has a token, if not I redirect to login else nothing append, then on my app I append an auth header for each ajax request.
This is my router
export const isLoggedIn = (nextState, replace) => {
console.log(localStorage.getItem('id_token'));
}
<Router history={browserHistory} onUpdate={isLoggedIn} >
<Route path="/" component={App}>
<IndexRoute component={Login.Login} />
<Route path="admin/" component={Dashboard} />
<Route path="admin/tutti" component={Users} />
</Route>
</Router>
Here I login
$.get('/login',credential, function (result) {
localStorage.setItem('id_token', result.token)
});
Generic request:
$.ajax({
url:"/api/users",
type:'GET',
contentType: "application/json",
success:function (result) {},
headers: {"x-access-token": localStorage.getItem('id_token')}
});
is this a correct way to manage the React auth flow?
my doubt is, on isLoggedIn I need to verify the token in some way?
thank you at all!
Do you know Higher Order Components?
Here is an article about HOC: https://medium.com/#franleplant/react-higher-order-components-in-depth-cf9032ee6c3e#.hb4ck2u52
React authentication flow can be written as a HOC.
For example:
import React, { Component, PropTypes } from 'react';
export default function (ComposedComponent) {
class Auth extends Component {
componentWillMount() {
const isLoggedIn = .... // your way to check if current user is logged in
if (!isLoggedIn) {
browserHistory.push('/'); // if not logged in, redirect to your login page
}
}
render() {
return <ComposedComponent {...this.props} />;
}
}
return Auth;
}
But I suggest you to use FLUX flow, such as Redux, and store your state in Redux store.
Here is my Redux implementation:
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { browserHistory } from 'react-router';
export default function (ComposedComponent) {
class Auth extends Component {
componentWillMount() {
if (!this.props.isLoggedIn) {
browserHistory.push('/login');
}
}
componentWillUpdate(nextProps) {
if (!nextProps.isLoggedIn) {
browserHistory.push('/login');
}
}
render() {
return <ComposedComponent {...this.props} />;
}
}
Auth.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
};
function mapStateToProps(state) {
return { isLoggedIn: state.userReducer.isLoggedIn };
}
return connect(mapStateToProps)(Auth);
}
Usage:
import auth from '/path/to/HOC/Auth';
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Login.Login} />
<Route path="admin/" component={auth(Dashboard)} /> // wrap components supported to be protected
<Route path="admin/tutti" component={auth(Users)} />
</Route>
</Router>

fetching URL using react-redux works but if i refresh the page, it won't work

There are two problems that Im facing currently, Im trying to do a basic GET request and it works just fine, but the problem lies when I refresh the page
For your information I'm doing a react-redux server rendering.
The url that the react-redux trying to consume is '/courses'
The Problems
1) '/courses' is the url for the server and for fetching in react-redux, and it works perfectly fine, in terms of navigation. For example from '/' (home page) to '/courses' page
it shows all the json data perfectly but when I refresh the page, it will show all the json data
Example
Home page
Courses Page
it shows perfectly but when I refresh the page, it shows all the json data not from the react-redux.
I know you guys will say that just change the URL from '/courses' -> '/api/courses' I did that and I get my second problem
2) Which is
ReferenceError: fetch is not defined, whenever I refresh the page
Here's the code
Routes
import React from 'react';
import { IndexRoute, Route } from 'react-router';
import App from './components/App';
import Home from './components/Home';
import Course from './components/Course';
export default function getRoutes(store) {
return (
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path='/courses' component={Course} />
</Route>
);
}
Action
export function getCourses() {
return (dispatch) => {
return fetch('/courses', {
method: 'get',
headers: { 'Content-Type': 'application/json' },
}).then((response) => {
if (response.ok) {
return response.json().then((json) => {
dispatch({
type: 'GET_COURSES',
courses: json.courses
});
})
}
});
}
}
Main.js
import 'whatwg-fetch';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router, browserHistory } from 'react-router';
import configureStore from './store/configureStore';
import getRoutes from './routes';
const store = configureStore(window.INITIAL_STATE);
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory} routes={ getRoutes(store)} />
</Provider>,
document.getElementById('app')
)
And after that I just dispatch the action to the component which is Course.js which will render the component.
I'm really new to react-redux, and hope you guys could help me

Resources