No Set-Cookie header on Axios - vue.js

I am using a Symfony API backend and want to authenticate by REST call via Axios inside a Vue App.
The authentication works fine when using POSTMAN. It stores a session cookie and resends it on subsequent requests.
However, when I use Axios it won't give me the Set-Cookie header.
What I tried so far:
Adjusted CORS settings on Symfony backend (no CORS errors - all routes working)
Set { withCredentials: true } as config parameter on Axios post request.
Symfony EventSubsriber for injecting CORS headers:
public function onKernelResponse(ResponseEvent $event)
{
$response = $event->getResponse();
$response->headers->set('Access-Control-Allow-Methods', '*');
$response->headers->set('Access-Control-Allow-Headers', [ 'Content-Type', 'Set-Cookie' ]);
$response->headers->set('Access-Control-Allow-Origin', 'http://localhost:3000');
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->setStatusCode(200);
}
Note that this is for local testing only.
Axios Request:
login(user, pw) {
return axios.post(cfg.apiUrl + '/api/login', {
username: user,
password: pw
},
{ withCredentials: true }
);
}
Follow up request:
test() {
return axios.get(cfg.apiUrl + '/api/test');
}
Could someone point out what I am still missing? Why do I get the header on POSTMAN, but not on Axios?

The solution is to also add the { withCredentials: true } in further requests. As it seems, it is not possible to read the Set-Cookie header via Axios, as #jub0bs pointed out.
The correct code for a follow-up request looks like:
test() {
return axios.get(cfg.apiUrl + '/api/test', { withCredentials: true });
}

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

Problems to get a refresh token using vue, nuxt and keycloak

I'm doing a project with vue, nuxt and keycloak as server for token, axios as http client and #nuxtjs/auth-next module for keycloak access.
I'm using a public client so I don't have a secret key which is the most recommended.
The part of getting the token and talking to the backend is working.
But as it is a public client it has no refresh token.
Searching the internet, a recommendation would be to post from time to time to the keycloak /token endpoint, passing the current token, to fetch a new token.
To perform this post, it doesn't work to pass json, having to pass application/x-www-form-urlencoded.
But it generates an error saying that the parameter was not passed.
On the internet they recommended passing it as url string, but then it generates an error on the keycloak server, as a parameter that is too long, because of the current token that is passed.
Below is the code used to try to fetch a new token.
This code is being called on a test-only button.
If anyone can help, I appreciate it.
const token = this.$auth.strategy.token.get()
const header = {
"Content-Type": "application/x-www-form-urlencoded"
}
const body = {
grant_type: "authorization_code",
client_id: "projeto-ui",
code: token
}
this.$axios ( {
url: process.env.tokenUrl,
method: 'post',
data: body,
headers: header
} )
.then( (res) => {
console.log(res);
})
.catch((error) => {
console.log(error);
} );
Good afternoon people.
Below is the solution to the problem:
On the keycloak server:
it was necessary to put false the part of the implicit flow.
it was necessary to add web-origins: http://localhost:3000, to allow CORS origins.
In nuxt.config.js it was necessary to modify the configuration, as below:
auth: {
strategies: {
keycloak: {
scheme: 'oauth2',
...
responseType: 'code',
grantType: 'authorization_code',
codeChallengeMethod: 'S256'
}
}
}

React, Axios issue: Response for preflight does not have HTTP ok status ( 404 not found )

I've been trying using axios with basic auth but it always returns not found.
Here is my code
const ROOT_URL='http://localhost/b2b_ecom/b2b-api/index.php/api/';
export function login () {
return function(dispatch)
{
const api = axios.create({
mode: 'no-cors',
credentials:'include',
redirect: 'follow',
auth: {
username: 'mouad#b2b.dz',
password: '123456'
},
headers: {
'Content-Type': 'application/json',
}
});
api.post(`${ROOT_URL}site/login `).then(function(response)
{
dispatch({LOGIN,payload:response.data});
return true;
}).catch(function(error)
{
return error;
});
} ;
}
And here it is the error I got:
OPTIONS http://localhost/b2b_ecom/b2b-api/index.php/api/site/login 404 (Not Found)
Failed to load http://localhost/b2b_ecom/b2b-api/index.php/api/site/login: Response for preflight does not have HTTP ok status.
Screenshot
I think browser is adding access control header to request.
Check the following things
1) When you hit from postman or other such api clients this api should be working properly. (even curl is fine)
2) Try adding Access-Control-Allow-Origin as * for this request on server and see if it works.

Laravel Passport 401 Unauthorized Error using Apache and Vue

i am trying to connect a generate a laravel user API using vue and laravel passport but i keep getting an authorization error in my headers . This ismy code
<script>
import Hello from './components/Hello'
export default {
name: 'app',
components: {
Hello
},
created () {
const postData = {
grant_type: 'password',
client_id: 2,
client_secret: 'sXdg5nOO4UU2muiHaQnTq4hDQjyj17Kd9AeKuNEx',
username: 'robertrutenge#gmail.com',
password: 'password',
scope: ''
}
this.$http.post('http://localhost:8000/oauth/token', postData)
.then(response => {
console.log(response)
const header = {
'Accept': 'application/json',
'Authorization': ~'Bearer ' + response.body.access_token
}
this.$http.get('http://localhost:8000/api/user', {headers: header})
.then(response => {
console.log(response)
})
})
}
}
</script>
I have done a research and most answers suggest modifying apache config file or .htaccess file but that also does not seem to work on my end . Any help will be appreciated :-)
i think if your vue app and laravel app is combine, then you can use your api without any authorization header you just need to send X-CSRF-TOKEN and send that token with each request no need to send authorization check here
https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript
this is not problem on in vue.js or larvel. i moved my l Laravel API from Apache to nginx then working fine. i updated my middleware handler like this. then working fine on Apache server
$origin = $request->server()['HTTP_ORIGIN'];
if(in_array($origin, $url)){
header('Access-Control-Allow-Origin: '. $origin);
header('Access-Control-Allow-Headers: Origin, Content-Type, Accept, Authorization, X-Csrf-Token');
}
resolve (2)
go to
AuthServiceProvider.php
keypoint:set expiration of token
Passport::tokensExpireIn(Carbon::now()->addDays(1));
You must to set token expiration
cant be infinite

vue-resource interceptor for auth headers

I am trying to set up a Vuejs fronted application (vue-cli webpack template) to sit on top of my Laravel API.
I am able to get a successful response from the API with vue-resource by providing the correct auth token, for example:
methods: {
getUser () {
this.$http.get('http://localhost:8000/api/user',
{
headers: {
'Authorization': 'Bearer eyJ0e.....etc',
'Accept': 'application/json'
}
}).then((response) => {
this.name = response.data.name
});
},
However, I am now trying to set up interceptors so that the user's auth token will automatically be added for each request.
Based on the vue-resource readme I am trying this in my main.js:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers['Authorization'] = 'Bearer eyJ0e.....etc'
request.headers['Accept'] = 'application/json'
next()
})
And then back in my component I now just have:
this.$http.get('http://localhost:8000/api/user').then((response) => {
this.name = response.data.name
});
Problem:
When I specify the headers in the get itself, I get a successful response, but when I pass them through the interceptor I get back a 401 Unauthorized from the server. How can I fix this to respond successfully while using the interceptor?
Edit:
When I use dev-tools to view the outgoing requests I see the following behavior:
When making the request by supplying the headers to $http.get, I make a successful OPTIONS request and then a successful GET request with the Authentication header being supplied to the GET request.
However, when I remove the headers from the $http.get directly and move them to the interceptor, I only make a GET request and the GET does not contain the Authentication header, thus it comes back as a 401 Unauthorized.
It turns out my problem was the syntax for which I was setting the headers in the interceptor.
It should be like this:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer eyJ0e.....etc')
request.headers.set('Accept', 'application/json')
next()
})
While I was doing this:
Vue.use(VueResource)
Vue.http.interceptors.push((request, next) => {
request.headers['Authorization'] = 'Bearer eyJ0e.....etc'
request.headers['Accept'] = 'application/json'
next()
})
Add this option:
Vue.http.options.credentials = true;
And use the interceptors for global way:
Vue.http.interceptors.push(function(request, next) {
request.headers['Authorization'] = 'Basic abc' //Base64
request.headers['Accept'] = 'application/json'
next()
});