I am using Axios module for Nuxt.js.
I do have problem with CSRF token. Whenever I log in into the app, I cannot send any POST request. The response is always 403.
Axios configuration:
Inside nuxt.config.js
axios: {
debug: true,
credentials: true,
baseURL: `${process.env.API_PROTOCOL}://${process.env.API_HOST}${process.env.API_PORT ? `:${process.env.API_PORT}` : ''}${process.env.API_PREFIX}`,
},
Inside 'axios.js' plugin / interceptor
const EXPIRED_TOKEN_MESSAGE = 'Expired JWT Token';
export default function ({
$axios, redirect, store,
}) {
$axios.setHeader('Content-Type', 'application/json');
$axios.setHeader('Accept', 'application/json');
$axios.onRequest((config) => {
const configLocal = config;
if (config.method === 'post') {
configLocal.headers['X-Requested-With'] = 'XMLHttpRequest';
configLocal.headers['x-csrf-token'] = store.state.authentication.csrf;
}
});
$axios.onError((error) => {
const { response: { data: { message } } } = error;
// const msg = status === 500 ? 'Internal Server Error' : message;
if (message === EXPIRED_TOKEN_MESSAGE && store.state.authentication.csrf) {
store.dispatch('authentication/logout');
return redirect('/');
}
if (process.client) {
// store.dispatch('alerts/addAlert', { type: 'error', message: msg });
}
return Promise.reject(error.response);
});
}
Request call:
this.app.$axios.$post('resources/add', data).then(() => {
}).catch(e => console.log(e));
Reproduction steps - under the hood of request:
After I printed request config inside $axios.onRequest as we can see from above everything is set, but in networking not at all
Going further backend server is not receiving any headers:
With console log:
`2019-07-04 18:45:18.380 DEBUG 712 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
2019-07-04 18:45:18.380 DEBUG 712 --- [nio-8080-exec-1] w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.`
Any ideas what's wrong ?
Related
I am migrating a project from Webpack to Vite and have run into an issue with proxying requests to one of the endpoints in the MVC.Net backend.
Due to circumstances of the existing project, I need to handle certain calls manually - such as on initial page load of login page, check whether user is already authenticated and redirect to the main page.
I am trying to figure out how to use server.proxy.configure to handle these requests. I am managing fine with the GET requests, but I cannot seem to receive the POST request's body data.
Here is what I have at the moment:
server: {
proxy: {
"/api": {
target: "https://my.local.environment/",
changeOrigin: true,
configure: (proxy: HttpProxy.Server, options: ProxyOptions) => {
proxy.on("proxyReq", (proxyReq, req, res, options) => {
if (req.method === "GET") {
//handle simple get requests. no problems here
//...
} else {
const buffer = [];
console.log("received post request");
proxyReq.on("data", (chunk) => {
console.log("received chunk");
buffer.push(chunk);
});
proxyReq.on("end", () => {
console.log("post request completed");
const body = Buffer.concat(buffer).toString();
const forwardReq = http.request(
{
host: "https://my.local.environment",
port: 443,
method: "POST",
path: req.url,
headers: {
"Content-Type": "application/json",
"Content-Length": data.length,
},
},
(result) => {
result.on("data", (d) => {
res.write(d);
res.end();
});
}
);
forwardReq.on("error", (error) => {
console.log(error);
});
forwardReq.write(data);
forwardReq.end();
});
}
});
},
secure: false,
},
}
}
The problem is that neither proxyReq.on("data", (chunk) => { nor proxyReq.on("end", (chunk) => { ever actually trigger.
Additionally, req.body is undefined.
I have absolutely no idea where I am supposed to be getting the POST request's body.
I ended up finding a different question about the bypass option and this gave me the solution I was looking for. Ended up only handling the specific GET requests that I need to handle locally instead of forwarding to my deployed environment, and everything else gets handled automatically by vite.
"/api": {
target: "https://my.local.environment/",
changeOrigin: true,
agent: new https.Agent({
keepAlive: true,
}),
bypass(req, res, proxyOptions) {
if (req.method === "GET") {
//... here I get what I need and write to the res object
// and of course call res.end()
}
//all other calls are handled automatically
},
secure: false,
},
I am facing a timeout issue with nestJs Httpservice.
The error number is -60 and error code is 'ETIMEDOUT'.
I am basically trying to call one api after the previous one is successfully.
Here is the first api
getUaaToken(): Observable<any> {
//uaaUrlForClient is defined
return this.httpService
.post(
uaaUrlForClient,
{ withCredentials: true },
{
auth: {
username: this.configService.get('AUTH_USERNAME'),
password: this.configService.get('AUTH_PASSWORD'),
},
},
)
.pipe(
map((axiosResponse: AxiosResponse) => {
console.log(axiosResponse);
return this.getJwtToken(axiosResponse.data.access_token).subscribe();
}),
catchError((err) => {
throw new UnauthorizedException('failed to login to uaa');
}),
);
}
Here is the second api
getJwtToken(uaaToken: string): Observable<any> {
console.log('inside jwt method', uaaToken);
const jwtSignInUrl = `${awsBaseUrl}/api/v1/auth`;
return this.httpService
.post(
jwtSignInUrl,
{ token: uaaToken },
{
headers: {
'Access-Control-Allow-Origin': '*',
'Content-type': 'Application/json',
},
},
)
.pipe(
map((axiosResponse: AxiosResponse) => {
console.log('SUCUSUCSCUSS', axiosResponse);
return axiosResponse.data;
}),
catchError((err) => {
console.log('ERRRORRRORROR', err);
// return err;
throw new UnauthorizedException('failed to login for');
}),
);
}
Both files are in the same service file. Strangely, when i call the second api through the controller like below. It works fine
#Post('/signin')
#Grafana('Get JWT', '[POST] /v1/api/auth')
signin(#Body() tokenBody: { token: string }) {
return this.authService.getJwtToken(tokenBody.token);
}
When the two api's are called, however, the first one works, the second one that is chained is giving me the timeout issue.
Any ideas?
Two things that made it work: changed the http proxy settings and used switchMap.
Hi developers I have currently problem this project is an old project where the developer created. right now, we need to adjust some of there functionality. Right now I already extract the files to my localhost folder and now work without error on run watch and artisan serve. So the problem here is on login on the console it shows that http://localhost:8000/project/oauth/token 405 (Method Not Allowed), I really don't understand why this shows on the localhost however on the live server it works.
This project created using Vue Js and Laravel for the backend.
I will show you guys the authentication function.
Login Function:
authenticate(){
this.login_alert = false
this.$validator.validateAll().then((result)=>{
if(result){
const self = this;
const authUser = {}
try{
const data = {
username: this.email,
password: this.password,
remember: this.remember_me,
client_id: '2',
client_secret: 'secret',
grant_type : 'password',
scope : ''
}
this.$store.dispatch('AUTH_REQUEST',data)
.then(response=>{
console.log(data);
authUser.access_token = response.access_token
authUser.refresh_token = response.refresh_token
authUser.expires_in = response.expires_in
window.localStorage.setItem('project_token',JSON.stringify(authUser))
/*LOGIN*/
this.login_alert = false
this.loading = false
window.location.reload()
})
.catch(error=>{
this.login_alert = true
window.localStorage.removeItem('project_token')
this.loading = false
})
}catch(err){
console.log(err);
}
}
})
}
For the AUTH REQUEST:
AUTH_REQUEST:({commit,dispatch},obj)=>{
return new Promise((resolve,reject) => {
axios({
url: '/project/oauth/token',
data: obj,
method:'post',
config:'JSON'
})
.then(response=>{
if(response.status == 200){
resolve(response.data);
}
})
.catch(error=>{
reject(error);
localStorage.removeItem('project_token');
commit('AUTH_ERROR',error);
})
})
},
Hope some one experience this. thanks.
In my case, the compilation of the route should be specified properly, for example
async purchaseDelete(purchase) {
Csrf.getCookie();
return Api.delete('/api/purchase_settings/${purchase.id}');
},
the single tick on the axios delete method didnt represent correctly
async purchaseDelete(purchase) {
Csrf.getCookie();
return Api.delete(`/api/purchase_settings/${purchase.id}`);
}
When changed to back ticks, it responded with the correct result
Is there any way to add custom headers in cloudflare?
We have some https ajax to cache static files,
but it's not handling headers like "Access-Control-Allow-Credentials" in response header and cause failure on chrome.
Scott Helme has published a way to do it using new recently released Cloudflare Workers.
https://scotthelme.co.uk/security-headers-cloudflare-worker/
let securityHeaders = {
"Content-Security-Policy": "upgrade-insecure-requests",
"Strict-Transport-Security": "max-age=1000",
"X-Xss-Protection": "1; mode=block",
"X-Frame-Options": "DENY",
"X-Content-Type-Options": "nosniff",
"Referrer-Policy": "strict-origin-when-cross-origin",
}
let sanitiseHeaders = {
"Server": "My New Server Header!!!",
}
let removeHeaders = [
"Public-Key-Pins",
"X-Powered-By",
"X-AspNet-Version",
]
addEventListener('fetch', event => {
event.respondWith(addHeaders(event.request))
})
async function addHeaders(req) {
let response = await fetch(req)
let newHdrs = new Headers(response.headers)
if (newHdrs.has("Content-Type") && !newHdrs.get("Content-Type").includes("text/html")) {
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHdrs
})
}
Object.keys(securityHeaders).map(function(name, index) {
newHdrs.set(name, securityHeaders[name]);
})
Object.keys(sanitiseHeaders).map(function(name, index) {
newHdrs.set(name, sanitiseHeaders[name]);
})
removeHeaders.forEach(function(name) {
newHdrs.delete(name)
})
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHdrs
})
}
To add custom headers, select Workers in Cloudflare.
To add custom headers such as Access-Control-Allow-Credentials or X-Frame-Options then add the following little script: -
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
let response = await fetch(request)
let newHeaders = new Headers(response.headers)
newHeaders.set("Access-Control-Allow-Credentials", "true")
newHeaders.set("X-Frame-Options", "SAMEORIGIN")
// ... and any more required headers
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders
})
}
Once you have created your worker, you need to match it to a route e.g.
If you now test your endpoint using e.g. Chrome Dev tools, you will see the response headers.
cloudflare does not support this possibility
I am building a react (ES6) application with Webpack in SailsJS.
The problem I came across is that session is not properly handled or stored in Local Memory Store IF the action is triggered from the frontend
Setup:
// TestAPIController:
module.exports = {
create: function(req, res){
// TODO: Add user authentication piece
var user = {name: 'test user'};
req.session.user = user.name;
req.session.authenticated = true;
console.log('session:', req.session);
// req.session.save();
res.json(req.session);
},
echo: function(req, res){
console.log('session:', req.session);
return res.json({message:'got it'});
}
};
Policy:
TestAPIController: {
echo : 'sessionAuth'
}
Route:
'GET /TestAPI/create' : {
controller: 'TestAPIController',
action:'create'
},
'GET /TestAPI/echo' : {
controller: 'TestAPIController',
action:'echo'
},
Front End
onClickCreate(e){
fetch('/TestAPI/create').then( res => res.json());
}
onClickEcho(e){
fetch('/TestAPI/echo').then(res => res.json());
}
MarkUp
<li><a onClick={_this.onClickCreate}>Create Test</a></li>
<li><a onClick={_this.onClickEcho}>Echo Test</a></li>
Senario A
Click on Create Console Log: [CORRECT!]
Requested :: GET /TestAPI/create
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:21:01.069Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
And Then click on Echo, Console Log: [WRONG!]
Requested :: GET /TestAPI/echo
verbose: Sending 403 ("Forbidden") response:
You are not permitted to perform this action.
Senario B
Now go to http://localhost:3000/TestAPI/create in the chrome browser's URL
Browser prints the return from the call. and Log:
Requested :: GET /TestAPI/create
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:21:13.645Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
And then enter http://localhost:3000/TestAPI/echo in url address
Browser prints the return from the call [CORRECT!]
Requested :: GET /TestAPI/echo
session: Session {
cookie:
{ path: '/',
_expires: 2017-01-18T20:25:24.019Z,
originalMaxAge: 86400000,
httpOnly: true },
user: 'test user',
authenticated: true }
Maybe there is some fundamental principle I'm not following?
Or maybe there is a better way to control sessions?
Any help would be appreciated.
You have the solution here guys: https://github.com/balderdashy/sails/issues/3965