Add authoriazation to react routers - javascript

I have defined routers in my react application. I have a 3 pages in my application. After the user successfully logs in, it is taken to the next screens. Flow is working fine. But there is a problem. When I directly enter the url for other pages in my application, it loads that page regardless of whether user logged in or not. I want to add a check on this. If user is not logged in then he must be redirected to the login page.
These are my routes
<Route path="/" component={LoginPage}/>
<Route path='app' component={App}>
<IndexRoute component={Home}/>
<Route path='/video-screen' component={VideoScreen}>
<IndexRoute component={TagList}/>
<Route path='/add' component={AddTags}/>
<Route path='/TagList' component={TagList}/>
<Redirect from='*' to='/'/>
</Route>
</Route>
</Router>
And this is my login component's method which checks the login credentials and take user to next page if login is successful
handleLoginButtonClick() {
var that = this;
let token;
var settings = {
"async": true,
"crossDomain": true,
"url": "https://www.backend.example.raccte.com/auth/login/",
"method": "POST",
"credentials": 'include',
"headers": {
"content-type": "application/x-www-form-urlencoded",
},
"data": {
"password": document.getElementById("password").value,
"username": document.getElementById("username").value
},
success:( response, textStatus, jQxhr )=> {
this.props.tokenAction(response.auth_token);
}
}
$.ajax(settings).done((response) => {
token = response.auth_token
console.log(token);
this.context.router.push('/app')
});
Updates
function authorize(){
if(window.localStorage.token == null){
browserHistory.push('/')
}
}
function getRoutes(store) {
return (
<Router>
<Route path="/" component={LoginPage}/>
<Route path='app' component={App} onEnter={this.authorize}>
<IndexRoute component={Home}/>
<Route path='/video-screen' component={VideoScreen}>
<IndexRoute component={TagList}/>
<Route path='/add' component={AddTags}/>
<Route path='/TagList' component={TagList}/>
<Redirect from='*' to='/'/>
</Route>
</Route>
</Router>
)
}
export default getRoutes;
gives me an error saying Uncaught ReferenceError: authorize is not defined

Routes have an onEnter functionality you can use for this. Let's say you have a function to authorize it inside the component containing the React router stuff. You could do something like this (some pseudo code here):
authorize() {
if (user is NOT authorized) browserHistory.push(login page)
}
<Route path="/" component={LoginPage}/>
<Route path='app' component={App} onEnter={this.authorize}/>
</Router>
That way even if they enter the URL straight into the browser URL bar, the onEnter function is still called, and if they aren't logged in it will redirect them to the login page.

Issue is u declared your authorize method in App component, you need to declare it in the file where you defined all routes, like this:
function authorize(){
console.log('hello');
}
<Router>
<Route path="/" component={LoginPage}/>
<Route path='app' component={App} onEnter={authorize}>
<IndexRoute component={Home}/>
<Route path='/video-screen' component={VideoScreen}>
<IndexRoute component={TagList}/>
<Route path='/add' component={AddTags}/>
<Route path='/TagList' component={TagList}/>
<Redirect from='*' to='/'/>
</Route>
</Route>
</Router>

I use Higher-Order Components for this check this exemple
RequireAuth
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
export default function (ComposedCmponent) {
class RequireAuth extends Component {
componentWillMount () {
//we need to check if user is authenticated before the component will mount
//here i check if the user is authenticated i'm using JWT authentification for this exemple, usually i use local storage to save the token and we can check the validation of the token
//If the user is not authenticated we redirect to /login
let validtoken = window.localStorage.getItem('id_token')
if (!this.props.isAuthenticated || !validtoken) {
this.context.router.push('/login')
}
}
componentWillUpdate (nexProps) {
// we need to the same as we did in componentWillMount
// in case component will update the use not authenticated
//If the user is not authenticated we redirect to /login
if (this.props.isAuthenticated !== nexProps.isAuthenticated) {
if (!nexProps.isAuthenticated) {
this.context.router.push('/login')
}
}
}
render () {
//now the user is authenticated we render the component passed by HOC component
return (
<ComposedCmponent {...this.props} />
)
}
}
and if i want secure a path i use my HOC RequireAuth in my router
<Route path='/' component={App}>
<Route path='/dashboard' component={requireAuth(Dashboard)} />
</Route>

Related

React Router v6 always render "/"

I'm trying to implement router in react-create-app but it always render "/" and showing Home or SignIn page. How can I solve this?
function AppRouter({ isLoggedIn, user }) {
return(
<Router>
<Routes>
<Route path="/profile" element={<Profile />} />
<Route path="/signUp" element={<SignUp />} />
{isLoggedIn
? <Route exact path={"/"} element={<Home user={user}/>} />
: <Route exact path={"/"} element={<SignIn />} />
}
</Routes>
</Router>
)
}
It seems you have a slight misunderstanding of how the HashRouter works with the UI.
import { HashRouter as Router, Route, Routes } from "react-router-dom";
import Profile from "./Profile";
import SignUp from "./SignUp";
import Home from "./Home";
export default function App() {
return (
<Router>
<Routes>
<Route path="/profile" element={<Profile />} />
<Route path="/signUp" element={<SignUp />} />
<Route path="/" element={<Home />} />
</Routes>
</Router>
);
}
The HashRouter handles routing with a URL hash value, i.e. everything after the "#" in the URL. If you are trying to render your app and access "<domain>/" instead of "<domain>/#/" the routing won't work.
For example in your running codesandbox demo, the base URL is "https://5p7hff.csb.app/". At this base URL the hash router isn't really working, and you should really be accessing "https://5p7hff.csb.app/#/" instead so the hash router is loaded and the app's internal routing can work.
From "https://5p7hff.csb.app/#/" you should be to then navigate to any of your routes, i.e. "https://5p7hff.csb.app/#/profile" and https://5p7hff.csb.app/#/signUp".
If you switch to a different router, like the BrowserRouter then the "/#/" is no longer used, the router and routes render from "/" where the app is running from. The routes would be "https://5p7hff.csb.app/", "https://5p7hff.csb.app/profile", and "https://5p7hff.csb.app/signUp".

SetState with React hooks after axios post

I'm trying to figure out how to set my user state with react hooks after an axois post request. Struggling to find a good solution. I've read the react hooks documentation and almost all examples that I find are just when the page loads. I want to setState on form submission as the user logs in.
Login.js
const handleLogin = (evt) => {
//Prevent default form submission
evt.preventDefault();
const loginDetails = {
username: username,
password: password,
}
//send login request to api
axios.post('http://localhost:5000/users/login', loginDetails)
.then((res) => {
setUser(loginDetails.username);
history.push('/');
})
.catch((err) => {
console.log('incorrect password/user');
history.push('/login');
})
}
Due to the Asynchronous behaviour the state is not being set before the redirect. I have also tried to place the redirect in a useEffect, but it constantly reloads the page, even when I past in a dependency. How to I ensure that my state is being set, passed into my navbar component as a prop before the history.push('/') takes effect.
Edit
App.js
return (
<Router history={history} >
<div className={styles.app}>
<Navbar user={user}/>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route exact path="/blog" component={Blog}/>
<Route exact path="/blog/create" component={CreateBlog}/>
<Route exact path="/blog/:id" component={ViewBlog}/>
<Route path="/demo" component={Demo}/>
<Route path='/resume' component={Resume}/>
<Route
exact path="/login"
render={(props) => (
<Login loginHandler={handleLogin}
usernameHandler={onChangeUsername}
passwordHandler={onChangePassword}/>)}
/>
<Route exact path="/register" render={(props) => (
<Register usernameHandler={onChangeUsername}
passwordHandler={onChangePassword}
handleRegister={handleRegister}/>
)}/>
</Switch>
<Footer/>
</div>
</Router>
);
}
export default App;

Double forward slash in url breaks router

This is my React router:
function redirect(nextState, replaceState) {
replaceState({ nextPathName: nextState.location.pathname }, '/')
}
const routes = (
<Route component={App}>
<Route path="/" component={Lead}/>
<Route path="*" onEnter={redirect}/>
</Route>
);
export default routes;
Every route entered except for '/' gets redirected to '/'
The problem is: if the user enters mywebsite.com//
The server will not be able to serve static assets and the website will appear without JS nor CSS
How to solve that?
Add This At The End of <Switch> before <Route path="*" >
<Redirect from="//*" to="/*" />

React-router not matching on valid route

I cannot get this react router to match on second part of a path no matter what I do.
Express server is returning index.html for any URL match (I'm not getting a GET error on any URL so I assume this is fine)
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, 'index.html'));
});
Here are some examples of routes i've tried, which should be valid rules according to react router docs
Example 1 )
<Route path="test">
<IndexRoute component={Home} />
<Route path="/login" component={Login} />
</Route>
OR
<Route path="test">
<IndexRoute component={Home} />
<Route path="/login" component={Login} />
</Route>
http://localhost:3100/test = Success > Returns home component
http://localhost:3100/test/login = Fail > gives me blank screen
Example 2 )
<Route path="/login" component={Login} />
http://localhost:3100/login = Success > Returns login component
Example 3 )
<Route path="/test/login" component={Login} />
OR
<Route path="test/login" component={Login} />
http://localhost:3100/test/login = Fail > gives me blank screen
I'm using version 2.5, Any help would be much appreciated!
** Yarn dump of react router **
react-router#^2.5.0:
version "2.8.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-2.8.1.tgz#73e9491f6ceb316d0f779829081863e378ee4ed7"
dependencies:
history "^2.1.2"
hoist-non-react-statics "^1.2.0"
invariant "^2.2.1"
loose-envify "^1.2.0"
warning "^3.0.0"
Try path="login" instead of path="/login" . The slash before path is not needed in nested routes.
<Route path="test/">
<IndexRoute component={Home} />
<Route path="login" component={Login} />
</Route>
From express documentation, app.get('/*' uses regular expression. so, '/*' work for 0 to n '/'. Try:
app.get('/.*
/.* should resolve '/' + 0 to n caractères.
Regards
I think the problem lies more with the way you are rendering your app than react-router itself, since you are not getting a route not found message but a blank screen, I suggest the following.
when you create your history object do the following
import { useRouterHistory } from 'react-router';
import createBrowserHistory from 'history/lib/createBrowserHistory';
// ========================================================
// Browser History Setup
// ========================================================
const createHistory = (basename) => {
return useRouterHistory(createBrowserHistory)({
basename: basename
})
}
const history = createHistory('/test');
Your route configuration will then look like this
<Route path="/" component={App} >
<IndexRoute component={Home} />
<Route path="login" component={Login} />
</Route>
Your App component would then look something like this
export const App extends Component = {
render() {
return(
<div class='app-container'>
{this.props.children}
</div>
)
}
}
App.propTypes = {
children: PropTypes.element.isRequired
}
export default App;
You'll then be able to access and render the component Login # /test/login

Redirecting user based on state in React

Right now, my router looks like this:
<Router history={browserHistory}>
<Route component={Provider}>
<Route path="/" component={AppPage}>
<Route path="login" component={LoginPage}/>
<Route component={LoggedinPage}>
<Route path="onboarding" component={OnboardingPage}/>
<Route component={OnboardedPage}>
<IndexRoute component={HomePage}/>
<Route path="settings" component={SettingsPage}/>
</Route>
</Route>
</Route>
</Route>
</Router>
LoggedinPage redirects to /login if the user isn't logged in and OnboardedPage redirects to /onboarding if the user hasn't completed the onboarding flow (choosing username, etc). However, I think more of these conditional redirects may be needed in the future. What's the best way to handle these conditional redirects? Is it possible to have a single component that handles all the redirects?
<Route>s accept an onEnter hook that is called when the route matches. A hook would look something like this:
function requireAuth(nextState, replace) {
if (!loggedIn()) {
replace({ pathname: 'login' });
}
}
Then use it like so:
<Route path="/" component={AppPage}>
<Route path="home" component={HomePage} onEnter={requireAuth}/>
<Route path="login" component={LoginPage}/>
</Route>
A more composable example, that lets you combine multiple checks:
function checkAuth() {
if (!loggedIn()) {
return { pathname: 'login' };
}
}
function checkOnboarding() {
if (!completedOnboarding()) {
return { pathname: 'onboarding' };
}
}
function redirect(...checks) {
return function(nextState, replace) {
let result;
checks.some(check => {
result = check();
return !!result;
});
result && replace(result);
};
}
Then combine away!
<Route path="/" component={AppPage}>
<Route path="home" component={HomePage}
onEnter={redirect(checkAuth, checkOnboarding)}/>
<Route path="login" component={LoginPage}/>
<Route path="onboarding" component={OnboardingPage}
onEnter={redirect(checkAuth)}/>
</Route>
You could have a function that checks the if the user is logged in, and just import that:
import {browserLocation} from './browser-location'
export default function checkLoggedIn() {
if (localStorage.jwt === null)
browserLocation.pushState('/login')
}

Categories

Resources