React Authentication and Role Based Authorize
Hi guys, At this post, we will examine and do a user login, page by page permission, token storage and role based authorization on a react project using react router and typescript
We will use libraries in the following at the project. You could do it without typescript as well.
- React 18
- React Router v6
- Typescript 4.6
You can find github link of the project at end of the post.
Jwt will be frequently mentioned in this post so for you can read this post: What is Jwt?
Intro
Let's define routes in App.tsx/js
1 2 3 4 5 6 7 8 9 10 11 |
<div className="App"> <Routes> <Route path="/" element={<HomeLayout />}></Route> <Route element={<Adminlayout />}> <Route path="private2/private" element={<Private2 />} /> <Route path="private" element={<PrivatePage />} /> </Route> <Route path="login" element={<Login />}></Route> <Route path="*" element={<p>Not Found</p>}></Route> </Routes> </div> |
HomeLayout is public page. don't need auth.
AdminLayout : pages where that require authentication.
AdminLayout.tsx :
1 2 3 4 5 6 7 8 9 10 11 |
import { Outlet } from "react-router-dom"; const Adminlayout = () => { return ( <div> <h3>Admin layout</h3> <Outlet /> </div> ); }; export default Adminlayout; |
<Outlet /> We'll do a component called PrivateRoute. The component will check whether user's login. If user was login, the component will be render, if not redirect login page.
Bu sayfaları korumak için bir PrivateRoute adında bir component yapacağız. Bu component giriş yapıp yapmadığını kontrol edeceğiz eğer ki giriş yapmış ise ilgili componenti render edeceğiz değilse login sayfasına yönlendireceğiz.
PrivateRoute.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { Navigate, useLocation } from "react-router-dom"; import useAuth from "./useAuth"; interface IPrivateProps { children: React.ReactNode; } const PrivateRoute = ({ children }: IPrivateProps) => { const { isAuthenticated } = useAuth(); const location = useLocation(); return ( <> {isAuthenticated ? ( children ) : ( <Navigate to={"/login"} replace state={{ location }} /> )} </> ); }; export default PrivateRoute; |
useAuthis hook that we'll do auth process. the hook return authenticate status of user for now. next time we'll complex.
Let's use PrivateRoute at app.tsx
1 2 3 4 |
<Route element={<PrivateRoute><Adminlayout /></PrivateRoute> } > <Route path="private2/private" element={<Private2 />} /> <Route path="private" element={<PrivatePage />} /> </Route> |
when a request is made as /private, PrivateRoute will trigger before AdminAlyout and it will check some controllers. if the result is success, PrivatePage will be render otherwise will redirect to login page.
at now let's go into detail.
User's login and Token storage
After User has succesfully logged in then, server return token, this token type is jwt. We need storage to the token on browser for as not to call again. we have two options. These are Cookie and Local/Session Storage. The both have advantage and disadvantage as well.

Cookie
It storage data as key-value. If data is saved as HttpOnly = true, it cannot access and changed with javascript , only server can access it. should not set as true if required on client-side. Expire time could set. if not set, cookie is deleted when tab is closed. Can access cookies where HttpOnly is false with document.cookie . In this state occurs some vulnerabilities. One of these is CSRF .
CSRF is a type of attack. It can introduce unwanted events in the user's post operations.
Local/Seesion Storage
The data storage as key-value like Cookie. It cannot access from server. Can only access with LocalStorage api at javascript. the vulnerability of this is XSS. when you send data found in localstorage as post, can do inject jscode injection.
we will storage on cookie in this post. At now we will write service named tokenUtil.ts that will do read/write from cookie.
tokenUtil.ts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import jwtDecode from "jwt-decode"; const decodeToken = <T>(token: string) => { try { return jwtDecode<T>(token); } catch (e) { return null; } }; const getToken = () => { return document.cookie .split("; ") .find((row) => row.startsWith("token=")) ?.split("=")[1]; }; const setTokenToCookie = (token: string) => { document.cookie = `token=${token}; path=/`; }; export { decodeToken, getToken, setTokenToCookie }; |
jwt-decode convert a object to token. if not valid return null. can upload with npm.
We will storage on context to decoded token thus other components on component tree can access info at token. Let's create a component called AuthContext.tsx.
AuthContext:
1 2 3 4 5 6 7 8 |
interface IAuthProps { children: ReactNode; } const AuthContext = createContext({ token: "", onLogin: (token: string) => {}, onLogout: () => {}, }); |
Provider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const AuthProvider = ({ children }: IAuthProps) => { const navigate = useNavigate(); const location = useLocation(); const [token, setToken] = useState(getToken() || ""); const handleLogout = () => { setToken(""); setTokenToCookie(""); }; const handleLogin = (token: string) => { setToken(token); setTokenToCookie(token); var returnPath = Object.values(location.state as Location); navigate(returnPath[0].pathname || "/admin"); }; const value = { token, onLogout: handleLogout, onLogin: handleLogin, }; return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; }; |
useLocation ile kullanıcının redirect olmasını istediği sayfanın bilgilerini alıyoruz eğer direkt login sayfasına girmiş ise de /admin ‘e yönlendiriyoruz. Oluşturduğumuz AuthContext”e diğer componentlerin erişebilmesi için App.tsx de üst component olarak ekleyelim.
1 2 3 4 5 |
<AuthProvider> ...Route ...Route ... </AuthProvider> |
Şimdi useAuth hooksundan contextin verilerini okuyup tokeni decode edip kontrol edebiliriz
useAuth.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
interface IAuth { isAuthenticated: boolean; name: string; } const useAuth = () => { var { token } = useContext(AuthContext); var auth = decodeToken<IAuth>(token); var isAuthenticated = false; if (auth) { isAuthenticated = auth.isAuthenticated; } return { isAuthenticated }; }; export default useAuth; |
token nesnemiz decode olduğunda IAuth tipinde bekliyoruz böyle değilse null dönecektir. şimdi de Login sayfamızda bir context aracılığıyla login olalım
Login.tsx:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
const Login = () => { const { isAuthenticated } = useAuth(); const { onLogin } = useContext(AuthContext); const signIn = () => { //api return token setToken( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik9rYW4gQ2FuIEthcmFkYcSfIiwiaXNBdXRoZW50aWNhdGVkIjp0cnVlfQ.RwRNm0GPuL_aZF0TI1Lw3i1MsGJRjsndC-iGot7hPOo" ); }; const setToken = (token: string) => { onLogin(token); }; return isAuthenticated ? ( <Navigate to="/" />) : ( <div> <h1>Login Page</h1> <button type="button" onClick={() => signIn()}> Sign in </button> </div> ); }; export default Login; |
Bir apimiz olmadığı için jwt.io üzerinden IAuth tipinde bir token oluşturdum. Siz de buradan bir token oluşturabilirsiniz.

Artık /private istek attığımızda bir token doğrulaması yapıp ona göre yönlendirme yapacak.
Role Bazlı Yetkilendirme
Şimdi authenticate işlemlerini gayet güzel bir şekilde yaptık. Ama bazı sayfalar için kullanıcının sadece giriş yapması yetmeyebilir örneğin sadece adminlerin erişebilieceği ya da manager rolündeki kullanıcıların erişebileceği sayfalar olabilir bunlar içinde bir kontrol yapmamız lazım eğer izni olmayan bir sayfaya erişim sağlamaya çalıştıysa da bir yönlendirme yapalım.
Roles adında bir parametre ekleyeceğiz ve bunu private route aracılığıyla props olarak göndereceğiz ve aşağıdaki şekilde bir algoritma olacak

Yukarıda görüldüğü gibi eğer kullanıcı giriş yapmamış ise login page yönlendirme yapıyoruz login olduysa giriş yapmaya çalıştığı sayfanın bir yetkilendirmesi var mı ona bakıyoruz eğer sayfa da ekstra bir izin yoksa sayfayı gösteriyoruz ama eğer bir izin gerekiyorsa kullanıcının yetkilerine bakıyoruz yetkilerinde bu sayfaya erişim var ise sayfayı gösteriyoruz yok ise yetkiniz yok şeklinde bir mesaj gösteriyoruz.
Şimdi yukarıdaki mantığı işletmemiz için bir role mantığı ekleyeceğiz sayfanın erişim izni verdiği roller ve kullanıcıya ait roller. ve iş katmanımızda bunları kontrol edeceğiz.
PrivateRoute.tsx içine bir tane roles adında nullable parametre ekleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 |
interface IPrivateProps { children?: React.ReactNode; roles?: string[]; } const PrivateRoute = ({ children, roles }: IPrivateProps) => { const { isAuthenticated, isAllow } = useAuth({ roles }); const location = useLocation(); if (!isAuthenticated) { return <Navigate to={"/login"} replace state={{ location }} />; } return <>{isAllow ? children : <div>You are not allowed to access this page</div>}</>; }; |
useAuth hooksu ile rolleri kontrol edeceğiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
interface IAuth { isAuthenticated: boolean; name: string; roles?: string[]; } interface IAuthProps { roles?: string[]; } const useAuth = (props?: IAuthProps) => { var { token } = useContext(AuthContext); var auth = decodeToken<IAuth>(token); var isAuthenticated = false; var isAllow = true; if (auth) { isAuthenticated = auth.isAuthenticated; if (isAuthenticated && props && props.roles) { isAllow = props.roles.some((role) => auth?.roles?.includes(role)); } } return { isAuthenticated, isAllow }; }; |
şimdi ise uygulama da gösterelim önce jwt.io üzerinden objemize roles ekleyelim

oluşturduğunuz tokeni eklemeyi unutmayın.
İstediğimiz sayfanın roles propuna yazarak kontrol sağlayabiliriz.
1 2 3 4 5 6 7 8 |
<Route path="admin/manager" element={ <PrivateRoute roles={["manager"]}> <Private2 /> </PrivateRoute> } /> |
Sonuç
İlk olarak şunu unutmayınız, browser tarafında ne yaparsak yapalım buradaki bütün koruma aşılabilir. Burada yaptıklarımız sadece kullanıcıyı düzgün yönlendirebilmemiz için, önemli olan API/Servis tarafında güvenlik sağlamaktır. O yüzden önemli işlemlerinizi clienta koymamalıyız.
Genel olarak bu gönderide bir kullanıcının giriş yapma serüvenini inceledik. Neler olabileceğini sayfaların güvenliğini nasıl sağlayacağımızı ve yönlendirmeleri nasıl yapabileceğimizi öğrendik. Projenin linkine aşağıdan ulaşabilirsiniz.
Proje Linki : okankrdg/React-Authorize-Authentication (github.com)
Okuduğunuz için Teşekkürler İyi Çalışmalar 🙂