Axios + Ionic React + Sails: _csrf token 403 Forbidden - ionic-framework

I am developing an API with Sails.js and an user App with Ionic-React. At page load I make an axios request to get the _csrf token. When I submit the data from a login form to sails I always get a 403 Forbidden response. I disabled csrf (config/security.js) in sails and then I could retrieve the response. I am sending the token in the header.
I am trying too to get the session cookie but its not working I think that might be why the server refuses the request.
Ionic App:
componentDidMount(this: this) {
axios.get('http://localhost:1337/api/v1/security/grant-csrf-token')
.then(response => {
const _csrf = response.data._csrf
this.setState({
form: {
...this.state.form,
_csrf: _csrf,
}})
});
}
OnSubmit:
const { emailAddress, password, _csrf } = this.state.form;
const config= {
data: {
"emailAddress": emailAddress,
"password": password,
},
headers: {
"x-csrf-token": _csrf
},
withCredentials: true,
jar:cookieJar,
};
axios.post('http://localhost:1337/api/v1/users/authenticate', null, config)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})};
On Chrome DevTools Network Response:
On Postman this same request works and I get a 200 with the user data, and the request does include the sails.sid cookie.
I do not want to disable csrf protection, that wouldn't be a solution. Is it the sails.sid cookie that I am missing?

I am using this,
axios({
method: 'post',
crossdomain: true,
url: apiFormUrl,
data: formData,
headers: {
"Content-Type": "multipart/form-data",
"Authorization": access_token
}
})
.then
and it works

Related

axios not sending cookie to nestjs from NextJs

Have a front end nextJs running on a different port to the backend nestjs.
Within the nextJs session-cookie I have 2 JWT tokens access and refresh.
I can extract the access token from the next-auth session-token but axios will not send to nestjs.
If I use the { withCredentials: true } the whole next-auth token is sent but if I use the headers object nothing is received
const data = await axios.post(
process.env.NEXT_PUBLIC_SH_API_BASEURL + '/blog',
{ formData },
//{ withCredentials: true }
{
headers: {
Cookie:
'Authentication=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEzNTQ1N2FiLWI4MGUtNDU2OC1hY2RiLWNiODZmYTJlNGQxMyIsImlhdCI6MTY1MzU0NjM0NiwiZXhwIjoxNjU0NDQ2MzQ2fQ.fvnRXYwheuIOHvlTZRGqBiVR98JxdT7UqZbc6SAvcAk; Path=/; HttpOnly;'
},
}
);
nestJS - log when using with credentials
{
'next-auth.csrf-token': '493deccd24c0165f8afe08db04352415c46c7a6f150f9c51320aea0d5444589d|51953c1c056c986b799afb9dcea8c94469d35b4aab291b02ee343057fa80a70e',
'next-auth.callback-url': 'http://localhost:3000/auth/signin?callbackUrl=http://localhost:3000/',
'next-auth.session-token': 'eyJhbGc
}
But if use the headers I get
[Object: null prototype] {}
The nestJs logging is done using:
jwtFromRequest: ExtractJwt.fromExtractors([
(request: Request) => {
console.log('JWT strategy auth cookie');
console.log(request?.cookies);
return request?.cookies?.Authentication;
},
]),
If I make a call from postman there is no issue with processing the header cookie.
Could you let me know how to get axios to send the header cookie only?
Thanks

GraphQL query to GitHub failing with HTTP 422 Unprocessable Entity

I am currently working on a simple GitHub GraphQL client in NodeJS.
Given that GitHub GraphQL API is accessible only with an access token, I set up an OAuth2 request to grab the access token and then tried to fire a simple GraphQL query.
OAuth2 flow gives me the token, but when I send the query, I get HTTP 422.
Here below simplified snippets from my own code:
Prepare the URL to display on UI side, to let user click it and perform login with GitHub
getGitHubAuthenticationURL(): string {
const searchParams = new URLSearchParams({
client_id,
state,
login,
scope,
});
return `https://github.com/login/oauth/authorize?${searchParams}`;
}
My ExpressJs server listening to GitHub OAuth2 responses
httpServer.get("/from-github/oauth-callback", async (req, res) => {
const {
query: { code, state },
} = req;
const accessToken = await requestGitHubAccessToken(code as string);
[...]
});
Requesting access token
async requestToken(code: string): Promise<string> {
const { data } = await axios.post(
"https://github.com/login/oauth/access_token",
{
client_id,
client_secret,
code
},
{
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
}
);
return data.access_token;
}
Firing simple graphql query
const data = await axios.post(
"https://graphql.github.com/graphql/proxy",
{ query: "{ viewer { login } }"},
{
headers: {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
}
);
Do you guys have any clue?
Perhaps I am doing something wrong with the OAuth2 flow? As in most of the examples I found on the web, a personal token is used for this purpose, generated on GitHub, but I would like to use OAuth2 instead.
Thanks in advance for any help, I really appreciate it!
EDIT
I changed the query from { query: "query { viewer { login } }"} to { query: "{ viewer { login } }"}, nonetheless, the issue is still present.
I finally found the solution:
Change the URL from https://graphql.github.com/graphql/proxy to https://api.github.com/graphql, see here
Add the following HTTP headers
"Content-Type": "application/json"
"Content-Length"
"User-Agent"
Hope this will help others out there.

Token is generated at the endpoint but does not arrive on the page

I want to create a website with Svelte/Kit and use JWT.
I have found instructions on the internet, for example:
Svelte JWT Authentication https://morioh.com/p/1d95522418b2
SvelteKit Session Authentication Using Cookies https://www.youtube.com/watch?v=bG7cxwBMVag
But unfortunately no instructions for Svelte Kit and JWT. So I tried it myself.
The token is generated at the endpoint, but does not arrive on the page (or is not callable). I suspect that some setting in the headers is wrong, but can't figure out what is wrong. This is my highly simplified test environment:
(1) I call the endpoint login.js from the page index.svelte. For testing, I omit checking email and password and send JWT right back. Data arrives, but I don't see the JWT.
(2) The JWT should be sent to another endpoint. What is the best way to do this?
The "page" index.svelte (simplified):
<script>
let email="", password="";
const doLogin = async () => {
const response = await fetch("/auth/login", {
method: 'POST',
headers: {
"Content-Type": "application/json",
},
credentials: 'include',
body: JSON.stringify({
email,
password
})
});
if (response.status == 200) {
const { done, value } =
await response.body.getReader().read();
await console.log("done, value=", done,
JSON.parse(new TextDecoder("utf-8").decode(value)));
await console.log("headers=", response.headers);
}
}
</script>
<h1>Welcome to MyAuth</h1>
<input type=email bind:value={email}/><br/>
<input type=password bind:value={password}/><br/>
<button on:click={doLogin}>Submit</button>
The "endpoint" login.js (simplified):
import jwt from "jsonwebtoken";
export function post(request, context) {
const token = jwt.sign({
data: { text: "test" },
"topsecret",
});
const response = {
status: 200,
headers: {
'content-type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: {
passwordOk: true,
}
};
return response;
}
The console shows:
done, value= false {passwordOk: true}
index.svelte:59 headers= HeadersĀ {}
index.svelte:44 Fetch finished loading: POST "http://localhost:3000/auth/login".
doLogin # index.svelte:44
I think you are mixing up the two major parts to authentication:
Requesting/sending credentials.
Using those credentials to access protected content.
Authorization: Bearer ${token} is normally sent from the (browser) client to the server to request access to protected content. So right now, your server is asking the client for permission. This doesn't make sense.
Instead, the login endpoint should send the token via:
Set-Cookie header in the login endpoint.
The body of the response (where passwordOk is).
Set-Cookie causes the browser to send this value as a cookie with every future request. The server can check for this cookie value before serving protected content. This can be more secure because you can send an HTTP only cookie.
If the token is sent in the body of the response to login the client should send the token in future requests with the Authorization: Bearer ${token} header. The server can then check for this header before serving protected content.

Not getting refresh_token when authenticating with paypal API

Node.js
Using paypal sandbox env
const PAYPAL_BASE_URL = "https://api.sandbox.paypal.com";
const PAYPAL_TOKEN_URL = `${PAYPAL_BASE_URL}/v1/oauth2/token`;
const tokenOptions = {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Access-Control-Allow-Credentials": true,
},
data: qs.stringify({ grant_type: "client_credentials" }),
auth: {
username: `${CLIENT_ID}`,
password: `${CLIENT_SECRET}`,
},
url: `${PAYPAL_TOKEN_URL}`,
};
async function getToken() {
return await axios(tokenOptions)
.then((res) => {
console.log(res.data.access_token);
console.log(res.data.expires_in);
console.log(res.data);
return res.data;
})
.catch((err) => {
console.log(err);
});
}
Response
"scope": "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller https://uri.paypal.com/services/payments/refund https://api.paypal.com/v1/vault/credit-card https://api.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks",
"access_token": "xxxxxxxx",
"token_type": "Bearer",
"app_id": "xxxx",
"expires_in": 32346,
"nonce": "2020-11-07T20:09:09Zmc3xM34owS0WsAI5rHVx2eOJb80xJ06Z6tFQx6LT_i0"
As you can see I am not getting the refresh_token. Reading the documentation I wanted to use the refresh token to get a new access_token when the expire time is getting close.
Should I not be getting a refresh_token here?
A client credentials request using a clientid/secret does not give a refresh_token.
Just repeat the same request when needed.
#Preston PHX
Adding a comment here as the comments does not allow that many characters.
Can I do something to get a refresh_token?
Problems I have is that I am using node as a proxy with http-proxy-middleware.
app.use("/", PROXY_TO_PAYPAL);
On app startup I authenticate and store the token in a variable that will expire.
console.log(`Starting proxy at ${HOST}:${PORT}`);
console.log(`PAYPAL_TOKEN_URL: ${PAYPAL_TOKEN_URL}`);
// set the token on startup
getToken().then((data) => {
token = data.access_token;
console.log(`expires_in ${parseInt(data.expires_in)} seconds`);
const timeoutDate = getTimeoutDate(parseInt(data.expires_in) * 1000);
tokenTimeOut = timeoutDate.getUTCMilliseconds();
console.log(`Token times out at ${timeoutDate.toUTCString()}`);
});
});
I need to update the token BEFORE it expires, otherwise I risk that a proxy request will fail as it could potentially use an expired token.
I created a scheduler that will watch the when the token is getting close to expire and will renew it (call the authentication again), like say 15 min before.
async function getToken() {
return await axios(tokenOptions)
.then((res) => {
console.log(res.data.access_token);
console.log(res.data.expires_in);
console.log(res.data);
return res.data;
})
.catch((err) => {
console.log(err);
});
}
cron.schedule("* * * * *", function () {
console.log("running a task every minute");
// refresh token
if (tokenTimeOut - new Date().getUTCMilliseconds() < 900*1000) {
getToken().then((data) => {
token = data.access_token;
const timeoutDate = getTimeoutDate(parseInt(data.expires_in) * 1000);
tokenTimeOut = timeoutDate.getUTCMilliseconds();
console.log(`Token times out at ${timeoutDate.toUTCString()}`);
});
}
});
I am adding the Bearer header on each request bore it is proxied
const middlewareOptions = {
target: `${PAYPAL_BASE_URL}`, // target host
changeOrigin: true,
onProxyReq(proxyReq, req, res) {
if (!token) {
getToken().then((data) => {
token = data.access_token;
tokenTimeOut = data.expires_in;
});
}
proxyReq.setHeader("Authorization", `Bearer ${token}`);
},
};
I might get the same token again until I am closer to the expire date (I think) - which could work. Eventually I would get a new token and a new expire time.
I am confused about the documentation. Here they talk about the refresh_token https://developer.paypal.com/docs/connect-with-paypal/integrate/#6-get-access-token
But here there are not refresh_token
https://developer.paypal.com/docs/api/get-an-access-token-curl/
What I wanted to do is when I am close to the expire time I would use the refresh_token to get a new token.

send axios request multipart/files

I am trying to upload a photo to a webservice and the documentation clearly specifies that the request type is Multipart/files so I am trying this request with axios which always fails.
HTTP.put(`/employees/${response.data.data.id}/photo`, { photo: this.image }, {headers: { put: { 'Content-Type': 'multipart/files' }}})
.then((profilePictureResponse) => {
console.log(profilePictureResponse);
})
.catch((error) => {
console.log(error.response);
});
HTTP is an axios instance with default configurations but the default content-type is set to application/json