I need to restrict links in react router by specific user roles (I have roles stored in token). What I'm trying to do now is: send username & password through SignIn component to getTokens() function from custom useAuth hook on submit to then pass a boolean isModerator inside route value to ensure that the user have the required authorities for the link to show. In my case request is just not going to the server on form submit, probably because I misuse context api or react itself somehow.
So this is how my useAuth hook looks right now:
import React, { useState, createContext, useContext, useEffect } from "react";
import axios from "axios";
export const AuthContext = createContext({});
export const AuthProvider = ({ children }) => {
const [authed, setAuthed] = useState(false);
const [moderator, setModerator] = useState(false);
const [accessToken, setAccessToken] = useState("");
const [refreshToken, setRefreshToken] = useState("");
const [authorities, setAuthorities] = useState([]);
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const signIn = async (e, username, password) => {
e.preventDefault();
const result = await getTokens(username, password);
if (result) {
console.log("User has signed in");
setAuthed(true);
}
};
const isModerator = async () => {
const result = await getAccessTokenAuthorities();
if (result) {
console.log("User is admin");
setModerator(true);
}
};
const getTokens = async (username, password) => {
const api = `http://localhost:8080/api/v1/public/signIn?username=${username}&password=${password}`;
const res = await axios.get(api, {
withCredentials: true,
params: {
username: username,
password: password,
},
});
const data = await res.data;
setAccessToken(data["access_token"]);
setRefreshToken(data["refresh_token"]);
console.log(data);
return accessToken, refreshToken;
};
const getAccessTokenAuthorities = async () => {
const api = `http://localhost:8080/api/v1/public/getAccessTokenAuthorities`;
const res = await axios.get(api, {
withCredentials: true,
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const data = await res.data;
setAuthorities(data);
let vals = [];
authorities.forEach((authority) => {
vals.push(Object.values(authority));
});
const check = vals.filter((val) => val.toString() === "MODERATOR");
if (check.length > 0) return !isModerator;
console.log(authorities);
return isModerator;
};
return (
<AuthContext.Provider
value={{
authed,
setAuthed,
moderator,
setModerator,
getTokens,
getAccessTokenAuthorities,
username,
password,
setUsername,
setPassword,
}}
>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
And this is me trying to use AuthContext in SignIn component:
import React, { useContext, useEffect, useState } from "react";
import { useAuth } from "../hooks/useAuth";
import { AuthContext } from "../hooks/useAuth";
const SignIn = (props) => {
const auth = useAuth();
const userDetails = useContext(AuthContext);
return (
<>
<h1>Вход</h1>
<form
method="get"
onSubmit={(e) => auth.signIn(e)}
encType="application/json"
>
<label htmlFor="username">Имя пользователя</label>
<input
type="text"
id="username"
onChange={(e) => userDetails.setUsername(e.target.value)}
></input>
<label htmlFor="password">Пароль</label>
<input
type="password"
id="password"
onChange={(e) => userDetails.setPassword(e.target.value)}
></input>
Вход
<input type="submit"></input>
</form>
</>
);
};
SignIn.propTypes = {};
export default SignIn;
Here is how I set my AuthProvider in index.js:
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css"; import reportWebVitals from "./reportWebVitals";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Inventory from "./components/Inventory";
import SignIn from "./components/SignIn";
import { AuthProvider } from "./hooks/useAuth";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<BrowserRouter>
<AuthProvider>
<Routes>
<Route path="/"
element={<App />}>
</Route>
<Route path="api/v1/public/signIn"
element={<SignIn />}>
</Route>
<Route path="api/v1/moderator/inventory" element={<Inventory />} >
</Route>
</Routes>
</AuthProvider>
</BrowserRouter>
</React.StrictMode> );
reportWebVitals();
Thanks in advance.
CodePudding user response:
Try creating your context like this:
export const AuthContext = createContext();
And then, in your SignIn
component, destructure your context's variables like this:
const {variable1, variable2, etc} = useContext(AuthContext)
Let me know if that helps.
CodePudding user response:
You're creating 2 instances of context when you initialize 2 variables of it. Both of these variables are behaving as separate instances of context. Its like when an object is initialized from a constructor.
All the methods you've passed to context Provider are available on const auth=useAuth()
. Inside your Signin component, you're calling userDetails.setUsername()
for changing value of username and to submit the form you're calling auth.signin
.
You can simply use auth.setUsername(e.target.value)
and auth.setPassword(e.target.value)
. For submitting form use auth.signin()
const SignIn = (props) => {
const auth = useAuth();
// const userDetails = useContext(AuthContext); No need to initialize this one
useEffect(() => {
// here in this log you will see all the methods are available from context provider
if (auth) console.log(auth);
}, [auth]);
return (
<>
<h1>Вход</h1>
<form
method="get"
onSubmit={(e) => auth.signIn(e)}
encType="application/json"
>
<label htmlFor="username">Имя пользователя</label>
<input
type="text"
id="username"
onChange={(e) => auth.setUsername(e.target.value)}
></input>
<label htmlFor="password">Пароль</label>
<input
type="password"
id="password"
onChange={(e) => auth.setPassword(e.target.value)}
></input>
Вход
<input type="submit"></input>
</form>
</>
);
};
export default SignIn;