codedthemes / berry-free-react-admin-template

Berry free react material-ui admin template for easing and faster web development.
https://berrydashboard.io/free/
MIT License
1.84k stars 896 forks source link

Help with protected the urls #23

Closed cgitosh closed 2 years ago

cgitosh commented 2 years ago

I'm a beginner in React JS and I chose Berry because it seems straight forward enough and uses all the tech stack that I am using. I got stuck somewhere and I hope you can assist (I know you have this in the Pro version but I'm not yet at the stage of buying, hopefully soon) I need to control who has access to the dashboard, how do I implement the AuthGuard/Protect my urls such that only authenticated users have access to the Dashboard? I guess this ties in with token refresh when it expires.

Mukthayar1 commented 2 years ago

ProctedRoutes.js import { useNavigate, useLocation, Navigate, Outlet } from 'react-router-dom'; import useAuth from "../hooks/useAuth";

const PrivateRoute = ({ children, allowedRoles }) => {

const { auth } = useAuth();
const location = useLocation();

console.log('auth===>',auth)

return (

    auth  ? <Outlet />
        :
        <Navigate to="/ECommerce/Login" state={{ from: location }} replace />
);

};

export default PrivateRoute;

New Routes

import React, { lazy } from 'react'; import MainLayout from 'layout/MainLayout'; import Loadable from 'ui-component/Loadable'; import { Routes, Route } from 'react-router-dom'; import MinimalLayout from 'layout/MinimalLayout';

import PrivateRoute from './ProvateRoute';

const DashboardDefault = Loadable(lazy(() => import('views/dashboard/Default'))); const AuthLogin3 = Loadable(lazy(() => import('views/pages/authentication/authentication3/Login3'))); const AuthRegister3 = Loadable(lazy(() => import('views/pages/authentication/authentication3/Register3'))); const AddProducts = Loadable(lazy(()=>import('views/utilities/AddProducts') ))

export default function RouteNew() { return ( <>

}> } /> } /> } /> }> }> } /> } /> }> }> } />
    </>
);

}

where useAuth is my contex api when ever your login i save data in useAuth

shaheer904 commented 2 years ago

I have check that if the redux state had the jwt token, then I have tried to access the login route but it shows black screen, can u please share the code if the user is loggedin then it will only redirect to dashboard, if user access the route which does not exist.

Mukthayar1 commented 2 years ago

I have check that if the redux state had the jwt token, then I have tried to access the login route but it shows black screen, can u please share the code if the user is loggedin then it will only redirect to dashboard, if user access the route which does not exist.

i have to code this . you can email me khanadil1000s@gmail.com so i can do work for you

shaheer904 commented 2 years ago

in config.js set basename='/' Apply a condition in the login and register page just before the return

if(user){
navigate('/dashboard/default')
}

change the NewRoutes File

import { lazy } from 'react';
import MainLayout from 'layout/MainLayout';
import Loadable from 'ui-component/Loadable';
import { Routes, Route } from 'react-router-dom';
import MinimalLayout from 'layout/MinimalLayout';
import PrivateRoute from 'Potected';
import Typography from './views/utilities/Typography';
import NoFound from 'NoFound';

const DashboardDefault = Loadable(lazy(() => import('./views/dashboard/Default')));
const AuthLogin3 = Loadable(lazy(() => import('./views/pages/authentication/authentication3/Login3')));
const AuthRegister3 = Loadable(lazy(() => import('./views/pages/authentication/authentication3/Register3')));
const AddProduct = Loadable(lazy(() => import('./views/utilities/Typography')));

const NewRoutes = () => {
    return (
        <>
            <Routes>
                <MinimalLayout>
                    <Route path="/" element={<AuthLogin3 />} />
                    <Route path="/Register" element={<AuthRegister3 />} />
                    <Route path="*" true element={<NoFound />} />
                </MinimalLayout>

                <MainLayout>
                    <Route element={<PrivateRoute />}>
                        {/* <Route path="/" element={<DashboardDefault />} /> */}
                        <Route path="/dashboard/default" element={<DashboardDefault />} />
                        <Route path="*" true element={<NoFound />} />
                    </Route>

                    <Route path="/utils" element={<PrivateRoute />}>
                        <Route path="/util-typography" element={<Typography />} />
                        <Route path="*" true element={<NoFound />} />
                    </Route>
                </MainLayout>
            </Routes>
        </>
    );
};

export default NewRoutes;

In PrivateRoute.js file change this return auth === 'user' ? <Outlet /> : <Navigate to="/" state={{ from: location }} replace />;

Now Add a new page NotFound.js

import React from 'react';
import { useNavigate } from 'react-router';

const NoFound = () => {
//user your own redux or contextAPI state or LocalStorage
    const a = localStorage.getItem('user');
    const navigate = useNavigate();

    return (
        <div>
            <h1>No Found</h1>
            {a === 'user' ? (
                <button onClick={() => navigate('/dashboard/default')}>go to dashboard</button>
            ) : (
                <button onClick={() => navigate('/')}>Go to login</button>
            )}
        </div>
    );
};

export default NoFound;
codedthemes commented 2 years ago

Anything needed here, i can close issue if no response.

shaheer904 commented 2 years ago

Please provide the code for protecting routes

cgitosh commented 2 years ago

Thank you @Mukthayar1 and everyone else for the contribution to my question. I managed to adapt your code and it's working. The code is in two files. 1.) the Protected routes file (I'm calling it RequireAuth.js) and the mainRoutes.js file. 1.) Code for RequireAuth.js `import { useLocation, Navigate, Outlet } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { selectCurrentToken } from './authSlice';

const RequireAuth = () => { const token = useSelector(selectCurrentToken); const location = useLocation(); return token ? : <Navigate to="/login" state={{ from: location }} replace />; }; export default RequireAuth;`

2.) Code for MainRoute.js

`import { Children, lazy } from 'react';

// project import import Loadable from 'components/Loadable'; import MainLayout from 'layout/MainLayout'; import RequireAuth from 'pages/authentication/RequireAuth'; import { element, elementType } from 'prop-types';

// render - dashboard const DashboardDefault = Loadable(lazy(() => import('pages/dashboard')));

// render - utilities const Typography = Loadable(lazy(() => import('pages/components-overview/Typography'))); const Color = Loadable(lazy(() => import('pages/components-overview/Color'))); const Shadow = Loadable(lazy(() => import('pages/components-overview/Shadow'))); const AntIcons = Loadable(lazy(() => import('pages/components-overview/AntIcons')));

// ==============================|| MAIN ROUTING ||============================== // / protected routes / const MainRoutes = { path: '/', element: , children: [ { path: '/', element: , children: [ { path: '/dashboard/default', element: }, { path: '/', element: }, { path: 'color', element: }, { path: 'typography' element: }
] } ] }; export default MainRoutes; `

pvimalmec commented 2 years ago

@cgitosh Could you please share code snippet, when i made changes in above two files, it didnt work for me. I can see you never use RequireAuth in Maniroutes. Am i missing something? please help:-)

cgitosh commented 2 years ago

I'm using RequireAuth in mainRoutes to wrap around the other routes that require a user to be authenticated (All routes in MainRoutes require authentication in my case. I will share my RequireAuth code and the authSlice code plus section of the Mainroutes where I'm wrapping the routes using RequireAuth. 1.) authSlice.js (I'm using Redux Tool Kit (RTK), in my journey I discovered that redux is really unavoidable so I invested time learning it then I found the RTK gem at the end which has really made things simpler..) import { createSlice } from '@reduxjs/toolkit'; import { loadState } from 'localStorage'; const persistedState = loadState(); const initialState = { user: '', token: '', refreshToken: '' }; const authSlice = createSlice({ name: 'auth', initialState: { ...persistedState.auth }, reducers: { setCredentials: (state, action) => { const { user, access, refresh } = action.payload; state.user = user; state.token = access; state.refreshToken = refresh; }, logOut: (state, action) => { state.user = null; state.token = null; } } }); export const { setCredentials, logOut } = authSlice.actions; export default authSlice.reducer; export const selectCurrentUser = (state) => state.auth.user; export const selectCurrentToken = (state) => state.auth.token;

2.) RequireAuth.js - Uses authSlice above to check if the user is authenticated and token is valid/not expired, if invalid/expired, user is redirected to login page. (Side note: Been trying to implement the refresh token for expired tokens but so far I have not been successful. Problem is on the Django side)

import { useLocation, Navigate, Outlet } from 'react-router-dom'; import { useSelector } from 'react-redux'; import { selectCurrentToken } from './authSlice'; const RequireAuth = () => { const token = useSelector(selectCurrentToken); const location = useLocation(); return token ? <Outlet /> : <Navigate to="login" state={{ from: location }} replace />; }; `export default RequireAuth;

3.) MainRoutes.js.. This file you have it so I will share snippets.

Hope that helps.. let me know if you need any more clarification