CSRF Protection with Flask/WTForms and React - reactjs

Has anyone successfully implemented CSRF protection for a form submitted with React (as a controlled component) to a Flask back-end (ideally with WTForms)? I've seen a lot of partial answers, and one with Django, but couldn't find anything definitive for Flask. My big issue seems to be that I don't know how to send the csrf token to my react front end, store it as a header before submitting my form, then submit my form with the correct token. Any direction would be really helpful.

So, essentially what I did is I set up a route in Flask that can receive both GET and POST requests. React sends a GET request when the component mounts, and Flask responds with the csrf token as a header (done manually). Then React stores this value in state. When the form is submitted, the csrf token from state is sent as a field, similar to the way it is sent in a pure Flask app, where it would be a hidden field. While this technically works, I am curious if this is still vulnerable to CSRF. I think the next best option is to set up CSRF protection on all endpoints, so can try that if this isn't secure.
Flask route:
#app.route('/api/login', methods=['GET', 'POST'])
def login():
form = LoginForm()
print(request.method)
if request.method == 'GET':
return ('', {'csrf_token': form.csrf_token._value()})
elif form.validate_on_submit():
return { 'message': 'Login successful' }, 200
else:
return { 'errors': form.errors }
GET request in componentDidMount:
componentDidMount() {
axios.get('/api/login',{data: null, headers: {'Content-Type': 'application/json'}})
.then(res => {
console.log(res)
this.setState({
csrf: res.headers.csrf_token
});
})
}
POST request when form is submitted:
onSubmitLogin = e => {
e.preventDefault();
const userData = {
username: this.state.username,
password: this.state.password,
csrf_token: this.state.csrf
};
axios({
method: 'post',
url: '/api/login',
data: userData,
headers: {
'content-type': 'application/json'
}
})
.then(res => {
console.log(res);
});
}

Maybe you need flask-security-too lib
https://flask-security-too.readthedocs.io/en/stable/patterns.html#csrf

Related

How can i access the cookie sent as a response from express in react js?

I am using mern stack to make an application. For authentication i am using toke auth with passport authentication and for security reasons i am sending token in cookie. I have a login call which returns a cookie with response. The snippet is below:
res.cookie("cookie_token", token, { maxAge: 84600 });
res.send({
status: "success"
});
I can see the cookie in postman and even in browser in network(xhr request).
I am using axios for making call to the login api in react js.
axios.get(myapiurl, {
headers: {
email: fields.email,
password: fields.password,
"access-control-allow-origin": "*"
}
})
.then(res => {
console.log(res);
}).catch((err) => {
console.log(err);
});
Though i can't find a snippet to access the cookie in react js. How can i parse it? I can't see cookie in response of axios though? How can i access it.
Please try this. Snippet from mdn.
function getCookie(sKey) {
if (!sKey) { return null; }
return document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + sKey.replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1") || null;
}
export function getLoginInfo() {
const cookieToken = getCookie('cookie_token')
console.log(cookieToken)
}

How to handle POST request in reactjs?

I have my ReactJS app running in http://localhost:3000/. I am receiving below form data to my React page as a POST request
<form action="http://localhost:3000/" method="post">
Employee Id: <input type="text" name="eid"><br>
Department Id: <input type="text" name="did"><br>
<input type="submit" value="Submit">
</form>
My react app should be able to handle this POST request and render the UI as below
<h1>{eid}</h1>
<h1>{did}</h1>
I am able to handle GET request using react router but struggling to handle POST request. How can I achieve this?
That is not possible if your React app is static(not server side rendered).
When you send some POST request to your react app, nginx(or other server) will not allow that kind of action(you cannot post to static files)
Even if you bypass that restriction, react script will not have any data from your POST request, because nginx will process your request and return just a html with react script to you
It will not work like php.. you need to have something like backend (node or php to pass the data) or even some site to accept the request..
First, you need maybe some theoretical view:
https://pusher.com/tutorials/consume-restful-api-react
https://www.robinwieruch.de/react-hooks-fetch-data
You should firstly save data
You save them to the state
You display them in the part where it is rendered
To download data from api (GET)- you don't do it directly in form - you only use either ComponentDidMount or UseEffect.
componentDidMount() {
fetch(ApiURL)
.then(res => res.json())
.then(res => this.setState({ planets: res }))
.catch(() => this.setState({ hasErrors: true }));
}
useEffect(async () => {
const result = await axios(
ApiURL,
);
setData(result.data);
});
To send data to api (POST)- It's complicated - you need information about client-server communication
Straight from the React docs:
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
})
})

How do I fix 'The access token is from wrong audience or resource.' when trying to access Azure's REST api using an MSAL token

I'm creating a node web app that needs to integrate with some azure services using Azure REST API. I'm using the node MSAL library for user login and retrieving an access token for making requests to the Azure REST api. I'm able to login a user successfully and retrieve an access token. However, when I try to make a request to Azure's REST api I am receiving a 401 error that says error="invalid_token", error_description="The access token is from wrong audience or resource."
The web app itself is built with Node LTS and React v16.8.6. It has been deployed in Azure and registered with active directory.
I'm using MSAL v1.0.1 to login a user and retrieve a token when the user lands on the login page.
login.js
import React, { Component } from 'react';
import * as Msal from 'msal';
let msalConfig = {
auth: {
clientId: process.env.REACT_APP_CLIENT_ID,
authority: `https://login.microsoftonline.com/process.env.REACT_APP_TENANT_ID'`,
navigateToLoginRequestUrl: false,
redirect_uri: 'http://localhost:3000/newEntry',
resource: 'https://management.azure.com/'
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var msalInstance = new Msal.UserAgentApplication(msalConfig);
var tokenRequest = {
scopes: ['https://management.azure.com/']
}
class Login extends Component {
constructor(props) {
super(props);
this.state = {
response: null,
};
}
componentWillMount() {
// prevent login loop by checking for logged in user and then acquire a token if logged in
if (!msalInstance.getAccount() && !msalInstance.isCallback(window.location.hash)) {
this.login();
} else {
msalInstance.acquireTokenSilent(tokenRequest)
.then(res => localStorage.setItem('access_token', res.accessToken))
.catch(err => console.error(err))
}
}
login = () => {
msalInstance.handleRedirectCallback((error, response) => {
if (error) console.error(error);
console.log(response);
})
msalInstance.loginRedirect(tokenRequest);
}
render() {
return (
<div>
<h2>Login</h2>
</div>
)
}
}
export default Login;
I am successfully returning an access token and putting it in local storage.
On a separate page I am retrieving the MSAL access token from local storage and using it to make a request to retrieve all resources associated with my Azure subscription.
componentWillMount() {
let token = localStorage.getItem('access_token');
let url = 'https://management.azure.com/subscriptions/process.env.REACT_APP_SUBSCRIPTION_ID/resource?api-version=2018-02-14'
fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`
}
})
.then(response => console.log(response))
.catch(err => console.error(err));
}
I suspect I'm getting the error because there is some discrepancy between the resource value sent with the login request and the scope value sent with the token request.
I've attempted changing the resource value to 'resource': 'https://management.core.windows.net/' per Azure: The access token has been obtained from wrong audience or resource but that didn't change anything.
I've also attempted various scopes including https://management.azure.com/user_impersonation and https://management.azure.com//user_impersonation following this example Access Token do not include access for API with MSAL
The get method is default, and the problem might be in 'Authorization': `Bearer ${token}`.
Sometimes people use different rules, like 'Authorization': token, or 'Token': `Bearer ${token}`.
I see some API using 'S-token': token. try that. I thinkit is because the token did get there.
You are using msal, so resource parameter is not needed in your msalConfig. You can remove it.
Change the scopes to scopes: ['https://management.azure.com/.default'].
So I ended up switching to the ADAL library to see if I would get the same error and I did. I did however realize the resource request URL wasn't correct. The api-version wasn't an accepted version. Once I switched it to 2018-02-01 the request returned 200.

Get csrftoken cookie with React

I am making my first app with React, interacting with a Django Rest Framework back-end application. They both are running in their local servers.
I found that requests from the React front-end (that are sent with Axios) requiere a csrftoken cookie. I read and followed Django Documentation about it, but I always get an undefined csrftoken cookie.
This is the code of the request:
handleClick() {
var csrftokenCookie = Cookies.get('csrftoken');
console.log(csrftokenCookie);
const axios = require('axios');
axios.post('http://127.0.0.1:8000/es/api-auth/login/', {
next: '/',
username: 'some_name',
password: 'secret'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
}
I print to console the result of the Cookies.get('csrftoken'), and I can see that it is always undefined.
I don't know what I am missing in order to be able to get the csrftoken cookie.

Cannot Basic Auth from React App with Axios or SuperAgent

I try to make a GET request with axios and I always get 401. This happens only when I send the request from my react app.
axios.get('http://localhost:8080/vehicles', {
withCredentials: true,
auth: {
username: 'admin',
password: 'admin'
},
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
})
Postman GET request with the same credentials and Basic Auth set, works.
If I simply type in my browser the url, a login box appears and with the same credentials, it works, I get what I want.
I tried with SuperAgent too:
Request.get("http://localhost:8080/vehicles")
.withCredentials()
.auth('admin', 'admin')
.then(response => {
return response.body
});
Still nothing.
I saw a question related to this one but no one answered it.
(Basic authentication : failure supergaent+OSX , success on superagent+Redhat , success on Postman+OSX,)
Can anyone point me to the right direction?
Thank you for reading.

Resources