CSRF token missing or incorrect. Django + AngularJS - angularjs

I'm getting CSRF token missing or incorrect error while doing a POST request to a remote django api from my localhost machine.
My settings on AngularJS:
.config(['$httpProvider', function($httpProvider){
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
}]);
but im still getting the CSRF token missing or incorrect error.
I check what headers are being sent and apparently angular is not sending HTTP_X_CSRFTOKEN.
But I can see that the cookie csrftoken=something is sent.
Does anyone know what is going on?
Request Header
POST /s/login/ HTTP/1.1
Host: server.somewhere.io:8000
Connection: keep-alive
Content-Length: 290
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://localhost/thesocialmarkt/
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en;q=0.8,en-US;q=0.6,pt-BR;q=0.4,pt;q=0.2
Cookie: csrftoken=hiYq1bCNux1mTeQuI4eNgi97qir8pivi; sessionid=1nn1phjab5yd71yfu5k8ghdch2ho6exc

As #Chris Hawkes pointed to this stackoverflow answer given by #Ye Liu
Since the angular app isn't served by django, in order to let the
cookie to be set, angular app needs to do a GET request to django
first.
I verified that as long as you don't make http get request, csrftoken cookie doesn't get set. So only
$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
would not work. You first need to make if not real then mock http get request to django rest_framework.
Update: Your comments pushed me to further study it, Please read this blog where is has mentioned as,
CLIENT-SIDE GENERATED CSRF-TOKENS. Have the clients generate and send
the same unique secret value in both a Cookie and a custom HTTP
header. Considering a website is only allowed to read/write a Cookie
for its own domain, only the real site can send the same value in both
headers
So lets try with this single request first.
$http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
where you are injecting $cookies to the controller/service.
If it works then may be writing interceptors would be good choice, and would help you to debug as well.
I am sure you are using AngularJs version at least 1.2, See this changeset
and in recent commit Angular http service checking csrf with this code,
var xsrfValue = urlIsSameOrigin(config.url)
? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
: undefined;
if (xsrfValue) {
reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
}
So it's necessary that you are sending same token which is present in cookie.
Further to analyse use developer tool of your browser to see request/response with the http request and analyse headers and cookies.

if you are using $http in AngularJS for ajax request and if you are facing any CSRF token issue then use this:
$http.defaults.xsrfCookieName = 'csrftoken';
$http.defaults.xsrfHeaderName = 'X-CSRFToken';

Related

http response not setting cookie in the browser

TLDR:
The following response header doesn't set the cookie in browser:
Access-Control-Allow-Origin: *
Allow: GET, HEAD, OPTIONS
Content-Length: 7
Content-Type: application/json
Date: Tue, 27 Apr 2021 15:58:02 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.4
Set-Cookie: csrftoken=r5r2YcZZvJKs79cbLd24VSyNscpUsxJB6UuWiWO2TXriy6B4r8KDZrwSDyI091K1; expires=Tue, 26 Apr 2022 15:58:02 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Vary: Accept, Cookie, Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
My request headers:
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Host: 127.0.0.1:8000
Origin: http://localhost:3000
Pragma: no-cache
Referer: http://localhost:3000/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36
I am new to Django, react and "http header" related stuff.
My django dev server runs at:
http://127.0.0.1:8000/
and my react dev server runs at:
http://127.0.0.1:3000
In order to access the website, login is required. So, all unauthorized requests are 1st redirected to login page, by configuring react-router and following this template. So, till now, no api calls are made.
In order to post login data, i need to have csrf token set by the server. But since i have not made any api calls, i created an endpoint /api/csrf/ explicitly, to set the csrf token.
# URL: /api/csrf/
class CSRFGet(APIView):
"""
Explicitly set csrf cookie
"""
#method_decorator(ensure_csrf_cookie)
def get(self, request):
return Response('hello')
I call this endpoint, when useProvideAuth hook is mounted.
function useProvideAuth() {
const [token, setToken] = useState(null);
const login = (username, password) => {
return axios.post(
'/auth/',
{
username: username,
password: password
})
.then(response => {
setToken(response.token)
})
}
useEffect(()=> {
axios.get(
'/csrf/'
)
},[])
return {
token,
login,
}
}
To retrieve and set this cookie, i followed the official Django docs. I also enabled CORS policy using django-CORS-headers allow all origins.
Now, when i make a request to any page, it redirects to login page, and i can see api/csrf/ responds with:
Set-Cookie: csrftoken=LgHo2Y7R1BshM4iPisi5qCXhdHyAQK7hD0LxYwESZGcUh3dXwDu03lORdDq02pzG; expires=Tue, 26 Apr 2022 06:29:23 GMT; Max-Age=31449600; Path=/; SameSite=Lax
But, the cookie is not set at all. Why is it so?
Is my approach for getting csrf cookie correct? Please let me know, if i am making any security vulnerability with this approach.
Could you try adding the following to the django-cors-headers configuration and retry?
CORS_ALLOW_CREDENTIALS = True
Also, please note that the above configuration would probably not work if you are allowing all origins. See this Mozilla documentation: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’
If you face such error, I suggest setting:
CORS_ALLOWED_ORIGINS = [
"http://127.0.0.1:3000",
]
or something fancier like:
CORS_ALLOWED_ORIGIN_REGEXES = [
r"^http://127.0.0.1:[0-9]{1,4}$",
]
Finally, make sure that you are using a django-cors-headers version >= 3.5 since the 2 above configuration had different aliases back then.
Let me know if it works, I am very curious.
Turns out the issue was, i was using http://127.0.0.1:8000 to make api calls, where as my server was on http://localhost:8000. Because of this, host and origin, in my request headers didn't match the same domain.
Cookies can be allowed to be used under same domain, with different ports and subdomains, unlike Same-Origin policy, but cannot be used cross-domains.
In my case, I guess http://127.0.0.1:8000 & http://localhost:8000 were considered different domains, and thus the browser was not setting my cookie.
Thanks Anas Tiour, for stating about Allow-Credentials. I had tried that too, but still had no luck until i found out the actual reason.

React-call to "Asp.Net Core with identity server" gives CORS-errors

Ill try to keep this short to save digital rain forest. Please ask If I missed any details.
I have an "asp .net 3.1 core + react"-project template in VS, with built in Identity server. This works ok, but I now want to do my react project in a separate project. So I started a new create-react-app-project.
So, from my new react project, when I call OidcConfigurationController. The controller method is called and I can step through the code on server side. Then I get a client error "Failed to fetch", which, by internet wizdom, seems to indicated CORS-error.
This is what I got when I inspect the header in chrome dev toolbar->network
Request URL: https://localhost:5001/authentication/_configuration/MyProject.Web
Referrer Policy: strict-origin-when-cross-origin
:authority: localhost:5001
:method: GET
:path: /authentication/_configuration/MyProject.Web
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US,en;q=0.9,sv;q=0.8
origin: http://localhost:3000
referer: http://localhost:3000/
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
sec-ch-ua-mobile: ?0
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
These are relevant lines in startup.cs
ConfigureServices()
services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
builder =>
{
//builder.WithOrigins("http://localhost:3002/", "https://localhost:3001")
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
services.AddSingleton<ICorsPolicyService>((container) => {
var logger = container.GetRequiredService<ILogger<DefaultCorsPolicyService>>();
return new DefaultCorsPolicyService(logger)
{
AllowAll = true
};
});
Configure()
app.UseCors(MyAllowSpecificOrigins); // I also tried to switch order on these 2 rows
app.UseIdentityServer();
Nothing I do here seems to change the Referrer Policy in the header, still get the exact same message
The React-call is just a plain fetch(address-of-the-controller-that-it-hits).
I have also tried to start a new Server Side-project (asp net core api) and set same CORS-policy, I can call this api from my react client without getting any errors)
So, in the request, you see the origin: http://localhost:3000 header is used. That is the source for the CORS request. But the request is for this URL:
https://localhost:5001/authentication/_configuration/MyProject.Web
Could it not be that there's a redirect from insecure HTTP to HTTPS that is interfering?
Do make sure you set the CORS settings in IdentityServer as well.
See the CORS documentation for more details.
As side note, IIS might cause CORS issues as well, see this answer for details:
IIS hijacks CORS Preflight OPTIONS request

401 Unauthorized on unprotected resource

My Setup
I have a server with a REST API that runs on Symfony with API Platform. The GET requests for my resources do not require authorization, however the other operations do. Authorization is handled with a JWT Bearer token.
The client uses React-admin with API Platform Admin. I added this code to send the JWT token along with the operations:
// dataProvider.js
import React from "react";
import { hydraDataProvider, fetchHydra as baseFetchHydra } from "#api-platform/admin";
export default entrypoint => {
const fetchHeaders = { Authorization: `Bearer ${localStorage.getItem("token")}` };
const fetchHydra = (url, options = {}) => baseFetchHydra(url, {
...options,
headers: new Headers(fetchHeaders),
});
return hydraDataProvider(entrypoint, fetchHydra);
};
The Problem
When I log in to my admin interface now, I get a 401 Unauthorized response, because the server did not expect a token for a GET request.
Request Headers:
Host: localhost:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0
Accept: application/ld+json
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:3000/
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1ODQ2NzcwMTYsImV4cCI6MTU4NDY4MDYxNiwicm9sZXMiOlsiUk9MRV9BRE1JTiJdLCJ1c2VybmFtZSI6IlNvbWVib2R5In0.O_StagfEJy5VQS-5s-DjuwzOlUgrl3MTmxPfZUU0J1go06tKOpLjiBrEIJpjo5AK67w93SfsUaIBop8apoacHQ
Content-Type: application/ld+json
Origin: http://localhost:3000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
TE: Trailers
Response Headers:
HTTP/2 401 Unauthorized
access-control-allow-origin: http://localhost:3000
access-control-expose-headers: link
cache-control: no-cache, private
content-type: application/json
date: Fri, 20 Mar 2020 04:40:39 GMT
link: <https://localhost:8000/api/docs.jsonld>; rel="http://www.w3.org/ns/hydra/core#apiDocumentation"
www-authenticate: Bearer
x-debug-token: 720652
x-debug-token-link: https://localhost:8000/_profiler/720652
x-powered-by: PHP/7.4.1
x-robots-tag: noindex
content-length: 282
X-Firefox-Spdy: h2
When I manually remove the Authorization line from the request headers in the browser and retry it, it works.
My Questions:
Is this even expected behavior?
Should the client always send the token?
If the token should always be sent, how do I tell API Platform to accept it even if it isn't needed?
If the token should only be sent when it's required, how do I let the hydraDataProvider know?
After many many hours of trying different solutions, I finally fixed this problem.
Answers to my Questions
No, sending a valid token should not result in a 401 response.
The token can be sent on every request.
My Solution
The problem was that the JWT authentication was configured incorrectly on my server. None of the guides I followed actually covered the following case:
I have the user email as identifier, not the user name.
So what ended up happening is that the token contained the encoded user name, which is not a unique identifier in my case. To tell JWT to use the email instead, I had to set the user_identity_field to email.
// config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
user_identity_field: email

Mixing Windows Authentication and Token Authentication

I am implementing a token authentication into a system that has legacy Windows Authentication. I have followed some token examples and have come up with the following. Here is my request header that I am issuing in Angular UI:
authorization:bearer - token value
Authorization:Negotiate some value
I am adding token to the request like this:
config.headers.authorization = "bearer " + access_token;
As you can see I end up with two a/A authorizations. The response from the server is:
HTTP Error 400. The request has an invalid header name
I am assuming it's because of these two entries. I have tried using capital A for my token entry but the response was the same.
Any idea, please?
Edit 1
GET /api/case HTTP/1.1
Authorization bearer value
Accept application/json, text/plain, */*
Referer http://localhost/main.aspx
Accept-Language en-US
Accept-Encoding gzip, deflate
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Cookie ASP.NET_SessionId=value
Host localhost
Authorization Negotiate value

Internet Explorer 11 replaces Authorization header

What would cause Internet Explorer to replace the HTTP header
Authorization : Bearer <server-provided-token>
with
Authorization : Negotiate <some token>
when making an AJAX request?
Details
In Internet Explorer, some AJAX requests that are configured to contain the header Authorization: Bearer ... are being sent by Internet Explorer with the header Authorization: Negotiate ... instead.
For example, Fiddler shows that the first two of three requests contain the Authorization : Bearer... header, while the third suddenly contains the Authorization : Negotiate... header. The first two requests are successful, and the third fails because the request can't be properly authenticated.
All of the requests are constructed using the same client-side code, and are made one after another (within the span of a second). I have verified that the Authorization header correctly contains the Bearer token in all three cases up until the point the request is provided to the browser.
Also, I'm not seeing the same behavior in Chrome; it's only occurring in IE.
Request 1
GET http://localhost/myapp/api/User HTTP/1.1
Accept: application/json, text/plain, */*
Authorization: Bearer oEXS5IBu9huepzW6jfh-POMA18AUA8yWZsPfBPZuFf_JJxq-DKIt0JDyPXSiGpmV_cpT8FlL3D1DN-Tv5ZbT73MTuBOd5y75-bsx9fZvOeJgg04JcO0cUajdCH2h5QlMP8TNwgTpHg-TR9FxyPk3Kw6bQ6tQCOkOwIG_FmEJpP89yrOsoYJoCfrAoZ7M4PVcik9F9qtPgXmWwXB2eHDtkls44wITF_yM_rPm5C47OPCvMVTPz30KwoEPi6fHUcL3qHauP-v9uypv2e48TyPHUwLYmNFxyafMhBx4TkovnRcsdLHZiHmSjMq0V9a2Vw70
Referer: http://localhost/client/login.html
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: localhost
DNT: 1
Connection: Keep-Alive
Request 2
POST http://localhost/myapp/api/Permissions HTTP/1.1
Referer: http://localhost/client/#/Dashboard
Content-Type: application/json
Authorization: Bearer oEXS5IBu9huepzW6jfh-POMA18AUA8yWZsPfBPZuFf_JJxq-DKIt0JDyPXSiGpmV_cpT8FlL3D1DN-Tv5ZbT73MTuBOd5y75-bsx9fZvOeJgg04JcO0cUajdCH2h5QlMP8TNwgTpHg-TR9FxyPk3Kw6bQ6tQCOkOwIG_FmEJpP89yrOsoYJoCfrAoZ7M4PVcik9F9qtPgXmWwXB2eHDtkls44wITF_yM_rPm5C47OPCvMVTPz30KwoEPi6fHUcL3qHauP-v9uypv2e48TyPHUwLYmNFxyafMhBx4TkovnRcsdLHZiHmSjMq0V9a2Vw70
Accept: application/json, text/plain, */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: localhost
Content-Length: 1419
DNT: 1
Connection: Keep-Alive
Pragma: no-cache
<Post Data Removed>
Request 3
GET http://localhost/myapp/api/UserPreferences/Dashboard HTTP/1.1
Referer: http://localhost/client/#/Dashboard
Content-Type: application/json
Authorization: Negotiate YHsGBisGAQUFAqBxMG+gMDAuBgorBgEEAYI3AgIKBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHqI7BDlOVExNU1NQAAEAAACXsgjiBgAGADMAAAALAAsAKAAAAAYBsR0AAAAPVk1ERVZFTlYtU1JTQ0VSSVM=
Accept: application/json, text/plain, */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Connection: Keep-Alive
DNT: 1
Host: localhost
The requests are being made via the AngularJS $http service, and the back-end is ASP.NET Web API hosted in IIS.
We had a problem where Internet Explorer was caching credentials. We could fix the problem by using the following script:
document.execCommand('ClearAuthenticationCache', 'false');
see: Wikipedia
I've just come across this issue too.
What was odd is that it worked fine on my development machine, it was when I deployed it the issue arose.
Again it worked fine in Chrome, Firefox etc.
It turns out that the issue is that IE was detecting the site was on the localintranet zone and was therefore trying to automatically trying log on (it was set by group policy - this is an internal app).
My workaround was that (luckily) it was only autodetecting local intranet zone when using a server name that wasn't an FQDN (e.g. myserver) - but using the full A
I had the same problem in a knockoutjs application, it worked fine in Chrome and Firefox but not in IE.
I also used Fiddler and noticed that the first ajax call used Bearer as intended and returned successfully. But then IE started to loop and send the subsequent ajax calls over and over again with the Negotiate authorization instead!
In my case it was some sort of timing issue in IE, I solved it by making the ajax calls that loaded data during rendering synchronous.
me.loadLimits = function () {
$.ajax({
type: 'GET',
dataType: 'json',
contentType: 'application/json',
url: '/api/workrate/limits',
headers: me.headers,
async: false,
success: function (result) {
...
I also encountered this issue when I was kicking off multiple data loads in my angular app.
I worked around this by detecting the browser and if IE, delayed each request by 50ms based on the index of the call:
return $q(function(resolve, reject) {
var delay = self.widget.useDelayLoading ? self.widget.index * 50 : 0;
setTimeout(function() {
restService.genericApi(self.widget.url, false).queryPost(json).$promise
.then(
function(r) { resolve(r); },
function(e) { reject(e); }
);
}, delay);
});
Interestingly, when I used $timeout, I had to increase the delay to 100ms.
We had faced similar issue with angular and web api. Issue happens when the system tries to access some resource at root level which had Windows Authentication enabled. In our case, application was trying to get the favicon from IIS root. Once this request gets unauthorized, IE will try getting the resouce with negotiation header; though it fails again. But from this point onwards, IE keep sending negotiate header instead of our bearer token. This is due to the settings in IE, which I think is in Internet Options -> Advanced tab -> Enable Integrated Windows Authentication in the Security section (not sure, I forgot the exact stuff).
Fix was either give anonymous access to root level or to the resource location which app is trying to access (bad option) or have document.execCommand('ClearAuthenticationCache', false); in the app.js file.
In my case, IE alternated between sending a bad request, followed by a good request on a second attempt, then followed by a bad request again and so on.
After trying several approaches to causing IE to retry - it appears that returning a 307 (Temporary redirect) with the same request url in the Location header solves the issue.
e.g. for a request to "http://myUrl/api/service/"
HTTP 307 Temporary Redirect
Location: http://myUrl/api/service/
IE retries the call with the proper data.
Edit: This method might be dangerous as it might create an infinite loop. A possible solution to work around it, is to return some counter as part of the url in the Location header and analyze it when receiving the call again.

Resources