Passport Github Sign In MERN App (Managing Auth in Redux )

Viewed 254

I'm trying to build a simple CRUD app where user can Sign In only through Github Social Login(O auth).

After looking at some tutorial & the official documentation I was able to set up the backend and frontend. To summarise, when user clicks the href in the login page user goes into the Oauth flow and when successful, the App reloads allowing it to fetch the user info from the server and dispatch the auth info in the store. From this point on, user is authenticated and can move freely around the app (can access ProtectedRoutes).

So everything seems to work fine until I do a manual refresh. If I do a manual refresh, I think the store loses the auth state and thus the user gets redirected to the login page (because of protected routes). But since the app quickly fetch back the user state, it gets pushed back to the home (see Login.js).

So to summarize manual refresh redirects to login -> push to home so everytime i see a glimpse of the login page. I'm not so worried about seeing the login page but whenever i try refresh or manually type a url other than home such as '/profile' it always kicks me back to the home page. This is to be expected because I always push to "/" when user gets authenticated.

I tried using history.goBack() but it doesn't take me to the desired page. How can I solve this issue? Is this the wrong way to do authentication?

  • BACKEND

authRoutes.js

  app.get('/auth/github', passport.authenticate('github'  ))
    app.get('/auth/github/callback', passport.authenticate('github', { failureRedirect: '/' }),
        (req, res) => {
            ... redirectLogic
  });

 app.get('/api/current_user', (req, res) => {
        res.send(req.user)
    })

passport.js

passport.serializeUser((user, done) => {
    done(null, user.id)
})

passport.deserializeUser((id, done) => {
    User.findById(id).then((user) => {
        done(null, user);
    })
})

passport.use(new GithubStrategy({
            clientID: keys.githubClientID,
            clientSecret: keys.githubClientSecret,
            callbackURL: '/auth/github/callback',
            proxy: true
          },
          async function(accessToken, refreshToken, profile, done) {
            const existingUser = await User.findOne({githubId: profile.id})
 
             if (existingUser) { // if user already exists
                 done(null, existingUser);
 
             } else { // no user exist with current profile Id
                 const newUser = await new User({githubId:profile.id, name: profile.username}).save();
                 done(null, newUser);
             }
         }
          ));

  • FRONTEND (REACT)

To connect the auth state I'm using redux-thunk. These are some relevant files

index.js(actions)

export const fetchUser = () => async dispatch => {
  const res = await axios.get('/api/current_user')
  dispatch({ type: FETCH_USER, payload: res.data })
}

authReducer.js

import { FETCH_USER } from "../actions/types";


export default function (state = null, action) {

    switch (action.type) {
        case FETCH_USER:
            return action.payload || false
        default:
            return state
    }
}

App.js

In App.js I call the fetchUser action when app gets mounted so auth state gets dispatched to the store.

 function App() {
 useEffect(() => {
 dispatch(fetchUser())
}, [dispatch])
return (
 <div className="App">
   <ProtectedRoute path='/' exact component={Home}/>
   <ProtectedRoute path='/info' exact component={Info}/>
   <ProtectedRoute path='/profile' exact component={Profile}/>
   <Route path='/login' exact component={Login}/>
 </div>
);
}

ProtectedRoute.js

function RouteGuard(props) {
const {auth} = useSelector((state) => state.auth);
if (auth} {
 return <Route {...props} />
} else <Redirect to="/login"/>
} 

**Login.js

const Login = () => {
const history = useHistory();
const {auth} = useSelector((state) => state.auth);

//this is to make sure you can't access the login page while you're logged in and it will redirect you to the homepage when auth state changes from store
useEffect(() => {
if(auth)
history.push("/");
}
return (
 <a href="auth/github">
  <button type="button" className="header-btn login-btn">
    <span className="img-icon">
      <img src="images/github_icon.png" alt="github_icon" />
    </span>
    <span className="button-text">github</span>
  </button>
</a>;
)
}

1 Answers

I had the same problem when I login from the front end it works will but when I chick if the user is authenticated it return no. So I solved by : identifying cors in my backend server and setting the credentials to true and identifying the origin which was my react route.

const app = express(); app.use(cors({ origin:"http://localhost:3001", credentials: true, }))

and when I make a request to the backend I set the withCredentials to true using anxious.

axios.post('http://localhost:3000/login', user, { withCredentials: true, })

I hope that helps

Related