Redux middleware for dynamic auth / redirecting - javascript

I attempted to create a very simple redux middleware to handle the auth needs in my application. Users in this app can have 3 states:
unregistered (should always be redirected to login)
partially registered (should redirect them to a secondary login. Users need to belong to a group to access any features in the app. So after initial registration they need to create or join a group. OR if they leave their group they'll be in this state and will need to have any actions redirect them to this page until they join a new group)
fully registered, should have full access with no forced redirects until they log out or leave their group.
So I was thinking a simple solution would be to have middleware that checks the status code of any async actions and redirects accordingly. So maybe a 401 would trigger a redirect to to the login/signup page, /login, and a 422 would trigger redirect to the secondary registration, /register.
This is my basic set up so far:
import { browserHistory } from 'react-router'
export default function({ dispatch }) {
return next => action => {
const url = window.location.href.split('/')
const location = url[url.length - 1]
if (location === 'login' || location === 'registration') {
return next(action)
}
let status;
const token = localStorage.getItem('token')
if (action.payload) {
status = action.payload.status
}
if (status === 401) {
return browserHistory.push('/login')
} else if (status === 422) {
console.log('redirect to registration')
return browserHistory.push('/register')
}
return next(action)
}
}
Obviously this approach does not utilize any higher order components for auth like most react/redux auth examples use. It seems to work OK, but I see a view start to render for a split second before a redirect so it isn't 100% smooth.
Is there a more efficient / simple way to handle this three state auth system? I am using react-router-redux.

Related

customization in react auth0

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.

SvelteKit Hook Prevents Endpoint Request

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)

Secure logging in to a Vue.js SPA

I'm pretty new to Vue.js, I followed lots of tutorials, but one thing bothers me.
Let's say I'm building a SPA. The user can log in and then access data using a RESTful API.
So the first step would be to send my login information (username, password) to the API and receive a token. The token would be saved to my vuex and also to the localstorage/cookie so the user stays logged in after a page refresh.
All of the other requests to the API would be signed by the token.
My routes are set up:
const routes = [
{
path: '/dashboard', name: 'dashboard', component: Dashboard, meta: { requiresAuth: true }
},
{
path: '/login', name: 'Login', component: Login,
},
]
I'm using a route guard to protect the /dashboard page from displaying to a non-logged in user:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.loggedIn) {
next({ path: "/login" })
} else {
next();
}
} else {
next();
}
})
My concern is with the loggedIn getter from vuex store which is implemented this way:
const getters = {
loggedIn(state) {
return state.token !== null;
}
};
Everything is working as it supposed to. I understand that without a access token I won't be able to get data from a server.
But...
I can open the developer tools, put an access_token to my localstorage with a random value, refresh the page and suddenly I can access the /dashboard page.
So my question is, how to avoid this scenario?
My first idea was, to have 2 pages - the login page and the second one which contains the SPA. The authentication would be done via session server-side and the SPA page could be accessed only to a logged in user.
Or is there some standard way to to this, so my SPA can handle this?
as said in the comments, i would create a checkLogin() like this:
checkLogin() {
axios
.get('/webapi/check')
.then(() => {})
.catch(err => {
if (err.response.status === 401) {
// use a mutation to toggle your "loggedIn" state
this.$store.commit('loggedIn', false)
if (this.$route.path !== '/login') {
this.$router.push('/login')
}
}
})
}
and there for u can use ur routeGuard to use the function on each change
and ofc ur backend should't allow valid backend responses without the token
UPDATED
u need a state for loggedIn
then u do a mutation to toggle this state depending on how your backend response.

How to redirect to login page when not authorized in react-admin framework

I have been building an admin panel using react-admin framework.
It worked well, but I couldn't make it redirect to login page when not authenticated.
I used authProvider.js for authentication.
Any help would be appreciated.
You can use Promise.reject();
So in general, you can check if you are authenticated in authProvider.js file.
// in src/authProvider.js
import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_CHECK } from 'react-admin';
export default (type, params) => {
if (type === AUTH_LOGIN) {
// ...
}
if (type === AUTH_LOGOUT) {
// ...
}
if (type === AUTH_ERROR) {
// ...
}
if (type === AUTH_CHECK) {
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject();
}
return Promise.reject('Unknown method');
};
Promise.reject() redirect to login page in default.
If you want to redirect to other page, you can use Promise.reject('/no-access').
You must be using a Node API. So you can use a middle-ware everytime before every routing inside a admin oriented page, with a combination of JWT.
for example:
app.use('/admin/users',YOURMIDDLEWARE,(req,res)=>{
//your work
})
And the middle-ware contains three params namely req,res and next. After you are done with the checking you can call next to pass it to the 'your work' part from the middleware.
Check this blogPost
Maybe TL;DR but that's the thing!

vue-apollo execute graphql query in router guard

I have an application where a user can sign up for an event. As soon as the user has signed up, he can visit routes he couldn't before.
I'm using graphql for my api requests.
The code in my route guard looks like this:
router.beforeEach(async (to, from, next) => {
if (to.meta.signUpRequired && from.name) {
const { data: { getCurrentUser } }
= await apolloProvider.defaultClient.query({
query: USER_INFO_QUERY
});
if (getCurrentUser && getCurrentUser.isCollectionAttendee) {
return next();
}
return next('/');
}
return next();
});
This works fine and the request is not executed every time the route changes because vue-apollo caches the result. My problem is, that if the user signs up and then tries to switch the route, he can't because the cached result of the query is still used.
I don't want to use a network-only fetchPolicy before every route change.
How can I solve this?
Maybe try using this.$apolloProvider.defaultClient.resetStore() after sign-up.

Categories