How to solve django and angular cors errors - django

I'm developing application and got this error:
Access to XMLHttpRequest at 'https://subdomain.domain.org/api/parks/?page_size=6&page_number=1' from origin ''https://subdomain.domain.org' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Django Config
setting.py:
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
ALLOWED_HOSTS = ['app_name-backend.herokuapp.com']
CORS_ALLOWED_ORIGINS = [
'https://app_name-frontend.herokuapp.com',
'https://subdomain.domain.org',
]
CORS_ALLOW_ALL_ORIGINS = False
Angular Requests
service.ts:
getParks(filters: any=null, parameters:any=null): Observable<any> {
...
var httpHeaders = new HttpHeaders();
httpHeaders.set('Content-Type', 'application/json');
httpHeaders.set('Access-Control-Allow-Origin', '*');
var requestUrl = this.baseServiceUrl + this.serviceUrl;
return this.httpClient.get<any>(requestUrl, { params: httpParams, headers: this.httpHeaders })
.pipe(
...
),
catchError(this.handleError<Park[]>('Park getParks', []))
);
}
The front and back are hosted on heroku
Any idea how to fix it ?
Thanks in advance.

Related

CORS policy blocks XMLHttpRequest

With Ionic Angular running in on my localhost I made this call to my Django backend (running on different localhost):
test() {
return this.httpClient.get(endpoint + '/test', {
headers: { mode: 'no-cors' },
});
}
And on the backend side I have following code to respond:
#csrf_exempt
def test(request):
response = json.dumps({'success': True})
return HttpResponse(response, content_type='application/json', headers={'Access-Control-Allow-Origin': '*'})
I have also this in my settings.py file:
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
]
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
Still, I get this error message in my console:
Access to XMLHttpRequest at 'http://127.0.0.1:8000/test' from origin 'http://localhost:8100' has been blocked by CORS policy: Request header field mode is not allowed by Access-Control-Allow-Headers in preflight response.
What am I doing wrong?
You need to just add one more setting
CORS_ALLOW_ALL_HEADERS=True
Other than the above you do not need to set a header on each response. Just simply respond back with payload as
#csrf_exempt
def test(request):
response = json.dumps({'success': True})
return HttpResponse(response, content_type='application/json', headers={'Access-Control-Allow-Origin': '*'})

Axios post request to route 'appName/v1/users/' (Djoser) throws 401 error but Postman doesn't

I'm new to Django and trying to build basic user authentication with REST API and a Vue.js frontend. To send the request, I am using axios, which is configured first in a seperate composable axios.js:
import axios from 'axios'
axios.defaults.withCredentials = true
axios.defaults.baseURL = 'http://localhost:8000'
and used inside a register.vue component:
const submitRegistration = () => {
axios.post('api/v1/users/', {username: 'userName', password: 'userPassword'})
.then(res => {
console.log(res)
})
}
To make it simple, I'm sending a data-object with predefined strings, as you can see above. The request gets sent to one of the djoser routes in projectName/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/v1/', include('djoser.urls')),
path('api/v1/', include('djoser.urls.authtoken')),
]
This however, throws a 401 Unauthorized Error:
code: "ERR_BAD_REQUEST"
config: {transitional: {…}, adapter: Array(2), transformRequest: Array(1), transformResponse: Array(1), timeout: 0, …}
message: "Request failed with status code 401"
name: "AxiosError"
request: XMLHttpRequest {onreadystatechange: null, readyState: 4, timeout: 0, withCredentials: true, upload: XMLHttpRequestUpload, …}
response: {data: {…}, status: 401, statusText: 'Unauthorized', headers: AxiosHeaders, config: {…}, …}
stack: "AxiosError: Request failed with status code 401\n at settle (http://localhost:3000/node_modules/.vite/deps/axios.js?v=a45c5ec0:1120:12)\n at XMLHttpRequest.onloadend (http://localhost:3000/node_modules/.vite/deps/axios.js?v=a45c5ec0:1331:7)"
I've configured settings.py like so:
INSTALLED_APPS = [
...,
'corsheaders',
'rest_framework',
'rest_framework.authtoken',
'djoser'
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...,
]
CORS_ALLOWED_ORIGINS = ['http://localhost:3000']
CORS_ALLOW_CREDENTIALS = True
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
)
}
Edit:
The problem might be caused by authToken. In the root vue component app.vue, I am checking the store for an authentication token and setting an axios header accordingly:
onMounted(() => {
if (store.authToken) {
axios.defaults.headers.common['Authorization'] = `Token ${store.authToken}`
}
})
If I remove this code, the error turns into 400 BAD_REQUEST. Although I don't understand why one would need an authToken when registering a new user.
store.authToken is set during initialization:
import {reactive} from "vue";
export const store = reactive({
authToken: localStorage.authToken ?? null,
})
and shoud be null, when a new user is registered.
Edit2:
I followed some advice and used Postman to send the post request to http://localhost:8000/api/v1/users/ and it works. A new user is created.
My authentication check in the vue root component app.vue was flawed:
<script setup>
onMounted(() => {
if (store.authToken) {
axios.defaults.headers.common['Authorization'] = `Token ${store.authToken}`
}
})
</script>
store.authToken always returned true despite being null because it was fetched from localStorage being a string. JSON.parse() solved the problem.

CORS headers not showing up in my request/response on Django

I've made the following CORS implementation on my Django project using django-cors-headers.
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOWED_ORIGINS = []
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
....
]
INSTALLED_APPS = [
...
'corsheaders',
]
For some reason, I don't see the effect of these headers (I don't have Access-Control-Allow-Origin in my headers). I print out the request and response headers in my view. This is my view:
def payment(request, *args, **kwargs):
print(request.headers)
params_in = json.loads(request.body.decode('utf-8'))
headers_in = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer key,
}
response = requests.post('https://url.com/v1/endpoint',
headers=headers_in,
data=params_in)
resp_out = Response(response.json()['value'])
print(resp_out.headers)
return resp_out
My request headers are ['Host', 'Connection', 'Content-Type', 'Origin', 'Accept-Encoding', 'Cookie', 'Accept', 'User-Agent', 'Referer', 'Accept-Language', 'X-Request-Id', 'X-Forwarded-For', 'X-Forwarded-Proto', 'X-Forwarded-Port', 'Via', 'Connect-Time', 'X-Request-Start', 'Total-Route-Time', 'Content-Length'] and my only resp_out header is 'Content-Type'.
Why could this be? What am I missing?
You don't receive the CORS headers as a part of the request rather the Access-Control-Allow-Origin: Your_Origin_Name(if among the allowed ones mentioned in CORS_ALLOWED_ORIGINS = []
) header will be sent back as a response header.

Django doesn't create session cookie in cross-site json request

I want to make cross-site JavaScript call from third-party domain (in this case my desktop/localhost server) to my remote Django server hosted on my_domain.com/ and calling REST WS exposed on my_domain.com/msg/my_service with using session/cookies for storing session state.
But when I call this service (hosted on remote Django server) from my desktop browser or localhost Django server (JS is in index.html), Django doesn't create session cookie and on remote server are doesn't store session state. But when i call this service from Postman or from same localhost JS to localhost instance of same Django service it works and session is created.
My JS script in index.html which make call to WS send_message:
fetch('http://my_domain.com/ws/my_service', {
method:"POST",
credentials: 'include',
body:JSON.stringify(data)
})
.then(res => res.json())
.then(json => {
showResponse(json.message);
})
When I run this script from my desktop browser or my localhost server it runs correctly with cookies and sessions parameters.
Django my_service implementation view
#csrf_exempt
def my_service(request):
if request.method == "POST":
message_bstream= request.body
request.session.set_expiry(0)
message= json.loads(message_bstream)
out,sta=state_machine_response(message["message"],int(request.session["state"]))
request.session["state"] =sta
respo={"message":out}
response = HttpResponse(json.dumps(respo), content_type="application/json")
response.set_cookie(key='name', value='my_value', samesite='None', secure=True)
#return JsonResponse(respo, safe=False, status=200)
return response
else:
return HttpResponseNotFound ("Sorry this methode is not allowed")
Or I try generate response like
return JsonResponse(respo, safe=False, status=200)
My settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:8000',
)
CORS_ALLOWED_ORIGINS = [
'http://localhost:8000',
]
CSRF_TRUSTED_ORIGINS = [
'http://localhost:8000',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
Please do you have any idea?
You can't save cookies from a third-party API call unless you use SameSite=None with the Secure option in the Set-Cookie header. You can achieve this for the sessionid and CSRF cookie with the following settings:
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'
In this case, you must use the HTTPS protocol scheme.
Another solution would be to use a proxy, which is helpful in a localhost development environment. This is an example using vue.js on the vue.config.js file:
const { defineConfig } = require('#vue/cli-service')
if (process.env.NODE_ENV == "development"){
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
proxy: {
"^/api/": {
target: process.env.VUE_APP_BASE_URL,
changeOrigin: true,
logLevel: "debug",
pathRewrite: { "^/api": "/api" }
}
}
},
})
}
Some useful doc https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#samesitenone_requires_secure

Cannot request data using fetch (JS) from Django REST Framework API [duplicate]

This question already has answers here:
“Request header field Access-Control-Allow-Origin is not allowed by Access-Control-Allow-Headers in preflight response”
(2 answers)
Closed 3 years ago.
I am trying to send a POST request to my Django REST Framework API at localhost:8000. I receive the Django response when Postman sends the request, but I receive a console error on my React.js application using the fetch() function. I see that, for the React.js app's request, an OPTIONS request is sent to the Django backend.
This is the error I receive on the React app.
Access to fetch at 'http://localhost:8000/api/admin/login/' from origin 'http://ucla.localhost:3000' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
Here is my JavaScript code.
fetch(authenticateUrl, {
method: "POST",
credentials: "include",
headers: {
Accept: "application/json",
'Content-Type': "application/json",
'Access-Control-Allow-Origin': '*',//"http://localhost:8000",
},
body: JSON.stringify({ username, password }),
signal: abortLoginController.signal
})
Here is my Django settings.py
MIDDLEWARE = [
'django_hosts.middleware.HostsRequestMiddleware',
#'debug_toolbar.middleware.DebugToolbarMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_hosts.middleware.HostsResponseMiddleware',
]
...
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
# CORS_ORIGIN_WHITELIST = (
# 'http://sub.localhost:3000',
# 'http://localhost:3000'
# )
SESSION_COOKIE_SAMESITE = None
From what is, it seems that your request header includes a field that is not allowed by the CORS policy you setup and specifically, the field 'access-control-allow-origin' is forbidden by the policy and so your request is totally blocked. You can try either:
To modify the CORS policy to allow that header
or
Setup the Django app to not send this header on your requests.