CSRF cookie not set [Django/AngularJS] - angularjs

First of all, this is not a duplicate question. I have tried all the related posts on stackoverflow but could not find a solution.
I have a Django app for the backend and and AngularJS app for the frontend. I am using djangorestframework-jwt for API authentication. I have an API endpoint that does not require any authentication and I am getting CSRF Failed: CSRF cookie not set error on the browser only for this endpoint.
In my django settings I have:
ALLOWED_HOSTS = ['*']
and it does not include any CSRF_COOKIE_SECURE, CSRF_COOKIE_HTTPONLY, or SESSION_COOKIE_SECURE settings.
The djangorestframework-jwt settings is:
JWT_AUTH = {
'JWT_SECRET_KEY': SECRET_KEY,
'JWT_ALGORITHM': 'HS256',
'JWT_VERIFY': True,
'JWT_VERIFY_EXPIRATION': True,
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=3000),
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_AUTH_COOKIE': 'refresh-token'
}
I noticed that in the browser cookies if there is any refresh-token key then the endpoint works just fine. The problem arises when that key is not present in the browser cookies. I set 'JWT_AUTH_COOKIE': None or removed the following lines:
'JWT_ALLOW_REFRESH': True,
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=1),
'JWT_AUTH_COOKIE': 'refresh-token'
from the JWT_AUTH settings but no luck.
I also tried #csrf_excempt for that endpoint but that did not work either.
Interestingly, when I send the request from postman it works.
Here is the request I am sending from the frontend:
$http({
url: url,
method: "PUT",
headers: {
'Content-Type': 'application/json'
},
data: data
})
I would like to know why I am getting the error when refresh_token key is not present in the browser cookies and how to solve this issue.

I solved my issue by adding 'X-CSRFToken': $cookies.get("csrftoken") to the Http request header, so the request now look like:
$http({
url: url,
method: "PUT",
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': $cookies.get("csrftoken")
},
data: data
})

Related

Microsoft Graph API: Access Token 403 Forbidden error Using ajax call but works properly using postman

When I try to make a rest call from postman I am able to get access token but using ajax call I am getting 403 forbidden error. Appended https://cors-anywhere.herokuapp.com/ url to access token url to avoid CORS error.
const formData = new FormData();
formData.append("client_id", "client_id");
formData.append("client_secret", "S7D7Q~excS5KjBh9JnPK-afZjTjtALGTKNweP");
formData.append("grant_type", "client_credentials");
formData.append("scope", "https://graph.microsoft.com/.default");
$(document).ready(function () {
requestToken();
});
var token;
function requestToken() {
$.ajax({
async: true,
crossDomain: true,
credentials: "include",
url: "https://cors-anywhere.herokuapp.com/https://login.microsoftonline.com/b262d1f3-4738-400d-ad54-c82cdabb6540/oauth2/v2.0/token",
method: "POST",
headers: {
"content-type": "application/x-www-form-urlencoded"
},
cache: false,
processData: false,
contentType: false,
data: formData,
success: function (response) {
console.log(response);
token = response.access_token;
},
});
}
You should not use a client credential in your front-end. That's your application's password and will be visible to anyone visiting the page. Also if that is your actual secret in the question, you should remove it and create a new one.
This URL will not work:
https://cors-anywhere.herokuapp.com/https://login.microsoftonline.com/b262d1f3-4738-400d-ad54-c82cdabb6540/oauth2/v2.0/token
The reason you get a CORS error is because Azure AD is trying to prevent you from shooting your own foot.
AAD sees the request and thinks it should not come from a browser front-end and thus denies it.
The correct way to get the token is to use MSAL.js and acquire a token on behalf of the signed in user (delegated permissions).

Axios post fails with 403 error code and showing CSRF token validation failed but works fine django api when passing csrftoken in header with Postman

I am tring to send csrf token by using axios post in react js but csrf token validation is failing. I am also done with credentials:true
but it's not working.I am receving csrf token in cookie but not able to send it.
class LoginService extends Component {
loginUser(formData)
{
const config = {
headers: { 'content-type': 'application/json',
'X-CSRFToken': Cookies.get("csrftoken")}
}
return axios.post(`${API_BASE_URL}api/user/v1/account/login_session/`,formData,config);
}
}
If you are using httpsOnly cookies then 'X-CSRFToken': Cookies.get("csrftoken")} will not work.
What is HttpOnly?
According to the Microsoft Developer Network,
HttpOnly is an additional flag included in a Set-Cookie HTTP response
header. Using the HttpOnly flag when generating a cookie helps
mitigate the risk of client side script accessing the protected cookie
(if the browser supports it).
Using withCredentials: true should suffice.
loginUser(formData)
{
const config = {
headers: { 'content-type': 'application/json' },
withCredentials: true
}
return axios.post(`${API_BASE_URL}api/user/v1/account/login_session/`,formData,config);
}
}
withCredentials indicates whether or not cross-site Access-Control
requests should be made using credentials
withCredentials: false,
// default
Of course you also need to ensure the cookie domain is set correctly in order for the cookie to work.
Tip: Check your chrome network inspector, and see whether the desire cookie is sent together with your request
CSRF with HttpOnly
If you are setting csrf cookie with httpOnly you can remove httpOnly.
There's an answer with regards to this here https://security.stackexchange.com/a/175540/135413
When you send the header in Axios to the server, you need to know if your server-side accepts the patterns: CSRF-TOKEN or X-CSRF-TOKEN or XSRF-TOKEN or X-XSRF-TOKEN.
You are using X-CSRFToken, that not combine with any patterns shown above.
More here: https://www.npmjs.com/package/csurf
**This is how I solve my problem :
first set these two as:-
axios.defaults.xsrfCookieName = "csrftoken";
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
**
loginUser(formData)
{
const config = {
headers: { 'content-type': 'application/json'},
"X-CSRFToken": Cookies.get('csrftoken'),
withCredentials: true
}
return axios.post(`${API_BASE_URL}api/user/v1/account/login_session/`,formData,config);
}
}

ReactJS seems to turn POST requests into GET requests

I'm using React and the fetch API to do a POST request to a user authentication backend. I can do this POST request below with Postman, and I get the correct JWT back, but oddly - whenever I use the following code in React, the POST request somehow hits the server as a GET request.
Code in React:
return this.fetch('http://fakeURL.com/auth', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"email": "email#email.com",
"password": "password",
})
}).then(res => {
this.setToken(res.token);
return Promise.resolve(res);
})
The logs show (first the pre-flight request):
Request URL: http://fakeURL.com/auth
Request Method: OPTIONS
Status Code: 200 OK
Referrer Policy: no-referrer-when-downgrade
And the actual request:
Request URL: http://fakeURL.com/auth
Request Method: GET
Status Code: 405 METHOD NOT ALLOWED
Referrer Policy: no-referrer-when-downgrade
Things I've tried:
Running the app both locally and in an AWS S3 bucket (with CORS configured to allow all methods, and from any origin)
Using Flask on our backend to enable CORS
Hitting our backend via Postman with the same API POST request (when we use Postman, the API request works as intended, as a POST that returns a token)
Hitting other URLs (e.g. http://httpbin.org/post) to see if my code can hit those endpoints with a POST rather than a GET... and those endpoints see GET requests as well (instead of the intended POST)
This is so confusing - what could possibly cause our POST request to go out as a GET request? I feel like we've eliminated every possibly cause outside of something weird happening in React. Thanks for the help!
npm install --save axios
on your auth page:
import axios from 'axios'
Modify your post so instead of using fetch:
login(emailValue, passwordValue) {
return axios({
url: `yourAuthURL`,
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
data: {
"email": emailValue,
"password": passwordValue,
}
}).then(res => {
console.log(res);
})
}

Call symfony 2 rest api with Angular JS, cors

I'm using angularJS to call a restfull API in symfony 2.
My angularJS app url is angular.me.dev
My symfony2 api url is api.me.dev
I followed this tutorial to make my restfull api.
The problem is when I try to call this api
$http.({
method: 'GET',
url: 'http://api.me.dev/api/articles',
headers: {
'Authorization': 'Bearer ' + token
}
})
An error occured: (here on google chrome):
XMLHttpRequest cannot load http://api.me.dev/api/articles. Response for preflight has invalid HTTP status code 405
(and on on firefox):
The Same Origin Policy disallows reading the remote resource
what did I find about this ?
AngularJS Sends OPTIONS request instead of POST
AngularJS performs an OPTIONS HTTP request for a cross-origin resource
Then I decide to allow headers, origin, ... on my server like this:
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Credentials "true"
Header always set Access-Control-Allow-Methods "PUT, DELETE, POST, GET, OPTIONS"
Header always set Access-Control-Allow-Headers "*"
No change
I add to AngularJS
#coffee script
app.config ['$httpProvider', ($httpProvider) ->
$httpProvider.defaults.useXDomain = true
delete $httpProvider.defaults.headers.common['X-Requested-With']
]
Then I decided to install NelmioCorsBundle in my symfony api but I didn't see any change.
Finally, I notice a call works
$http.({
method: 'GET',
url: 'http://api.me.dev/api/articles',
})
Here the call return a 401 response (good, i need to be logged)
$http.({
method: 'GET',
url: 'http://api.me.dev/public/api/users',
})
Here I have a call doesn't need authorization, it works.
I found only when I remove headers it works, and when I had any headers (content-type or authorization for example) an error occured.
Can anyone help me ? thanks !
Ok, my bad.
I said NelmioCors seems not working but i did something wrong.
I try again with nelmio cors bundle, using this configuration.
nelmio_cors:
paths:
'^/':
allow_origin: ['http://angular.me.dev']
allow_headers: ['Authorization', 'X-Requested-With', 'Content-Type', 'Accept', 'Origin', 'X-Custom-Auth']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE', 'OPTIONS']
max_age: 3600
It's working !
NelmioCorsBundle resolve the problem.
https://github.com/nelmio/NelmioCorsBundle

Angular JS - $http not sending headers

As the title suggest, I need to pass an authorization token but when I check the network console, I'm getting a 403 because the accept and authorization isn't there. NOTE: I removed my authorization token for this example
$http({
url: 'http://api.stubhub.com/search/catalog/events/v2?title="san"',
dataType: 'json',
method: 'GET',
data: '',
headers: {
"Content-Type": 'application/json',
"Authorization": 'Bearer {token}'
}
}).success(function(response){
$scope.searchResponse = response;
}).error(function(error){
$scope.error = error;
});
This seems like correct syntax? What's wrong here?
EDIT: added the XHR screenshot
EDIT: 2. Here's my network traffic, HAR via paste bin:
http://pastebin.com/fiFngZSy
setting custom headers on XHR requests triggers a preflight request.
I bet you're seeing an OPTIONS request without the Authorization header, and a corresponding response whose Access-Control-Allow-Headers header value is not listing Authorization, or the API doesn't even allow unauthorized OPTIONS requests.
I don't know the stubhub api, but does it return CORS-compliant responses?

Resources