I use token authentication for my react spa app. I'm trying to add a token using React hooks.
I have a three file for this small example.
AuthContex.js
AuthReducer.js
authentication-service.js
When user click on the login button, react call authentication-service.js in login function.
When the user is logged in, I need to register the token into the state with the help of hooks. Because, I need to refresh the token at any point in the application, and as a result of this refresh the token was not distributed correctly to all components. I thought of solving this problem by placing the token on top of all components. Thus, if the token is refreshed at any time in the application, it would be distributed properly to the subcomponents.
But react has gave the error --->>
× Unhandled Rejection (Error): Invalid hook call. Hooks can only be
called inside of the body of a function component. This could happen
for one of the following reasons:
You might have mismatching versions of React and the renderer (such
as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
function login(username, password) {
const { authDispatch } = useContext(AuthContext);
return axios.post('/api/Authentication/SignIn', {
email: username,
password: password
})
.then((res) => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('auth-jwt', JSON.stringify(res.data));
currentUserSubject.next(res);
authDispatch({ type: LOGIN, payload: res.data });
return res;
}).catch(handleResponse);
}
Please see my CodeSandbox for a small demo.
Related
I store a user token in a vuex state.
When the page of my nuxt app is refreshed, i want this token to persist, to check for eligibility to visit user-related routes.
From a global middleware, i just dispatch an action like so
context.store.dispatch('authModule/resetToken')
triggering the vuex action
resetToken(context) {
context.commit('RE_AUTH')
},
which triggers the following mutation
RE_AUTH: (state, token) => {
if (!state.token && process.client && localStorage.getItem('auth-token')) {
token = localStorage.getItem('auth-token')
state.token = token
}
},
When i reload the page, the mutation is triggered, but the if condition does not resolve true and execute the code to reappend the token, even though there is the correct named token in localhost.
When i however then navigate to another page, the code does execute and reappends the token. I dont understand why, because global middleware should be executed first in lifecycle and vuex should have access to localstorage
In my nuxt project, i get a token from the backend that identifies the user. I tie this token to a vuex prop and save it in localstorage, so the user doesnt have to re authenticate when the page is refreshed.
To revalidate the user only when the page is actually refreshed, i wrote a beforemount function in the highest level, which is the default layout
beforeMount() {
const token = localStorage.getItem('user-token')
console.log(token)
if (token) {
this.$axios.defaults.headers.common.Authorization = token
this.$store.dispatch('auth/setToken', token)
}
}
In the next step, the router on protected pages should check if the authstate is true, which is only true if the vuex localstorage prop is set. Since i only need that on certain pages, i wrote a router middleware thats only loaded on these pages
export default function (context) {
if (!context.store.getters['auth/isAuthenticated']) {
return context.redirect('/auth?login')
}
}
Problem is, the middleware runs before the beforeMount according to the docu, which creates the problem that the middleware checks for a token which is set later and kicks the user out.
Since the only thing that comes before router middleware is global middleware, my plan was to write the reauth as global middleware. But somehow, the middleware is also called on every router change, not only before the site is created aka page refresh. This would execute the function way too often.
The goal is to set the token before the route validate based on the token, only on page refresh, since the rest is taken care of by vuex.
I am using #auth0/auth0-react library to handle authentication process. For an unauthorized user, it will redirect to back to initial screen ("/") route with this query params. ?error=unauthorized... Then again if I am trying to execute the login it will stay on the same page. Maybe I need to find a way to clear the previous auth0 state.
const { loginWithRedirect, logout} = useAuth0();
const loginWithAuth0 = () => {
logout({}); // clear previous auth 0 state
loginWithRedirect({
redirectUri: window.location.origin + "/dashboard"
});
};
Above is my attempt. Using logout({}) I am trying to clear the previous auth0 state and going forward which doesn't happen. For an authorized user there's no issues. So what I need is to handle the unauthorized user behaviour. Currently it will not redirect to the dashboard which is correct for unauthorized. But what I need is to clear the previous auth0 state and redirect to the next attempt. How can I achieve it?
You could destructure the isAuthenticated prop off of the object returned by the useAuth0() hook:
const { loginWithRedirect, logout, isAuthenticated } = useAuth0();
if (!isAuthenticated) {
logout({}); // (not certain this is necessary)
// redirect with whatever router your framework might include
}
Also consider the withAuthenticationRequired function which is exported from the same #auth0/auth0-react package, and allows you to protect a route/component and configure a redirect for unauthenticated users at the same time:
When you wrap your components in this Higher Order Component and an anonymous user visits your component they will be redirected to the login page and returned to the page they we're redirected from after login.
Trying out SvelteKit and I'm having a hard time with hooks. The docs don't really seem to explain it all too well. My current understanding of hooks is that they basically allow you to interact with requests made to your server before they get to their destination? (I'm open to a better explanation - specifically the handle hook).
My current issue is I made an endpoint called login. As the name suggests, it allows users to sign into my application by generating a token and storing it as a cookie on their client. This works until I add hooks. After reading the hooks description, I figured the handle hook is perfect for what I want to do - validate the token on each request - if invalid, reroute the user to the login screen, if valid, allow the request to continue uninterrupted.
export const handle: Handle = async ({ event, resolve }) => {
const isLogin = event.url.pathname.startsWith('/login')
const cookies = cookie.parse(event.request.headers.get('cookie') || '');
const token = cookies['token']
if (!token) {
if (!isLogin) {
return Response.redirect(`${event.url.origin}/login`)
}
return await resolve(event)
} else {
try {
await verifyToken(token)
if (isLogin) {
return Response.redirect(`${event.url.origin}/about`)
}
} catch (err) {
return Response.redirect(`${event.url.origin}/login`)
}
}
return await resolve(event)
};
This does not work as expected. When I initiate the request to the api/login endpoint, the request does not seem to make it there. I have console.logs all over the endpoint but no messages were outputted to the terminal & when I check the application storage, no new cookie was added.
What am I missing about hooks?
Why is it not passing the request off to the endpoint?
Any idea how I can fix this?
The handle hook runs for every request—including endpoints.
When you fetch /api/login without a token, your hook will redirect the request to /login since isLogin === false. You need to allow through every route that should be accessible without a login, for example:
const isLogin = /^\/(api\/)?login$/.test(event.url.pathname)
I've been looked at numerous similar questions on stack-overflow and/or GitHub but none of the solutions offered have solved my problem. As it is stated in the title, the onAuthStateChanged handler is not called.
I have a Sign up form where user create an account by providing (among other things) his email and password. Then I signup the user with firebase:
firebase.auth().createUserWithEmailAndPassword(user.email, user.password)
But later on, when I reload the application, onAuthStateChanged is never called and
firebase.auth().currentUser returns null;
Here is my code (simplified):
useEffect(() => {
setAuthenticated(true);
const unsubscribe = firebase.auth().onAuthStateChanged((user) => { // <-- this is not called
if (user) {
// user is connected
}
});
return unsubscribe;
}, []);
Also in firebase console: Authentication --> Sign-in method --> Authorized domain, I have added my domains (including localhost).
This code used to work until yesterday...