React-router multiple component onEnter in case of auth? - javascript

I am creating one app that has 2 components for a single path. Here are my routes:
<Router history={hashHistory}>
<Route path="/" component={AppContainer} onEnter={requireEnter}>
<Route path="/homepage" component={HomePage} />
<Route component={MainPage} onEnter={requireAuth}>
<Route path="/home" component={DashBoard} />
</Route>
</Route>
</Router>
In this case I am entering into my app using the AppContainer component. Instead of that I want that component should be of my choice because I have a home page that will have path="/".
Right now onEnter={requireEnter} is handling in following cases:
function requireEnter (nextState, replace) {
if (nextState.location.pathname == "/") {
if (checkLoggedIn()) {
replace({
pathname: '/home'
})
} else {
replace({
pathname: '/homepage'
})
}
}
}
But I want that something like this:
function requireEnter (nextState, replace) {
if (nextState.location.pathname == "/") {
if (checkLoggedIn()) {
//component should be AppContainer and redirect to '/home'
} else {
//component should be home page
}
}
}

You can add a Route with no component in it, had used it in another similar case:
<Route path="/" onEnter={requireEnter}>
<Route path="home" component={AppContainer}>
<IndexRoute component={HomeContainer} />
</Route>
<Route path="homepage" component{HomePageContainer}>
</Route>
</Route>
Then as usual:
function requireEnter (nextState, replace){
if(nextState.location.pathname=="/"){
if(checkLoggedIn()){
replace({
pathname: '/home'
});
}else{
replace({
pathname: '/homepage'
});
}
}
}

Related

how do you redirect a user who is not logged in using passport authentication

trying to stop a user from accessing my /mainpage route if they arent logged and to redirect them to login.... currently using passport and react router and components... here is my app js
return (
<Router>
<div>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/mainpage" component={MainPage} />
<Footer />
</div>
</Router>
);
}
export default App;
this is the html-routes.js part im trying to make work
app.get("/mainpage", isAuthenticated, (req, res) => {
res.sendFile(path.join(__dirname, "/signup.html"));
});
If you had an endpoint to validate their authentication status, as well as some persisted value (such as isAuthenticated) stored in the UI you can set up an auth gate of sorts.
function AuthGate({ children ]} {
return(
<>
{isAuthenticated && validationCheck() ? children : <Login />}
</>
)
}
and then pass render your protected routes as such
function App() {
return(
<Router>
<div>
<Route exact path="/" component={Login} />
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={Signup} />
<AuthGate>
<Route exact path="/mainpage" component={MainPage} />
</AuthGate>
<Footer />
</Router>
)
}
this would ensure that as long as your checks to see if a user was authenticated were true, you would render the children of the AuthGate, otherwise return the Login page. Once a user logs in, as long as there is no redirect logic, they will already be on the Mainpage route and view the content as desired. Any routes not wrapped in the AuthGate would be accessible regardless of authentication status.
You can try
app.get('/mainpage', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }));
1. declare Private Route and add auth logic
// declare Private Route
type PrivateRouteProps = {
children: React.ReactNode
path: string
exact?: boolean
rest?: never
}
const PrivateRoute: React.FC<PrivateRouteProps> = (
{
children,
path,
exact,
...rest
}: PrivateRouteProps) => {
const store = useGlobalStore()
const loggedIn = store.loggedIn
console.log(store.loggedIn, '??')
return (
<Route
path={path}
exact={exact}
{...rest}
render={(props) =>
loggedIn ? (
children
) : (
<Redirect
to={{
pathname: '/login', // eslint-disable-next-line react/prop-types
state: { from: props.location },
}}
/>
)
}
/>
)
}
2. use Private Router
// use Router
const Router: React.FC = () => (
<BrowserRouter>
<Suspense fallback={<PageLoading />}>
<Switch>
<PrivateRoute path="/" exact>
<Redirect to={'/welcome'} />
</PrivateRoute>
<Route path="/login" exact>
<Login />
</Route>
<Route path="/403" exact>
<Error403 />
</Route>
<Route path="/404" exact>
<Error404 />
</Route>
<Route path="/500" exact>
<Error500 />
</Route>
<PrivateRoute path="*">
<LayoutRoutes />
</PrivateRoute>
</Switch>
</Suspense>
</BrowserRouter>
)

React-BreadCrumbs does not pick up variable from method

I have a multi-page web application running with reactjs.
I'm trying to define a custom react-breadcrumb for a specific page
which involves extracting a value and using that in the breadcrumb
Running the code with the below, I can see that console picks up jobName
But if I look at the breadcrumbs, I'm stuck with
home > templates > Missing name prop from Route
I don't quite understand why the variable isn't being picked up for the router. If I just hard code it in, it will work. Any advice?
getTemplateJobName(templateId,dateChosen){
doGetJobById({jobId: templateId,reconDate: dateChosen}).then(
({body: template})=>{
let {jobName: jobName}=template;
console.log(jobName);
return jobName;
});
},
render(){
return (
<div>
<Router history={browserHistory}>
<Route name='home' path={'/'+contextName}>
<IndexRoute component={LandingPage}/>
<Route name='templates' path='templates'>
<IndexRoute component={JobPage}/>
<Route path=':reconDate&:templateId' component={JobDetailPage} staticName={true} getDisplayName={(params) => this.getTemplateJobName(params.templateId,params.reconDate)}/>
</Route>
<Route name='report' path='report' component={ReportPanel}/>
</Route>
<Route path='*' component={NotFoundPage}/>
</Router>
Try this please;
export default App extends React.Component {
constructor(props) {
super(props);
this.state = {
DisplayName: ''
};
}
getTemplateJobName(templateId,dateChosen) {
doGetJobById({jobId: templateId,reconDate: dateChosen}).then(
({body: template})=>{
let {jobName: jobName}=template;
console.log(jobName);
// return jobName;
this.setState({ DisplayName: jobName });
}
);
}
getDisplayName(params) {
this.getTemplateJobName(params.templateId,params.reconDate);
return this.state.DisplayName;
}
render() {
return
(<div>
<Router history={browserHistory}>
<Route name='home' path={'/' + contextName}>
<IndexRoute component={LandingPage}/>
<Route name='templates' path='templates'>
<IndexRoute component={JobPage}/>
<Route path=':reconDate&:templateId' component={JobDetailPage} staticName={true} getDisplayName={ this.getDisplayName.bind(this) }/>
</Route>
<Route name='report' path='report' component={ReportPanel}/>
</Route>
<Route path='*' component={NotFoundPage}/>
</Router>
</div>)
}
}

React: Authentification and avoiding repeating code components

All of my main react components have some parts like this:
export default class ExampleMain extends Component {
constructor(props) {
super(props)
this.state = {
isAuthenticated: Meteor.userId() !== null
}
}
componentWillMount() {
if (!this.state.isAuthenticated) browserHistory.push('/login')
}
componentDidUpdate() {
if (!this.state.isAuthenticated) browserHistory.push('/login')
}
}
With this I am checking if a user is logged in. If this is false, the user will be redirected to login route.
As this part is used in many components, I was thinking if I can optimize this to get a DRY code...
Update
I am using react router:
render((
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='login' component={ Login } />
<Route path='content/:id' component={ Content } />
</Route>
<Redirect from='*' to='/' />
</Router>
), document.getElementById('root'))
You can try something like this:
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='/login' component={ Login } />
<Route path='content/:id' component={ Content } />
</Route>
<Redirect from='*' to='/' />
</Router>
And in App, using withRouter to "inject" the router inside your component:
import { withRouter } from 'react-router';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: Meteor.userId() !== null
}
}
componentWillMount() {
if (!this.state.isAuthenticated) {
this.props.router.push('/login');
}
}
}
export default withRouter(App);
Maybe this helps you. I would tried to use any hook before routing. But you always can extend your own class with such functionality like that example
function requireAuth(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
}
}
render((
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='login' component={ Login } />
<Route path='content/:id' component={ Content } onEnter={requireAuth} />
</Route>
<Redirect from='*' to='/' />
</Router>
), document.getElementById('root'))
To see full code follow link above.

Add authoriazation to react routers

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>

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