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.
Related
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)
Express js middleware is not working as expected. It is showing too many redirections.
When i remove the token or logout it shows in the browser that too many redirections
Middleware
const isAuthenticate = async (req, res, next) => {
const token = req.cookies.jwt;
if (token) {
jwt.verify(token, "thisisjwtsecret", async (err, token_decode) => {
if (!err) {
const u_id = token_decode._id;
const userData = await User.findOne({ _id: u_id });
req.user = userData;
req.isAuth = true;
next();
} else {
res.redirect("/user/login");
}
});
} else {
res.redirect("/user/login");
}
};
Route.js
// Auth Controller
const AuthController = require("../../controllers/auth/AuthController");
const { isAuthenticate } = require("../../middlewares/isAutheticated");
router.get("/user/login", isAuthenticate, AuthController.login);
router.post("/user/login", AuthController.checkLogin);
router.get("/user/register", isAuthenticate, AuthController.createUser);
router.post("/user/register", isAuthenticate, AuthController.storeUser);
module.exports = router;
LOgin function
// Showing Login Page to User
const login = (req, res) => {
return res.render("auth/login");
};
When i remove the token or logout it shows in the browser that too many redirections
Now that you've shown revised code for isAuthenticate(), the redirect loop is caused by the redirects in that code. Here's what happens:
Some route you have (any route) that uses isAuthenticate as middleware for the route detects that the user is not logged in. It then redirects to /user/login. That's fine up to that point. Then, the browser issues a new request for /user/login and that takes you to this route definition:
router.get("/user/login", isAuthenticate, AuthController.login);
But, that route definition again runs the isAuthenticate() middleware which redirects to /user/login and thus you have an infinite redirect loop.
Probably you just need to remove the isAuthenticate() check from this route. If the user is already going to the /user/login page, you don't need to check their authentication or redirect them. If you have a reason to want to know if they are authenticated or not, then you need a separate version that ONLY does the auth check and does not redirect and you can use that in the /user/login route definition.
Original answer before code was shown that did res.redirect().
So, this middleware you show sets req.isAuth to true or false and then calls next() to continue routing. All three code paths through that middleware just set req.isAuth and then call next(). Nowhere in this middleware does it do any redirect. So, if the core problem is too many redirections, that issue must be caused somewhere else by some other route/middleware that actually does a redirect, probably when it sees that req.isAuth is false since you said that the problem occurs when logged out or when the token is missing.
When redirecting, you have to make absolutely sure that when you redirect to a URL, there is ZERO chance (no code path of any kind) that the route handler for that URL will also redirect to that URL. That's how you get into a redirect loop.
Looking at the other routes you show, if the too many redirects issue is when redirecting to /user/login, then it seems likely the problem is in the authController.login() handler from this route:
router.get("/user/login", isAuthenticate, AuthController.login);
If the code for that route checks req.isAuth and redirects in any circumstances, then that would be an endless redirect loop.
If you need further advice, please provide the following information:
Which exact redirect URL is causing the problem of too many redirects? Is is /user/login?
Show us the code for the route that does that redirect because that's apparently where the fault is.
I would like to post at the path /users and then immediately post to /users/:id, but the actions need to be different at each of these URLs, so I can't use the array method for applying the same middleware to different URLs
The idea is that POST(/users/:id, ...) will never be called by the client. It only gets called immediately after POST(/users, ...)
When using express, you are providing a handler function for a specific endpoint. Actually it's an array of those functions (middlewares). That means that you can switch from :
route.post('/users/`, (req, res, next) => {
// do your magic
});
to
route.post('/users/', handleMyCall);
This way you can easily reuse those functions in multiple endpoints without your need to actually make requests:
route.post('/users/', (req, res) => {
// do something +
handleMyCall(req, res);
// either return the result of this call, or another result
});
route.post('/users/:userID', (req, res) => {
// do another operation +
handleMyCall(req, res);
});
Update:
Using GET or POST differs in the way the data is sent to the server. You can use both for your cases, and it really depends on the testing client you have.
Typically, a GET request is done to query the database and not do any actions. POST is usually used to create new entities in the database.
In your scenario, I'd guess you would have post('/users/) in order to create a user. And then have get('/users/:userID') to find that user and return it to the client.
You can easily have different endpoints with different handles for those cases.
As I understood from the comments, you'll need a POST request on /users (to persist data in some database) and GET /users/:id to retrieve these data, which is very different from POSTing the same thing on 2 different endpoints.
POST is generally used to persist and GET to retrieve data.
I'll assume you use some kind of NoSQL DB, perhaps MongoDB. MongoDB generate a unique ID for each document you persist in it.
So you'll have to have 2 routes :
const postUser = async (req, res, next) => {
try {
// persist your user here, perhaps with mongoose or native mongo driver
} catch (e) {
return next(e);
}
}
const getUserById = async (req, res, next) => {
try {
// get your user here thanks to the id, in req.params.id
} catch (e) {
return next(e);
}
}
export default (router) => {
router.route('/users').post(postUser);
router.route('/users/:id').get(getUserById);
};
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.
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!