The hash value of the URL in Nuxt.js - vue.js

The router mode is "history", and I want to valid each request in the middleware which will redirect the request to a given page when the request is invalid. In my case, the request that does not exist in the white_path will be redirect to '/500':
export default function (context) {
const white_path = new Array('/login', '/404', '/500')
const cur_path = context.route.path
if(white_path.indexOf(cur_path) < 0){
context.redirect(301, '/500')
}
}
But the problem is that when I input the url: http://localhost:3000/blog/SampleTitle/#first-paragraph, but then it redirects to http://localhost:3000/500#first-paragraph. Why the hash value does not disappear? In the middleware, the hash value is empty, but after redirect to the client, the hash value becomes the one appear in the last url.
How can I solve it? Thank your very much!

Related

Nuxt SSR - i can't check if a user is authenticated

I'm trying to work on a Nuxt SSR frontend that uses a Django backend with Session Authentication.
I would like to have some SSR pages as well as client rendered pages in my frontend, so i'm using Universal mode.
The problem is that i did not find a working approach to check if a user is authenticated before loading a page, so i can't restrict pages to anonymous users. In order to check if a user is authenticated, Django will check if the request's headers contain a cookie, and according to that return if the user is authenticated or not.
Here is what i tried:
1) Middleware
export default async function ({context, redirect}) {
axios.defaults.withCredentials = true;
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//Redirect if user is authenticated
}).catch(function (error) {
console.log(error)
});
}
Here i'm sending a request to my backend to check if the user is authenticated. The problem is that the middleware is executed from server side, which means there will never be any cookie in the request, even if the user is authenticated. This means that every time i refresh the page, according to the middleware the user is always anonymous, even when the user is authenticated.
2) Plugin
export default function (context, inject) {
if (process.client){
console.log('client')
return axios({
method: 'get',
url: 'http://127.0.0.1:8000/checkAuth',
withCredentials: true,
}).then(function (response) {
//IF AUTHENTICATED, REDIRECT
context.redirect('/')
}).catch(function (error) {
console.log(error)
});
} else {
console.log('server')
}
}
Here i'm trying the same but with a plugin, and i'm "forcing" the plugin to check if the user is authenticated on the backend only when the plugin executes from client side. This works, cookies are sent in the headers and Django receives the cookie, but the problem with this solution is that Nuxt doesn't allow redirecting to other pages from a plugin (https://github.com/nuxt/nuxt.js/issues/4491).
3) Using beforeMount() in Vue
I tried to do that using beforeMount() from my Vue pages, but the problem is that since it will execute AFTER idration, the page will be loaded and after 1/2 seconds the redirect happens, which is kind of ugly.
Is it possible that there isn't a way to do this? Any kind of advice is appreciated
EDIT: the problem is not that i don't know how to code this, the problem is that when Nuxt sends a request to my backend from the server side middleware, the request will not contain any cookie, and because of this my Django backend cannot check the session cookie, which means that the backend cannot check whether or not the user is authenticated. The same code works when the middleware is executed from client side (if i navigate directly to the page instead of refreshing), because the request will contain the cookies.
I'm trying to understand if this is normal or not, but this could be an issue with Nuxt.
I know this a year old question and it was probably about nuxt 2, now nuxt 3 is out and running and I found my self with the same problem and here is how I solved it, just in case someone stumble here just like I did.
With Nuxt 3 server side you can use useFetch with the options headers: useRequestHeaders(['cookie'])
const { data, error, pending, refresh } = await useFetch(api.auth,
{
credentials: "include",
headers: useRequestHeaders(['cookie'])
}
);
There are a few issues you need to be aware of:
_ The cache, if you perform the same request with the same parameters it will return the same cached response (it won't even call the end point API). Try to use the key option with different values or the returned refresh method and check the doc "Data fetching" for more info.
_ The cookies, any cookie generate server side won't be shared with the client side, this means if the API generate a new token or session cookie on server side the browser won't receive those cookies and may generate new ones, this may get you in some 400 - bad request if you use session with CSRF, check this issue for more info.
I do have a working middleware with this
export default ({ redirect, store }) => {
if (store?.$auth?.$state?.loggedIn) {
redirect('https://secure.url')
} else {
redirect('https://login.please')
}
})

Checking the validity of JWT Tokens - beforeEnter

I've got a function that runs 'beforeEnter' in the Vue router to verify that the user has been authenticated, otherwise triggers a message.
It checks to see if a (jwt) token is saved in the localStorage - this works if the user signs out manually, as it removes the token from the localStorage. However when the token expires it still remains in the localStorage so the function thinks ((localStorage.token)) the user is logged in.
The server still blocks any requests made as the token is invalid - so is safe.
How do I check the token's validity on the server side, in the 'beforeEnter' middleware, before the page loads?
Do I need to make an endpoint that checks a tokens validity and returns the result? (I'm using fetch(), however I've seen people use axios interceptors...)
Worth nothing that I'm not using VUEX, and there seems to be more details on that?
function protectedPage(to, from, next) {
if (localStorage.token) {
next();
} else {
Vue.toasted.show("The session has ended. Please login.", {
theme: "toasted-primary",
position: "top-center",
duration: null,
action: {
text: "Login",
onClick: (e, toastObject) => {
next("/");
toastObject.goAway(0);
}
}
});
next("/");
}
}
Since exp is part of the payload, and JWT is just a base64 string, you can just decode it and check the exp time on your Vue app.
This is a function to decode JWT token and get the payload (taken from here)
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(Buffer.from(base64, "base64").toString("ascii").split("").map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
};
and check it on your beforeRouteEnter function:
beforeRouteEnter (to, from, next) {
if (localStorage.token) {
const jwtPayload = parseJwt(localStorage.token);
if (jwtPayload.exp < Date.now()/1000) {
// token expired
deleteTokenFromLocalStorage();
next("/");
}
next();
} else {
next("/");
}
},
You don't really need to check it on your backend server, since there's no security concern by decoding the JWT token payload and checking it in on the client side. Plus it saves you one HTTP request every time a user access a route.
You need a backend middleware which bound to each API call and validates user session if still exists and has same tokens.
If the session has been expired or token has been changed and doesn't match with the current user session, you can redirect user to the login page from backend and force him to create a fresh session.
I think you don't need to fetch the authentication for each route entrance, just block the backend api calls and return a message or redirect to the login page. User can still browse the pages with the expired session info but won't be able to perform any fetch or form actions.

Lambda#edge redirect gets in a redirect loop

I have a static website on aws s3. Have setup route 53 and cloud front and everything works smoothly. s3 Bucket is setup to serve index.html as index document.
Now I have added another file called index-en.html that should be served when the request country is any other country and not my home country.
For this I have added a lambda#edge function with the following code:
'use strict';
/* This is an origin request function */
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;
/*
* Based on the value of the CloudFront-Viewer-Country header, generate an
* HTTP status code 302 (Redirect) response, and return a country-specific
* URL in the Location header.
* NOTE: 1. You must configure your distribution to cache based on the
* CloudFront-Viewer-Country header. For more information, see
* http://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
* 2. CloudFront adds the CloudFront-Viewer-Country header after the viewer
* request event. To use this example, you must create a trigger for the
* origin request event.
*/
let url = 'prochoice.com.tr';
if (headers['cloudfront-viewer-country']) {
const countryCode = headers['cloudfront-viewer-country'][0].value;
if (countryCode === 'TR') {
url = 'prochoice.com.tr';
} else {
url = 'prochoice.com.tr/index-en.html';
}
}
const response = {
status: '302',
statusDescription: 'Found',
headers: {
location: [{
key: 'Location',
value: url,
}],
},
};
callback(null, response);
};
I have also edited cloud front behavior to whitelist Origin and Viewer-country headers and setup the cloudfront Viewer-Request event and lambda Function ARN relation.
However I get a "too many redirect error".
I have 2 questions:
How to correct the "too many redirects error"?
For viewers outside "TR" the default landing page should be index-en.html, from which 2 more pages in english are accessible via navigation menu. So when users request a specific page from page navigation they should be able to access those pages, when no page is requested the default landing page should be served.
Appreciate help.
Thanks.
You are creating a redirect loop because you are sending the viewer back to the same site, same page, no matter what the results of your test.
if (countryCode === 'TR') {
return callback(null, request);
} else {
...
callback(null,request) tells CloudFront to continue processing the request -- not generate a response. Using return before the callback causes the rest of the trigger code not to run.

Axios with Vue access http instead of https

I am using vue2 and axios to make my ajax calls. In a page I am calling various ajax calls, some go as HTTPS while others go through HTTP, although both codes are similar.
Example:
axios.get('/api/' + app.$variable1 + '/get-something/')
.then(({ data }) =>
{
app.array = [];
for(let i = 0; i < data.length; i++)
{
app.vats.push({
id:data[i]['id'],
name:data[i]['name'],
something_else[i]['something_else']
});
}
})
Question:
How can I force Axios to take HTTPS?
Objs:
I cannot manually add https, as such: "https://www.example.com/1234/12" because I am using relative urls (I have certain id's assigned at url, and reuse them to make my calls).
Server:
1) I am forcing Https through htaccess
2) I am also using Secure Headers which does not allow the browser to get out of "self"
EDIT:
So trying to get down to the issue:
1) In the Mounted method I am calling 4 individual API's. The first two fail due to HTTP, and the last two get through. I tried chaning the order, and its always the first two to fail. I tried to move the code to Created, which makes less sense, and sure enough it did not work.
HELP!!
Add an Axios request interceptor and change the config.url from http to https. All requests will be intercepted and you will have access to the base URL scheme.
const instance = axios.create({
baseURL: 'http://api.com',
})
instance.interceptors.request.use(function(config) {
// change the url scheme from http to https
config.url = config.url.replace('http://', 'https://')
return config
})

Express js Redirect not rendering page

I am trying to redirect the user that uses my website after he has logged into his account, he will be redirected to a dashboard.
The problem is that I can see a request for the /dashboard route in the Network tab of the browser Inspection Tools, but the page never loads.
This is my code so far.
router.post('/login', function(request, response){
// verify against database and send back response
var userData = {};
userData.email = request.body.email;
userData.password = request.body.password;
userData.rememberPassword = request.body.rememberPassword;
// check if in the database and re-route
var query = database.query('SELECT * FROM users WHERE email = ?', [userData.email], function(error, result){
if(error){
console.log('Error ', error);
}
if(result.length){
// check if the password is correct
if(userData.password === result[0].password){
// redirect to the dashboard
// TODO this piece of code does not redirect correctly
response.redirect('/dashboard'); // < HERE
console.log('Haha');
}
}
else{
// do something when there is are no results
// invalid email status code
response.statusCode = 405;
response.send('');
}
});
});
And this is the route towards the dashboard, and it is being rendered properly when accessed in the browser.
router.get('/dashboard', function(request, response){
response.render(path.resolve(__dirname, 'views', 'dashboard.pug'));
});
Why can't it redirect when the user completes the login process?
Thanks.
The issue can be in the frontend and not in the backend. If you are using AJAX to send the POST request, it is specifically designed to not change your url.
Use window.location.href after AJAX's request has completed to update the URL with the desired path.But the easiest way would be to create a form with action as /login and use the submission to trigger the url change.
To make sure that the problem does not lie with the backend,
Add a logging statement to router.get('/dashboard' to verify if
the control was passed.
Check that the HTTP status code of the /login route is 302 indicating a redirect and location header has the route /dashboard.
Content type of response /dashboard is text/html.If not please set it using res.setHeader("Content-Type", "text/html").