Adding CSRF tokens to HTTP headers in react/spring boot project - javascript

I've been working on a React/Spring project with the ambition of better understanding spring security and while going fairly successful thus far I've found a shortage of information relating to the handling of CSRF tokens between React and Spring boot. As such, I'm at an impasse.
My question is: How do you add and authenticate a CSRF token between React and Spring Boot on POST requests?
So far I've managed to get the CSRF token into my Cookies (thanks spring) and from there, I've attempted accessing the CSRF token and adding it to my HTTP headers although still receiving 403 responses on POST requests.
My spring security config class contains the declaration enabling CSRF outside of http(withHttpOnlyFalse() ).
How I'm trying to access the CSRF token:
I found this online previously for accessing the cookie:
function getCookie(name) {
if (!document.cookie) {
return null;
}
const xsrfCookies = document.cookie.split(';')
.map(c => c.trim())
.filter(c => c.startsWith(name + '='));
if (xsrfCookies.length === 0) {
return null;
}
return decodeURIComponent(xsrfCookies[0].split('=')[1]);
}
How I'm declaring HTTP headers:
let csrfToken = getCookie("XSRF-TOKEN");
console.log("testing csrf token: " + csrfToken);
const res = await fetch(`/register`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": csrfToken,
},
body: JSON.stringify({
firstName: firstName,
lastName: lastName,
mobileNumber: mobileNumber,
email: email,
password: password,
}),
});
Any help/support is greatly appreciated.

I've managed to resolve this.
Considering the difficulty I had sourcing information here's my solution:
Install the react-cookies library (npm install react-cookies)
Inside of the component which triggers the POST request, declare the following:
const cookies = useCookies(['XSRF-TOKEN']);
Pass 'cookies' to your function which facilitates the fetch request - For me this was simply called 'signUp' and was called inside my handleSubmit() method.
const handleSubmit = (event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
signUp(data.get("firstName"), data.get("mobileNumber"), data.get("email"),
data.get("password"), setUser, cookies['XSRF-TOKEN'] );
}
My fetch request inside of signUp() looks like the following:
await fetch(`/register`, {
headers: {
"X-XSRF-TOKEN": token,
"Content-Type": 'application/json'
},
credentials: 'include',
method: "POST",
body: JSON.stringify(customerData),
});
I'm sure this isn't the cleanest, nor the most practical way to do this and I hope someone is willing to shed further clarity if this is the case.
This was posted on the basis that I struggled to find a solution and hope this may be of some use/help going forwards.

Related

JWT Bearer Keeps returning 401 Status = Bearer error="invalid_token", error_description="The signature is invalid"

Hello everyone it is been 6 hours I am struggling to solve this issue.
I have the following projects:
Client App: ReactJS using axios library
Server App: .NET Core Web api implementing JWT for authorization and authentication.
The Problem:
when trying to send get request from my react application using axios to the backend and attaching the jwt in the header I always get 401 unauthorized.
I tried the same way using postman It works perfectly !!!!!!!!!!
My attempts:
I tried to add the cors to my api and allows every origin, every header, every method still did not work.
Sending Request From ReactJS using axios:
async function getAllUserTasks() {
try {
return axios({
method: "get",
url: "http://localhost:5133/todo/ToDos",
headers: {
Authorization: `Bearer ${localStorage.getItem("jwtToken")}`,
},
body: {
userId: JSON.stringify('924BF80F-F394-4927-8DCC-A7B67AFA663C')
},
});
} catch (error) {
console.log(error);
}
}
//call the function one time
useEffect(() => {
getAllUserTasks();
}, []);
My config for the JWT in .NET app:
services.AddAuthentication(defaultScheme: JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings.Issuer,
ValidAudience = jwtSettings.Audience,
IssuerSigningKey = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(jwtSettings.Secret))
});
My config for policy and cors:
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.WithOrigins("http://localhost:3000", "http://localhost:3000/")
.AllowAnyMethod()
.AllowAnyHeader();
}));
This is really frustrating!
Try post request and also get the token from local storage outside of request definition. I think one of theese will fix your problem.

How to I get token?

I am trying to implement token based authentication. The code below works on tutorial I'm watching but when I run it, I can't don't get any token. Here is the code :
axios.defaults.baseURL = "http://localhost:3002";
const [userLogin, setUserLogin] = useState({
username: "",
password: "",
});
const { username, password } = userLogin;
const handleSubmit = async (e) => {
e.preventDefault();
const data = userLogin;
const response = await axios.post("/login", data, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
credentials : 'include'
});
};
Preview on Network :
Any help will be appreciated. Thanks.
I don't understand the question clearly but I think that you need to do this.
yourwebscript (request) > backend server running for example nodejs js > the backend server returns a token (you send username and password).
Now you have the token and you can make the request with token.
I think I have understand it, you need to read de response with e.responseJSON in the success or completed section.

Auth0 React Native patch user by Management API

I want to patch the user_metadata of a user in my Auth0 Authentication in react native but I get the following error:
{"error": "Unauthorized", "message": "Missing authentication", "statusCode": 401}
So I am importing Auth0 in my react native:
import Auth0 from "react-native-auth0";
const auth0 = new Auth0({
domain: Config.AUTH0_DOMAIN,
clientId: Config.AUTH0_CLIENT_ID
});
I use the constants Config.AUTH0_DOMAIN and Config.AUTH0_CLIENT_ID from my dashboard from my application.
As a next step I execute the following code:
login = () => {
auth0.webAuth
.authorize({
scope: Config.AUTHO_SCOPE,
audience: Config.AUTH0_AUDIENCE,
device: DeviceInfo.getUniqueID(),
prompt: "login"
})
.then(res => {
auth0.auth
.userInfo({token: res.accessToken})
.then(data => {
fetch(`https://<MY_AUTH_DOMAIN>/api/v2/users/${encodeURIComponent(data.sub)}`, {
method: "PATCH",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
"metadata": {first_name: 'John', last_name: 'Doe', skillLevel: 'PRO!'}
})
}).then(res => res.json())
.then(async (data) => {
try {
console.log('user stored', data);
} catch (e) {
console.log("error while user storing", e)
}
})
})
})
}
Whereby Config.AUTHO_SCOPE and Config.AUTH0_AUDIENCE is also from my auth0's app dashboard.
Am I missing some authentication in my headers or is the Management API the wrong choice? Do I need to to this query probably from my Back-End?
Resources:
Official API Doc from the Management API: https://auth0.com/docs/api/management/v2?_ga=2.147997749.368915485.1617866251-2089752765.1617460658#!/Users/patch_users_by_id
Official react-native-auth0 doc: https://auth0.github.io/react-native-auth0/src_management_users.js.html
Thanks for the help!
I was having this issue and I got it working after a little work.
First, I had to configure my project to give metadata write permission in Auth0's dashboard at Applications/Apis.
The two I added were read:current_user and update:current_user_metadata.
Then, in my authorize request, I modified both scope and audience.
audience: 'https://<MY APP DOMAIN>/api/v2/'
scope: 'read:current_user update:current_user_metadata openid profile offline_access'
Next, I got the userId by passing my authentication token to auth.userInfo like so.
auth0.auth.userInfo({token: authToken}).then((response)=>{
return response.sub;
})
Finally, I used the value returned from response.sub along with the authToken that I had setup with special audience and scope to patch the user, and it worked successfully. UserId is what response.sub returned.
auth0.users(authToken).patchUser({id: userId, metadata: newUserMetadata});
EDIT:
One other issue I see with your code snippet, if you want to use fetch, is you didn't put a bearer authorization token in the header. Your fetch response will probably return with a 401 error if that's the case. The code should look something like this:
const serverResponse = await fetch('https://<MYAPP>.auth0.com/api/v2/users/' + user.sub,{
headers:{
Authorization: 'Bearer ' + accessToken
}
})

how do I make a post and get request with ReactJS, Axios and Mailchimp?

I am new to ReactJS and I am trying to build this app that need to use mailchimp so the user can subscribe for newsletter. I need to make a request using axios? can someone guide me through this? where do i put my api key? Did I do it correct in the bellow code? i put my mailchimps username in 'username' and my apikey in 'xxxxxxxxxxxxxxxxxxx-us16', however, i got the 401 error saying Unauthorized, BUT my console did say Fetch Complete: POST and caught no error.
import React, {Component} from 'react';
import './Subscribe.css';
class Subscribe extends Component {
sub = () => {
let authenticationString = btoa('username:xxxxxxxxxxxxxxxxxxx-us16');
authenticationString = "Basic " + authenticationString;
fetch('https://us16.api.mailchimp.com/3.0/lists/xxxxxxxxx/members', {
mode: 'no-cors',
method: 'POST',
headers: {
'Authorization': authenticationString,
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
email_address: "somedude#gmail.com",
status: "subscribed",
})
}).then(function(e){
console.log('complete')
}).catch(function(e){
console.log("fetch error");
})
};
render () {
return(
<div>
<button onClick={this.sub}> subscribe </button>
</div>
);
};
};
In the documentation, the curl example uses the --user flag. Using this to convert curl commands to an equivalent js code, you need the 'auth' property on the option object of the fetch to make it work.
fetch('https://us16.api.mailchimp.com/3.0/lists/xxxxxxxxx/members', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
email_address: "somedude#gmail.com",
status: "subscribed",
},
auth: {
'user': 'username',
'pass': 'xxxxxxxxxxxxxxxxxxx-us16'
})
})
It took me a while to get the syntax right for this. This is an example of a working request using nodejs in a server-side-rendered reactjs app using axios.
It appears "get" requests won't work for this method because of the 401 error: MailChimp does not support client-side implementation of our API using CORS requests due to the potential security risk of exposing account API keys.
However, patch, put, and post seem to work fine.
Example (using async / await)
// Mailchimp List ID
let mcListId = "xxxxxxxx";
// My Mailchimp API Key
let API_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxx-us12";
// Mailchimp identifies users by the md5 has of the lowercase of their email address (for updates / put / patch)
let mailchimpEmailId = md5(values["unsubscribe-email-address"].toLowerCase());
var postData = {
email_address: "somedude#gmail.com",
status: "subscribed"
};
// Configure headers
let axiosConfig = {
headers: {
'authorization': "Basic " + Buffer.from('randomstring:' + API_KEY).toString('base64'),
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
try {
let mcResponse = await axios.post('https://us12.api.mailchimp.com/3.0/lists/' + mcListId + '/members', postData, axiosConfig)
console.log("Mailchimp List Response: ", mcResponse);
} catch(err) {
console.log("Mailchimp Error: ", err);
console.log("Mailchimp Error: ", err["response"]["data"]);
}
You can using the method described there: AJAX Mailchimp signup form integration
You will need to use JSONP otherwise you will get a CORS error.
If you use a modern environment (I mean not jQuery), you can achieve this method using a library like qs or queryString to transform your form data to an uri.
Your final url could look something like:
jsonp(`YOURMAILCHIMP.us10.list-manage.com/subscribe/post-json?u=YOURMAILCHIMPU&${queryString.stringify(formData)}`, { param: 'c' }, (err, data) => {
console.log(err);
console.log(data);
});
It's a bit hacky and I guess Mailchimp can remove this from one day to the other as it's not documented, so if you can avoid it, you'd better do.

Post form data with axios in Node.js

I'm testing out the Uber API on Postman, and I'm able to send a request with form data successfully. When I try to translate this request using Node.js and the axios library I get an error.
Here is what my Postman request looks like:
The response I get is: { "error": "invalid_client" }
Here is what I'm doing in Node.js and axios:
var axios = require("axios");
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
axios.post('https://login.uber.com/oauth/v2/token', {
client_id: '***',
client_secret: '***',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:8080/',
code: '***'
}, config)
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
When I do this, I get a 400 response.
I added the 'multipart/form-data' header because I filled out the form-data in the Postman request. Without the header I get the same result.
I'm expecting to get the same response I'm getting from Postman, is there something wrong with my config variable in the Node.js script?
Any help would be appreciated!
You might be able to use Content-Type: 'application/x-www-form-urlencoded'. I ran into a similar issue with https://login.microsoftonline.com where it was unable to handle incoming application/json.
var axios = require("axios");
axios({
url: 'https://login.uber.com/oauth/v2/token',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: `client_id=${encodeURIComponent('**')}&client_secret=${encodeURIComponent('**')}&grant_type=authorization_code&redirect_uri=${encodeURIComponent('http://localhost:8080/')}&code=${encodeURIComponent('**')}`
})
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
You could also use a function to handle the translation to formUrlEncoded like so
const formUrlEncoded = x =>
Object.keys(x).reduce((p, c) => p + `&${c}=${encodeURIComponent(x[c])}`, '')
var axios = require("axios");
axios({
url: 'https://login.uber.com/oauth/v2/token',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
data: formUrlEncoded({
client_id: '***',
client_secret: '***',
grant_type: 'authorization_code',
redirect_uri: 'http://localhost:8080/',
code: '***'
})
})
.then(function(response) {
console.log(response.data)
})
.catch(function(error) {
console.log(error)
})
Starting with Axios 1.3, you can send multipart/form-data data using FormData:
const axios = require('axios');
const form = new FormData();
form.append('my_field', 'my value');
form.append('my_other_field', 'my second value');
axios.post('http://example.com', form)
FormData is available on Node 17.6.0 (or newer), on older versions you'll have to use a polyfill such as form-data.
If you're using older versions of both Node and Axios, you have to set the Content-Type header yourself as well:
const axios = require('axios');
const FormData = require('form-data');
const form = new FormData();
axios.post('http://example.com', form, { headers: form.getHeaders() })
To send data with Content-Type application/x-www-form-urlencoded, wrap your {} with new URLSearchParams(). Like this snippet:
const axios = require("axios");
axios
.post("https://api.zipwhip.com/user/login", new URLSearchParams({
username: "hello",
password: "byby",
}))
.then((res) => console.log(res.data));
As for 10 June 2017, axios library does not support posting form data in Node.js. Here is the issue on GitHub - https://github.com/mzabriskie/axios/issues/789
We had the similar problem and decided to switch to request library.
This use case is now clearly documented with multiple solutions in the axios docs: https://axios-http.com/docs/urlencoded#form-data
From the error it seems your client_id or client_secret is incorrect. Enable debugging and share the raw request/response (after filtering credentials out).
It is not true! You can post data with axios using nodejs. I have done it. The problem is, if you use PHP on the server side, there is a pitfall you need to be aware of. Axios posts data in JSON format (Content-Type: application/json) PHP's standard $_POST array is not populated when this content type is used. So it will always be empty. In order to get post parameters sent via a json request, you need to use file_get_contents("http://php://input") .
A simple PHP script on the server side would be:
if($_SERVER['REQUEST_METHOD']==='POST' && empty($_POST)) {
$_POST = json_decode(file_get_contents('http://php://input'));
}
Using this method you can avoid the formData dependency. You CAN post data directly from node.js.

Categories