Vue axios doesnt change header - vue.js

I am quite new to vue and I am trying to send a request to my api using axios.
I build an interceptor which seems to work (logging is happening)
export default function setup() {
console.log('Http interceptor starting...')
Axios.interceptors.request.use((request) => {
const token = store.getters.token;
if (token) {
request.headers.Authorization = `Bearer ${token}`;
}
console.log(request);
return request
}, (err) => {
return Promise.reject(err);
});
}
If I check the console I can see the request including the token. If I check my network tab in the browser i can see the same request without the token. If I check the console of my api the token is null. Any Ideas?
Edit: If I use postman with the same request and the same token it is working as it shuld

Related

Invalid csrf token with NestJS

I would like to implement Csrf protection with NestJS and Quasar.
But I think I misunderstand something...
btw I'm not doing SSR, so I don't send the form from the back to the view.
Here is the NestJs back-end code:
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: true,
bodyParser: false,
});
console.log(`your App is listening on port ${PORT}`);
// Added Cookie-parser to user csurf packages
// Prevent CSRF attack
app.use(cookieParser());
app.use(csurf({ cookie: true }));
await app.listen(PORT);
}
bootstrap();
So I'm just using CookieParser and csurf package.
On my login page I call a "csrf endpoint" just to send a cookie to the view, to send it back with the post call (login).
I still get the "invalid csrf token" AND a CORS error and don't know why....(see screen below), any suggestions to make it works ?
When I try to login, error in the browser:
And error in the back-end:
Same error if I try a request with insomnia.
I thought that the CSRF token is attached to the "web browser" to go back to the back-end with nest request, so why I'm still getting this error ?
Insomnia send the cookie automatically with the right request so the token should go back to the back-end.
Any idea ?
Regards
EDIT:
After many times reading docs, It seems that CSRF protection is for SSR only ? No need to add csrf security with SPA ? Could anyone can confirm ?
EDIT: Here's another work:
The purpose here is to send a request before login to get a csrf token that I can put into a cookie to resend when I login with a POST method.
Here is my endpoint:
import { Controller, Get, Req, Res, HttpCode, Query } from "#nestjs/common";
#Controller("csrf")
export class SecurityController {
#Get("")
#HttpCode(200)
async getNewToken(#Req() req, #Res() res) {
const csrfToken = req.csrfToken();
res.send({ csrfToken });
}
}
Here is what I've done into my main.ts file (I'll explain below):
async function bootstrap() {
const PORT = process.env.PORT;
const app = await NestFactory.create(AppModule, {
cors: {
origin: "*",
methods: ["GET,HEAD,OPTIONS,POST,PUT"],
allowedHeaders: [
"Content-Type",
"X-CSRF-TOKEN",
"access-control-allow-methods",
"Access-Control-Allow-Origin",
"access-control-allow-credentials",
"access-control-allow-headers",
],
credentials: true,
},
bodyParser: false,
});
app.use(cookieParser());
app.use(csurf({ cookie: true }));
console.log(`your App is listening on port ${PORT}`);
await app.listen(PORT);
}
bootstrap();
And here my axiosInstance Interceptors of the request in my VueJS frontend:
axiosInstance.interceptors.request.use(
(req) => {
const token = Cookies.get('my_cookie')
if (token) {
req.headers.common['Authorization'] = 'Bearer ' + token.access_token
}
req.headers['Access-Control-Allow-Origin'] = '*'
req.headers['Access-Control-Allow-Credentials'] = 'true'
req.headers['Access-Control-Allow-Methods'] = 'GET,HEAD,OPTIONS,POST,PUT'
req.headers['Access-Control-Allow-Headers'] =
'access-control-allow-credentials,access-control-allow-headers,access-control-allow-methods,access-control-allow-origin,content-type,x-csrf-token'
const csrfToken = Cookies.get('X-CSRF-TOKEN')
if (csrfToken) {
req.headers['X-CSRF-TOKEN'] = csrfToken
console.log(req)
}
return req
},
(err) => {
console.log(err)
},
Here the same for repsonse:
axiosInstance.interceptors.response.use(
(response) => {
if (response?.data?.csrfToken) {
const {
data: { csrfToken },
} = response
Cookies.set('X-CSRF-TOKEN', csrfToken)
}
return response
},
And inside my login I make a call on the mounted function of my login component:
async mounted() {
const result = await securityService.getCsrf()
},
So now to explain:
As I said I'm not building a SSR project, that's why I want to send the token into a classic axios reponse and store it in a Cookie (this part is for test I heard that storing a csrf token into a classic cookie is not the right way.)
And for each next request I get the csrf token and "attach" it to the request into the headers, making my headers "custom".
Here is a problem I don't know how to make custom headers works with nestJS and CORS, that's why I try many thing with CORS options in NestJS and writte some custome header before the request go to the back-end but without success, I've got the same error message:
I'm a bit confuse about this problem and CORS/CSRF is a big deal for spa, my questions still the same, with CORS and SameSite cookie attributes, and my api is in a subdomain of my front-end, is it really necessary to make a anti-csrf pattern ?
Btw how can I make my custom headers working and why CORS say to me there is no "Access-Control-Allow-Origin" header but there is:
try to generate csrf token and pass to front on each petition
// main.ts - from NestJs - Backend
// after app.use(csurf({ cookie: true }))
app.use((req: any, res: any, next: any) => {
const token = req.csrfToken()
res.cookie("XSRF-TOKEN", token)
res.locals.csrfToken = token
next()
})
from: https://github.com/nestjs/nest/issues/6552#issuecomment-1175270849

Handling an authentication page returned by an axios request in vue

I have a vue app that sits behind a firewall, which controls authentication. When you first access the app you need to authenticate after which you can access the app and all is well until the authentication expires. From the point of view of my app I only know that the user needs to re-authenticate when I use axios to send off an API request and instead of the expected payload I receive a 403 error, which I catch with something like the following:
import axios from 'axios'
var api_url = '...'
export default new class APICall {
constructor() {
this.axios = axios.create({
headers: {
'Accept': 'application/json',
'Cache-Control': 'no-cache',
'Content-Type': 'application/json',
},
withCredentials: true,
baseURL: api_url
});
}
// send a get request to the API with the attached data
GET(command) {
return this.axios.get(command)
.then((response) => {
if (response && response.status === 200) {
return response.data; // all good
} else {
return response; // should never happen
}
}).catch((err) => {
if (err.message
&& err.message=="Request failed with status code 403"
&& err.response && err.response.data) {
// err.response.data now contains HTML for the authentication page
// and successful authentication on this page resends the
// original axios request, which is in err.response.config
}
})
}
}
Inside the catch statement, err.response.data is the HTML for the authentication page and successfully authenticating on this page automatically re-fires the original request but I can't for the life of me see how to use this to return the payload I want to my app.
Although it is not ideal from a security standpoint, I can display the content of err.response.data using a v-html tag when I do this I cannot figure out how to catch the payload that comes back when the original request is fired by the authentication page, so the payload ends up being displayed in the browser. Does anyone know how to do this? I have tried wrapping everything inside promises but I think that the problem is that I have not put a promise around the re-fired request, as I don't have direct control of it.
Do I need to hack the form in err.response.data to control how the data is returned? I get the feeling I should be using an interceptor but am not entirely sure how they work...
EDIT
I have realised that the cleanest approach is to open the form in error.response.data in a new window, so that the user can re-authenticate, using something like:
var login_window = window.open('about:blank', '_blank');
login_window.document.write(error.response.data)
Upon successful re-authentication the login_window now contains the json for the original axios get request. So my problem now becomes how to detect when the authentication fires and login_window contains the json that I want. As noted in Detect form submission on a page, extracting the json from the formatting window is also problematic as when I look at login_window.document.body.innerText "by hand" I see a text string of the form
JSON
Raw Data
Headers
Save
Copy
Collapse All
Expand All
status \"OK\"
message \"\"
user \"andrew\"
but I would be happy if there was a robust way of determining when the user submits the login form on the page login_window, after which I can resend the request.
I would take a different approach, which depends on your control over the API:
Option 1: you can control (or wrap) the API
have the API return 401 (Unauthorized - meaning needs to authenticate) rather than 403 (Forbidden - meaning does not have appropriate access)
create an authentication REST API (e.g. POST https://apiserver/auth) which returns a new authentication token
Use an Axios interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 401) { // can't handle
return error;
}
this.token = await this.axios.post("auth", credentials);
error.config.headers.authorization = `Bearer ${this.token}`;
return this.axios.request(config);
}
return error; // not an axios error, can't handler
});
The result of this is that the user does not experience this at all and everything continues as usual.
Option 2: you cannot control (or wrap) the API
use an interceptor:
this.axios.interceptors.response.use(function onResponse(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// no need to do anything here
return response;
}, async function onResponseError(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
if ("response" in error && "config" in error) { // this is an axios error
if (error.response.status !== 403) { // can't handle
return error;
}
if (!verifyLoginHtml(error.response.data)) { // this is not a known login page
return error;
}
const res = await this.axios.post(loginUrl, loginFormData);
return res.data; // this should be the response to the original request (as mentioned above)
}
return error; // not an axios error, can't handler
});
One solution is to override the <form>'s submit-event handler, and then use Axios to submit the form, which gives you access to the form's response data.
Steps:
Query the form's container for the <form> element:
// <div ref="container" v-html="formHtml">
const form = this.$refs.container.querySelector('form')
Add a submit-event handler that calls Event.preventDefault() to stop the submission:
form.addEventListener('submit', e => {
e.preventDefault()
})
Use Axios to send the original request, adding your own response handler to get the resulting data:
form.addEventListener('submit', e => {
e.preventDefault()
axios({
method: form.method,
url: form.action,
data: new FormData(form)
})
.then(response => {
const { data } = response
// data now contains the response of your original request before authentication
})
})
demo

Accessing the response from one GET request within another

I'm working with Vue to interact with an external API on a Drupal website, but in order to do so dynamically per Drupal user, I need to get a token from Drupal first. To do so, I'm trying to do two GET requests. The first gets me a bearer token out of Drupal, and the second uses it to authenticate the third-party API request.
Below is what I'm trying – I'm able to get the token successfully in the first request's response, but not when I try to use it in the header of my second request. If I try hardcoding the token that I see in the console log, it does work, so I know none of that is the issue. It's just that this.jwt['data']['token'] in the second request's headers seems to not pull back the token.
What do I need to adjust in order to access the token from the first response as part of the headers of my second request?
created() {
axios
.get('/jwt/token')
.then(response => {
this.jwt = response
console.log(this.jwt['data']['token']) // this does show what I want to access later
})
},
mounted() {
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: "Bearer " + this.jwt['data']['token'] } // ...but this doesn't work
})
.then(response => {
this.comments = response
})
},
It's likely the response to the token request has not finished by the time the component mounts, at which point this.jwt is not yet assigned.
I would move the token request into the mounted hook, fetching comments only when the token request succeeds:
export default {
mounted() {
axios
.get('/jwt/token')
.then(tokenResp => {
this.jwt = tokenResp
axios
.get('/comment/doc/' + this.id, {
headers: { Authorization: 'Bearer ' + this.jwt['data']['token'] }
})
.then(commentsResp => {
this.comments = commentsResp
})
})
}
}

AWS-amplify Including the cognito Authorization header in the request

I have create an AWS mobile hub project including the Cognito and Cloud logic. In my API gateway, I set the Cognito user pool for the Authorizers. I use React native as my client side app. How can I add the Authorization header to my API request.
const request = {
body: {
attr: value
}
};
API.post(apiName, path, request)
.then(response => {
// Add your code here
console.log(response);
})
.catch(error => {
console.log(error);
});
};
By default, the API module of aws-amplify will attempt to sig4 sign requests. This is great if your Authorizer type is AWS_IAM.
This is obviously not what you want when using a Cognito User Pool Authorizer. In this case, you need to pass the id_token in the Authorization header, instead of a sig4 signature.
Today, you can indeed pass an Authorization header to amplify, and it will no longer overwrite it with the sig4 signature.
In your case, you just need to add the headers object to your request object. For example:
async function callApi() {
// You may have saved off the JWT somewhere when the user logged in.
// If not, get the token from aws-amplify:
const user = await Auth.currentAuthenticatedUser();
const token = user.signInUserSession.idToken.jwtToken;
const request = {
body: {
attr: "value"
},
headers: {
Authorization: token
}
};
var response = await API.post(apiName, path, request)
.catch(error => {
console.log(error);
});
document.getElementById('output-container').innerHTML = JSON.stringify(response);
}
Tested using aws-amplify 0.4.1.

vue-resource not passing token in request headers

I'm new to Vuejs 2, currently using vue-resource to retrieve data from the server. However, I would need a token passed in the request header at the same time in order to retrieve the data from the server.
So the problem is, I am unable to retrieve data because the token is not passed into the request header, using vue-resource.
Here is the method that uses the vue-resource's interceptor (to pass in the token) to intercept the GET request:
test () {
this.$http.interceptors.push((request) => {
var accessToken = window.localStorage.getItem('access_token')
request.headers.set('x-access-token', accessToken)
return request
})
this.$http.get(staffUrl)
.then(response => {
console.log(response)
}, (response) => {
console.log(response)
})
}
Documentation for vue-resource, HTTP: https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
When I try to GET the data, i end up with an error 403 (forbidden) and after checking the request headers in the dev tools, I also could not find the token in the request headers.
Please tell me where I went wrong because I'm really new to this so i appreciate any help! Thank you!
Setting interceptors inside the component using $http doesn't work, or at least it doesn't in my testing. If you examine/log this.$http.interceptors right after your push in the test method, you'll note that the interceptor was not added.
If you add the interceptor before you instantiate your Vue, however, the interceptor is added properly and the header will be added to the request.
Vue.http.interceptors.push((request, next) => {
var accessToken = "xyxyxyx"
request.headers.set('x-access-token', accessToken)
next()
})
new Vue({...})
Here is the test code I was using.
Also note, if you are using a version prior to 1.4, you should always call the next method that is passed to the interceptor. This does not appear to be necessary post version 1.4.
please go through this code
import vueResource from "vue-resource";
import { LocalStorage } from 'quasar'
export default ({
app,
router,
Vue
}) => {
Vue.use(vueResource);
const apiHost = "http://192.168.4.205:8091/";
Vue.http.options.root = apiHost;
Vue.http.headers.common["content-type"] = "application/json";
Vue.http.headers.common["Authorization"] = "Bearer " + LocalStorage.get.item("a_t");
Vue.http.interceptors.push(function(request, next) {
console.log("interceptors", request);
next(function(response) {
});
});
}