react private route not working with keycloak - javascript

I am trying to protect certain routes so only users who have authenticated can be redirected and have access to the app page but the redirect does not work, and neither does the page load. When I navigate to the localhost:7171/app page it shows a Whitelabel Error Page
App.js
import React from "react";
import { ReactKeycloakProvider } from "#react-keycloak/web";
import keycloak from "./Keycloak";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Nav from "./components/Nav";
import WelcomePage from "./pages/Homepage";
import SecuredPage from "./pages/Securedpage";
import PrivateRoute from "./helpers/PrivateRoute";
function App() {
return (
<div>
<ReactKeycloakProvider authClient={keycloak}>
<Nav />
<BrowserRouter>
<Routes>
<Route exact path="/" element={<WelcomePage />} />
<Route path="/app"
element={<PrivateRoute>
<SecuredPage />
</PrivateRoute>}/>
</Routes>
</BrowserRouter>
</ReactKeycloakProvider>
</div>
);
}
export default App;
Securedpage.js
import React from 'react';
const Secured = () => {
return (
<div>
<h1 className="text-black text-4xl">Welcome to the Protected Page.</h1>
</div>
);
};
export default Secured;
PrivateRoute.js (the log is not hit ever)
import { useKeycloak } from "#react-keycloak/web";
const PrivateRoute = ({ children }) => {
const { keycloak } = useKeycloak();
const isLoggedIn = keycloak.authenticated;
console.log("checking auth access " + isLoggedIn);
return isLoggedIn ? children : null;
};
export default PrivateRoute;
Nav.js
import React from "react";
import { useKeycloak } from "#react-keycloak/web";
const Nav = () => {
const { keycloak, initialized } = useKeycloak();
function loginHelper(){
console.log("login clicked");
keycloak.login();
};
function logoutHelper(){
console.log("login clicked");
keycloak.logout();
};
return (
<div>
<div>
<section>
<nav >
<div >
<h1>
Home Page.
</h1>
<ul>
<li>
<a href="/">
Home
</a>
</li>
{/* <li>
// <a className="hover:text-blue-800" href="/secured">
// Secured Page
// </a>
// </li>-*/}
</ul>
<div>
<div>
{!keycloak.authenticated && (
<button
type="button"
className="text-blue-800"
onClick={() => loginHelper()}
>
Login
</button>
)}
{!!keycloak.authenticated && (
<button
type="button"
className="text-blue-800"
onClick={() => logoutHelper()}
>
Logout ({keycloak.tokenParsed.preferred_username})
</button>
)}
</div>
</div>
</div>
</nav>
</section>
</div>
</div>
);
};
export default Nav;
Two problems:
After login, it does not redirect to /app page. I have added /app/* as a redirect URI in keycloak setting.
Even if I manually go to /app page, it shows an error instead of redirecting to the login page.

Related

Why my react app reloads the homepage every time I change the language?

I have two buttons on my react app's header. One for translating the application to Spanish and the other for English. The problem is that every time I press one of this buttons and update the state, the homepage gets loaded despite the translation is completed correctly. How can I avoid this redirection to the homepage and remain in the current page where the user is at that moment?
App.js, where the IntlProvider gets the language through state triggered by onclick function:
import { MsalProvider } from "#azure/msal-react";
import "bootstrap/dist/css/bootstrap.min.css";
import { IntlProvider } from "react-intl";
import { BrowserRouter } from "react-router-dom";
import "./App.css";
import AppContent from "./AppContent";
import { lang } from "./assets/lang/lang";
import Header from "./pages/common/Header";
import { useState } from "react";
function App({ msalInstance }) {
const [localeLanguage, setLocaleLanguage] = useState("en");
const onChangeLanguage = (lang) => {
setLocaleLanguage(lang);
};
return (
<MsalProvider instance={msalInstance}>
<BrowserRouter>
<IntlProvider
key={localeLanguage}
locale={localeLanguage}
messages={lang[localeLanguage]}
>
<Header onChangeLanguage={onChangeLanguage} />
<AppContent />
</IntlProvider>
</BrowserRouter>
</MsalProvider>
);
}
export default App;
Header.js, where the two buttons are:
import { useIsAuthenticated, useMsal } from "#azure/msal-react";
import { FormattedMessage } from "react-intl";
import { Link } from "react-router-dom";
import logo from "../../assets/img/softwareOne.png";
import { loginRequest } from "../../util/authConfig";
import { useIntl } from "react-intl";
export default function Header(props) {
const isAuthenticated = useIsAuthenticated();
const { instance } = useMsal();
const intl = useIntl();
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<Link className="navbar-brand" to="/">
<img src={logo} className="logo-navbar" alt="" />
</Link>
<div className="collapse navbar-collapse text-center">
<span className="navbar-text">
<FormattedMessage id="well_architected_survey" />
</span>
</div>
<div className="flex">
<button
className="btn btn-secondary"
onClick={() => {
props.onChangeLanguage("es");
}}
value="es"
>
{intl.locale === "es" ? "Español" : "Spanish"}
</button>
<button
className="btn btn-secondary m-3"
onClick={() => {
props.onChangeLanguage("en");
}}
value="en"
>
{intl.locale === "en" ? "English" : "Inglés"}
</button>
</div>
<div>
<button
className="btn btn-outline-secondary logout-header"
onClick={() =>
isAuthenticated
? instance.logoutRedirect({ postLogoutRedirectUri: "/" })
: instance.loginRedirect(loginRequest)
}
>
<FormattedMessage
id={isAuthenticated ? "logout_button" : "login_button"}
/>
</button>
</div>
</nav>
);
}
I see that the localeLanguage state is used as a React key on the IntlProvider component. When the key value changes this will necessarily remount that component and its entire React subtree (which may very well include any initial mounting effects in any rendered children components). I don't see a point of using a React key here though so it's likely safe to remove it.
function App({ msalInstance }) {
const [localeLanguage, setLocaleLanguage] = useState("en");
const onChangeLanguage = (lang) => {
setLocaleLanguage(lang);
};
return (
<MsalProvider instance={msalInstance}>
<BrowserRouter>
<IntlProvider // <-- remove key prop
locale={localeLanguage}
messages={lang[localeLanguage]}
>
<Header onChangeLanguage={onChangeLanguage} />
<AppContent />
</IntlProvider>
</BrowserRouter>
</MsalProvider>
);
}

How to link a js file in react?

I want to link a local file description.js which is in the same folder. How can I link it to this file? The purpose is to open that file as a page when the user clicks on read more button.
import React, { Component } from 'react';
import image from './images/pk.png';
//import Description from './description';
<script type="text/javascript" src="description.js"></script>
export default class Box extends Component {
render() {
let {name,code,numCode,amb,fire,pol,dispatch}=this.props;
return (
<div className='box'>
<div className='e_body'>
<img className="countryImg" src={image} alt='Fail to load'/>
<h5>{name}</h5>
<p>ISO-Code: {code}<br/>ISO-Numeric: {numCode}</p>
<a href="description.js" className='btn btn-sm btn-warning'>Read More</a>
</div>
</div>
)
}
}
first define routes in App.js
and rename your file with capital description.js -> Description.js
App.js should look like:
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
const App = ({}) => {
const navigate = useNavigate();
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Your Home Compoenent />} />
<Route path="/description" element={<Description />} />
</Routes>
</BrowserRouter>
);
};
Your component
import React, { Component } from "react";
import image from "./images/pk.png";
import { useNavigate } from "react-router-dom";
const Box = ({ name, code, numCode, amb, fire, pol, dispatch }) => {
const navigate = useNavigate();
return (
<div className="box">
<div className="e_body">
<img className="countryImg" src={image} alt="Fail to load" />
<h5>{name}</h5>
<p>
ISO-Code: {code}
<br />
ISO-Numeric: {numCode}
</p>
<button
className="btn btn-sm btn-warning"
onClick={() => {
navigate("/description");
}}
>
Read More
</button>
</div>
</div>
);
};
You have to use 'Routes' for this .
then do
import Usenavigate from react-router-dom
const navigate = useNavigate();
inside onClick callback function
navigate('/Description')

React, implementing protected route with authentication with React Router Dom [duplicate]

This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 9 months ago.
Unable to route to admin page even after successful login, displays a blank screen. On successful login system should navigate and display the admin screen. Could someone please advise why the routing is not happening ?
//login.js
import React, { useEffect, useState } from 'react';
import { useForm } from "react-hook-form";
import { useNavigate } from "react-router-dom";
const Login = () =>{
const { register, errors, handleSubmit } = useForm();
const [loginData, setLoginData] = useState("");
const [helperText, setHelperText] = useState('');
const navigate = useNavigate();
const onSubmit = (data) => {
try {
const userEmail = "dev#test.com"; // for time being just hard code data
const userPassword = "somePass123"; // for time being just hard code data
if(data.email === userEmail && data.password === userPassword ){
localStorage.setItem('loginEmail', userEmail);
setLoginData(userEmail);
navigate('/admin');
window.location.reload(true)
} else {
setHelperText("Invalid login details");
}
} catch (e){
console.log(e);
}
};
console.log(errors);
return (
<div className="wrapper">
<h3>Login</h3>
<section className="col2">
<div className='loginSection'>
<form onSubmit={handleSubmit(onSubmit)}>
<label>Email</label>
<input
type="text"
{...register("email", { required: true})}
/>
<label>Password</label>
<input
type="text"
{...register("password", { required: true})}
/>
<label>
<span className="loginValidationText">{helperText}</span>
</label>
<section className="col4">
<input type="submit" />
</section>
</form>
</div>
</section>
</div>
)
}
export default Login
//protectedRoute.js
import React from "react";
import { Route, BrowserRouter } from "react-router-dom";
export const ProtectedRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) => {
if (localStorage.getItem("loginEmail")) {
return <Component {...props} />;
} else {
return (
<>
<BrowserRouter
to={{
pathname: "/login",
state: {
from: props.location,
},
}}
/>
</>
);
}
}}
/>
);
};
//navigation.js
import React from 'react';
import { NavLink} from 'react-router-dom';
const Navigation = () => {
return (
<div className="App">
<div className="wrapper">
<div id="wrap">
<nav className="siteNavigation_nav_links">
<div className="main_links_nav">
<img className='logoimage' alt="SSS Logo" src="/images/super.png"></img>
<div className="navigationpanel">
<NavLink className="mob_link" to="/">Home</NavLink>
<NavLink className="mob_link" to="/team">Team</NavLink>
<NavLink className="mob_link" to="/login">Login</NavLink>
</div>
</div>
</nav>
</div>
</div>
</div>
)
}
export default Navigation;
//App.js
import React, { useEffect, useState } from 'react';
import { BrowserRouter, Route, Routes, Switch} from "react-router-dom";
import Navigation from './components/navigation';
import Home from "./components/home";
import Team from "./components/team";
import Admin from "./components/admin";
import Login from "./components/login";
import { ProtectedRoute } from "./components/protectedRoute";
function App() {
return (
<BrowserRouter>
<Navigation />
<Routes>
<Route path="/" element={<Home />}>
</Route>
<Route path="/team" element={<Team />}>
</Route>
<Route path="/login" element={<Login />}>
</Route>
<Route path="/admin" element={
<ProtectedRoute >
<Admin />
</ProtectedRoute>
}>
</Route>
</Routes>
</BrowserRouter>
);
}
export default App;
Change PotectedRoute.js to the code below, as what you are doing is more like what we used to do for React Router Dom v5 while you are using v6.
import { Navigate, useLocation} from "react-router-dom";
export const ProtectedRoute = ({children}) => {
let location = useLocation();
if(!localStorage.getItem("loginEmail")){
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
};
The information from useLocation passed as prop to Navigate can be used in Login so you send the user to that specific url where they were going to instead of a hard coded one (admin in your case), useful if you had multiple protected routes. Though it's not a requirement, you can remove it.
To know more about authentication in React Router Dom v6, visit this example on StackBlitz from their documentation. Don't look at the editor lint errors, it's the most complete and straightforward authentication example.

How to create list and detail view using React Router DOM?

I'm new in React and doesn't know how to create List and Detail view using React Router DOM. Some how I created it but I know this is not a good way to do it. If you look into below code, you will find it will always reload the whole page if I select another campaign. What will be the good way to do it. Please help...
App.js
<Switch>
<Fragment>
<PrivateRoute exact path="/admin/campaigns/:id" component={CampaignComponent}/>
<PrivateRoute exact path="/admin/campaigns/:id/messages" component={CampaignComponent}/>
<PrivateRoute exact path="/admin/campaigns/:id/contacts" component={CampaignComponent}/>
</Fragment>
</Switch>
Campaign.js
let layout;
switch (props.match.path) {
case '/admin/campaigns/:id':
layout = <CampaignDetailComponent props={props}/>;
break;
case '/admin/campaigns/:id/messages':
layout = <CampaignMessageListComponent props={props}/>;
break;
case '/admin/campaigns/:id/contacts':
layout = <CampaignContactListComponent props={props}/>;
break;
default:
layout = <div/>;
}
return (
<div>
<div className="col-6">
<CampaignListComponent props={props}/>
</div>
<div className="col-6">
{layout}
</div>
</div>
)
So, in the first col-6 I want to show the list of Campaigns and in second col-6 I want to render the components as per route change.
You can follow this link to see the actual working code sample that demonstrate this issue.
Issue
You have your "navigation" coupled to your page UI.
Solution
Split out the rendering of your links in the left section from the content in the right section. The rest of the changes a centered around computing paths and route structuring.
App.js
Render the campaign list container and navigation on its own route. This is so the nested links can inherit from the route path.
import React, { Component } from "react";
import { HashRouter, Redirect, Route, Switch } from "react-router-dom";
import CampaignComponent from "./Campaign/Campaign";
import CampaignListComponent from "./Campaign/Components/CampaignList";
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<h3>Sample app to demonstrate React Router issue</h3>
<HashRouter>
<div className="campaign-container">
<div className="campaign-list">
<Route path="/admin/campaigns">
<CampaignListComponent />
</Route>
</div>
<div className="campaign-detail">
<Switch>
<Route path="/admin/campaigns" component={CampaignComponent} />
<Redirect from="*" to="/admin/campaigns" />
</Switch>
</div>
</div>
</HashRouter>
</div>
);
}
}
Campaign list component
import React, { useState } from "react";
import { Link, generatePath, useRouteMatch } from "react-router-dom";
const CampaignListComponent = () => {
const [campaignList, setCampaignList] = useState([
{ id: 1, name: "Campaign1" },
{ id: 2, name: "Campaign2" },
{ id: 3, name: "Campaign3" },
{ id: 4, name: "Campaign4" },
{ id: 5, name: "Campaign5" }
]);
const { url } = useRouteMatch();
return (
<div style={{ width: "90%" }}>
{campaignList.map(({ id, name }) => (
<div className="campaign-list-item" key={id}>
<Link to={generatePath(`${url}/:id`, { id })}>{name}</Link>
</div>
))}
</div>
);
};
Campaign component
import React from "react";
import { Route, Switch, useRouteMatch } from "react-router-dom";
import CampaignMessageListComponent from "./Components/CampaignMessageList";
import CampaignDetailComponent from "./Components/CampaignDetail";
import CampaignContactListComponent from "./Components/CampaignContactList";
const CampaignComponent = () => {
const { path } = useRouteMatch();
return (
<div className="campaign-list">
<Switch>
<Route
path={`${path}/:id/messages`}
component={CampaignMessageListComponent}
/>
<Route
path={`${path}/:id/contacts`}
component={CampaignContactListComponent}
/>
<Route path={`${path}/:id`} component={CampaignDetailComponent} />
</Switch>
</div>
);
};
Content components
Contact
import React from "react";
import { useHistory } from "react-router-dom";
const CampaignContactListComponent = () => {
const history = useHistory();
return (
<div className="row">
<p>Campaign Contact List</p>
<button onClick={history.goBack}>Go Back</button>
</div>
);
};
Message
import React from "react";
import { useHistory } from "react-router-dom";
const CampaignMessageListComponent = () => {
const history = useHistory();
return (
<div className="row">
<p>Campaign Message List</p>
<button onClick={history.goBack}>Go Back</button>
</div>
);
};
Detail
import React from "react";
import { Link, useHistory, useParams, useRouteMatch } from "react-router-dom";
const CampaignDetailComponent = () => {
const { id } = useParams();
const { url } = useRouteMatch();
const history = useHistory();
return (
<div>
<h6>Campaign Detail</h6>
<p>You have selected Campaign - {id}</p>
<button>
<Link to={`${url}/messages`}>Goto Messages</Link>
</button>
<button>
<Link to={`${url}/contacts`}>Goto Contacts</Link>
</button>
<button onClick={history.goBack}>Go Back</button>
</div>
);
};

TypeError: Object(...) is not a function - Redux - React-Router

Im getting this "TypeError: Object(...) is not a function" when i try to run the react-app.
im getting the error in the store of redux. I dont know why im getting this error, is this because it have something to do with React-Router.
This is my file
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import './Institute/Style/style.css'
import { ProtectedRoute } from './Institute/Protectedroutes/index'
import { Provider } from 'react-redux';
import store from './Redux/store/storage';
import 'bootstrap/dist/css/bootstrap.min.css';
//Some more Components imported here.
class App extends React.Component {
render() {
return (
<div>
<div>
<div id="navbar">
<ul id="nav">
<li>
<Link to='/'>
Home
</Link>
</li>
<li>
<Link to='/profilepage'>
Profile
</Link>
</li>
<li>
<Link to='/events'>
Events
</Link>
</li>
<li>
<Link to='/jobs'>
Jobs
</Link>
</li>
<li>
<Link to='/login'>
Login
</Link>
</li>
<li id="sch">
<Link to='/searchprofile'>
Search People</Link>
</li>
</ul>
</div>
<div>
</div>
</div>
<Switch>
<Route path='/' exact component={Home}/>
<Route path='/login' component={Login}/>
<ProtectedRoute path='/searchprofile' component={Searchprofile}/>
<ProtectedRoute path='/profilepage' component={Profilepage}/>
<ProtectedRoute path='/jobs' component={Jobs}/>
//More Routes here
<Route path='*' component={() => "(404 Not Found)"}/>
</Switch>
</div>
);
}
}
ReactDOM.render(
<Provider store={store}>
<Router>
<App/>
</Router>
</Provider>,
document.getElementById('root')
);
And this is my action file from action folder
const Auth_True = () => {
return{
type: 'Auth_True',
Auth_State: true
}
}
const Auth_False = () => {
return{
type: 'Auth_False',
Auth_State: false
}
}
export default {Auth_False,Auth_True};
And this is my Reducers from reducers folder
const intialState = {
Auth_state : false
}
const authstate = (state = intialState, action) => {
switch(action.type){
case 'Auth_True':
return{
Auth_state: action.Auth_state
}
case 'Auth_False':
return{
Auth_state: action.Auth_state
}
default:
return state;
}
}
export default authstate;
And the store from store folder - Getting Error from this file or i dont know from where exactly as it show
import { createStore } from 'react'
import authstate from '../reducers/reducers'
const store = createStore(authstate);
export default store;
Finally the Component where i dispatch the action
import React from 'react';
import { connect } from 'react-redux'
import Auth_True from '../../Redux/action/actions'
import Auth_False from '../../Redux/action/actions'
class Login extends React.Component{
render(){
return(
<div>
<div>
<h3>Auth State : {this.props.Auth}</h3>
<button onClick={this.props.change_state_true}>Set True</button>
<button onClick={this.props.change_state_false}>Set False</button>
</div>
</div>
);
}
}
const mapStatesToProps = state => {
return{
Auth : state.Auth_State
}
}
const mapDispatchToProps = dispatch => {
return{
change_state_true : () => dispatch(Auth_True()),
change_state_false : () => dispatch(Auth_False())
}
}
export default connect(mapStatesToProps,mapDispatchToProps)(Login);
Can u guys please help me understand why is this happening is this because of React-Router or the connect function from the component where i dispatch or is it the version of the redux and react not supporting.
instead of import { createStore } from 'react';
Try import { createStore } from 'redux';

Categories

Resources