How can I enable CORS on my Django REST Framework?
the reference doesn't help much, it says that I can do by a middleware, but how can I do that?
The link you referenced in your question recommends using django-cors-headers, whose documentation says to install the library
python -m pip install django-cors-headers
and then add it to your installed apps:
INSTALLED_APPS = (
...
'corsheaders',
...
)
You will also need to add a middleware class to listen in on responses:
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
and specify domains for CORS, e.g.:
CORS_ALLOWED_ORIGINS = [
'http://localhost:3030',
]
Please browse the configuration section of its documentation, paying particular attention to the various CORS_ORIGIN_ settings. You'll need to set some of those based on your needs.
python -m pip install django-cors-headers
and then add it to your installed apps:
INSTALLED_APPS = [
...
'corsheaders',
...
]
You will also need to add a middleware class to listen in on responses:
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
CORS_ALLOW_ALL_ORIGINS = True # If this is used then `CORS_ALLOWED_ORIGINS` will not have any effect
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
'http://localhost:3030',
] # If this is used, then not need to use `CORS_ALLOW_ALL_ORIGINS = True`
CORS_ALLOWED_ORIGIN_REGEXES = [
'http://localhost:3030',
]
more details: https://github.com/ottoyiu/django-cors-headers/#configuration
read the official documentation can resolve almost all problem
You can do by using a custom middleware, even though knowing that the best option is using the tested approach of the package django-cors-headers. With that said, here is the solution:
create the following structure and files:
-- myapp/middleware/__init__.py
from corsMiddleware import corsMiddleware
-- myapp/middleware/corsMiddleware.py
class corsMiddleware(object):
def process_response(self, req, resp):
resp["Access-Control-Allow-Origin"] = "*"
return resp
add to settings.py the marked line:
MIDDLEWARE_CLASSES = (
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
# Now we add here our custom middleware
'app_name.middleware.corsMiddleware' <---- this line
)
In case anyone is getting back to this question and deciding to write their own middleware, this is a code sample for Django's new style middleware -
class CORSMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = "*"
return response
For Django versions > 1.10, according to the documentation, a custom MIDDLEWARE can be written as a function, let's say in the file: yourproject/middleware.py (as a sibling of settings.py):
def open_access_middleware(get_response):
def middleware(request):
response = get_response(request)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"] = "*"
return response
return middleware
and finally, add the python path of this function (w.r.t. the root of your project) to the MIDDLEWARE list in your project's settings.py:
MIDDLEWARE = [
.
.
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'yourproject.middleware.open_access_middleware'
]
Easy peasy!
Below are the working steps without the need for any external modules:
Step 1: Create a module in your app.
E.g, lets assume we have an app called user_registration_app. Explore user_registration_app and create a new file.
Lets call this as custom_cors_middleware.py
Paste the below Class definition:
class CustomCorsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Headers"] = "*"
# Code to be executed for each request/response after
# the view is called.
return response
Step 2: Register a middleware
In your projects settings.py file, add this line
'user_registration_app.custom_cors_middleware.CustomCorsMiddleware'
E.g:
MIDDLEWARE = [
'user_registration_app.custom_cors_middleware.CustomCorsMiddleware', # ADD THIS LINE BEFORE CommonMiddleware
...
'django.middleware.common.CommonMiddleware',
]
Remember to replace user_registration_app with the name of your app where you have created your custom_cors_middleware.py module.
You can now verify it will add the required response headers to all the views in the project!
Updated 2021 for all those who have the latest version of Django v3.x.x, The steps to allow CORS from any origin are given below.
Step 1: Install required library
pip install django-cors-headers
Step 2: Then add in proper place in your INSTALLED_APPS in settings.py - after the rest_framework and before your application myapp
'rest_framework',
'corsheaders',
'myapp.apps.MyAppConfig',
Step 3: Allow the origins for your api (inside settings.py)
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.10.45:3000', # for network
)
Updated 2022 and adding a new use case
When your using Axios POST with the option withCredentials: true, there are a few additional options to consider.
I used this specific case for authentification over Basic or/and Session login.
To Avoid error messages as:
Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
And the above mentioned by others. I solved the issue in this way.
[IP addresses are from my local example, have in mind to change it]
setting.py
INSTALLED_APPS = [
...
'rest_framework',
'corsheaders',
'rest_framework.authtoken',
...
]
ALLOWED_HOSTS = ["localhost","192.168.0.50"]
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.0.50:3000', # for network
'http://localhost:8080', # for localhost (Developlemt)
'http://192.168.0.50:8080', # for network (Development)
)
CSRF_TRUSTED_ORIGINS = [
'http://localhost:3000', # for localhost (REACT Default)
'http://192.168.0.50:3000', # for network
'http://localhost:8080', # for localhost (Developlemt)
'http://192.168.0.50:8080', # for network (Development)
]
MIDDLEWARE = [
...
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'corsheaders.middleware.CorsMiddleware',
...
]
On the browser, the Axios request headers must be send and on the server site the headers must be permitted. If not, the error message will be.
Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
Up to this moment play with the headers. You can add more headers if you need them, like:
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]
Cheers :)
Well, I don't know guys but:
using here python 3.6 and django 2.2
Renaming MIDDLEWARE_CLASSES to MIDDLEWARE in settings.py worked.
first install django package
pip install django-cors-headers
and add to apps in settings file
INSTALLED_APPS = (
...
'corsheaders',
...
)
and then add cors middle ware to setting file
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]
and finally add cross orgin whitelist
#CORS_ORIGIN_ALLOW_ALL = True
#CORS_ALLOW_CREDENTIALS = True
#CORS_ALLOW_HEADERS = ['*']
CORS_ORIGIN_WHITELIST = ('http://localhost:5000',)
that will solve cors error easily. happy coding
Django=2.2.12 django-cors-headers=3.2.1 djangorestframework=3.11.0
Follow the official instruction doesn't work
Finally use the old way to figure it out.
ADD:
# proj/middlewares.py
from rest_framework.authentication import SessionAuthentication
class CsrfExemptSessionAuthentication(SessionAuthentication):
def enforce_csrf(self, request):
return # To not perform the csrf check previously happening
#proj/settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'proj.middlewares.CsrfExemptSessionAuthentication',
),
}
After trying every suggested solution and nothing seemed to work. I finally fixed the issue after much frustration by clearing my browser cache...
Then the accepted answer works by using django-cors-headers.
Hope this helps someone else!
Related
I am trying to make a POST call to Django from a React Native Web front end on different subdomains.
I thought I had configured CORS correctly, but that does not seem to be the case.
Here's what my Django settings.py looks like:
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = ['*']
CORS_ALLOWED_ORIGINS = ['https://api.example.com', 'https://example.com', 'https://www.example.com' ]
CSRF_TRUSTED_ORIGINS = [
'https://api.example.com', 'https://example.com', 'https://www.example.com'
]
ALLOWED_HOSTS = ["0.0.0.0", "api.example.com", "example.com"]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
]
INSTALLED_APPS = [
...
'corsheaders',
...
]
What exactly am I doing wrong here? The error I'm getting is this:
Access to XMLHttpRequest at 'https://api.example.com/api/v1/pagescreate/' from origin 'https://example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
And this is my Django view:
class PageCreateView(generics.CreateAPIView):
queryset = Page.objects.all()
serializer_class = PageSerializer
What could be causing this? Am I missing some setting in React? I'm using axios to make the calls, with the only header being "Content-Type": "application/json"
EDIT: Could this be due to some nginx rule on my server? Or maybe my Kubernetes configuration? I am using Docker to set it up the container and can easily link the Dockerfile, or any information from my Kubernetes setup
I had this issue before and I suggest to use:
CORS_ORIGIN_ALLOW_ALL = True
that will allow all origins
with this config ofcourse:
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
INSTALLED_APPS = [
...
'corsheaders',
...
]
I think, this could be resulted from the sequence of middlewares in the settings. (I am not completely confident though)
Can you try the following sequence of middleware
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
and just remove
CORS_ALLOW_HEADERS = ['*']
from the settings.
Let me know if this works out
CORS_ALLOW_CREDENTIALS = True makes CORS_ALLOW_HEADERS = ['*'] be translated literally i.e. not as wildcard.
If you want to add custom headers you need to extend the default list as it's done in the doc;
from corsheaders.defaults import default_headers
CORS_ALLOW_HEADERS = list(default_headers) + [
'my-custom-header',
]
As the request has Content-Type of application/json this is not a simple request as per MDN. So this request triggers CORS preflight request which includes Access-Control-Request-Headers request header. This request header elicits Access-Control-Allow-Headers response header.
Value of this response header can be * to indicate wildcard value in the absence of credentials. But the * is interpreted literally when HTTP cookies or authentication information are present in the request as it's done here with CORS_ALLOW_CREDENTIALS = True.
Here in CORS preflight Access-Control-Request-Headers has value of Content-Type but Access-Control-Allow-Headers is a literal * because of CORS_ALLOW_HEADERS = ['*']. So the request is not authorized, that's why the error No 'Access-Control-Allow-Origin' header is present on the requested resource.
GET works here because it is a "simple request" and doesn't trigger CORS perflight.
CORS
Access-Control-Allow-Headers
I have had a boatload of CORS issues with Django. Generally, you might try to use:
CORS_ALLOWED_ORIGINS = ['*']
CSRF_TRUSTED_ORIGINS = ['*']
(Note: This is just boilerplate and you probably don't want to do it in production; hunting down the actual issue is a necessity in the end)
to make sure it's in your Django setup. However, as I have mentioned here: What can cause Chrome to give an net::ERR_FAILED on cached content against a server on localhost?
There are all sorts of things that can cause CORS errors (sometimes not even registering them as CORS errors) and it can show up coming from all sorts of locations. The real issue is best hunted by digging through your logs.
(Note: this answer shows how to put your Django logs into files, which you probably should be doing. The standard Django REST-response startup logs will contain useful information)
Step one is to see if a specific request is hitting your Django logs at all. If it is, your CORS settings within Django are the problem. You can easily tell why it's getting rejected because Django will have the fully qualified (MYSUBDOMAIN.example.com) domain that it has rejected in the log. This makes changing your Django settings a snap; simply add the rejected domain to your allowed origins.
Note: If you're not a BASH / Django pro, going to the log directory (should be referenced in your settings.py) where Django is stuffing its logs and executing ls -alrt will give you the last modified log.
I have always debugged this problem in Apach2 on Ubuntu, though. So I had a log at /var/log/apache2 that showed me exactly which domain was being rejected and why. (When Apache rejected the request; if you're putting a web server in front of Django it will also probably have CORS policies and plugins) If you're using ngnix on docker, the following question (Where are logs of docker nginx conainter stored in host) suggests you use this command line command to get the log files out of docker and see what they're doing.
docker inspect --format='{{.LogPath}}' <nginx-containter-id>
I personally will often end up booting a Docker instance from a command line and going to the BASH prompt to debug it. Advice on that is outside the scope of this question.
Going out a step further, it is possible that this CORS policy problem is even farther out on your network stack. There are times when misconfiguration of security policies, bad redirects, or other infrastructure-related issues are misunderstood by Chrome as CORS issues. A CORS interpretation can even be a browser configuration issue, in some cases. I, on occasion, have booted up a virtual machine to set my security levels in Chrome to the absolute minimum and try it, just to see if CORS was really the issue.
Sometimes it wasn't. Sometimes it was that something else - a load balancer, a firewall, a misconfigured AWS security group - was the culprit.
So, yeah, digging this out can be a full on bug safari rather than simple hunt. But the core thing to check first is whether your Django logs are spitting out anything after each REST request. If they're not you need to go up the chain because you're not talking to Django at all.
Ah, another note: We get CORS errors when Django can't write to its log files.
I had a similar issue which was solved by shifting corsheaders.middleware.CorsMiddleware above django.middleware.common.CommonMiddleware,in MIDDLEWARE array (settings.py).
Disclaimer: not a django dev.
But you are using a non-whitelisted value for Content-Type which means your POST request will be "preflighted". Can you confirm the OPTIONS request is properly handled by your API?
If you're running this in K8s, see if including the following to ALLOWED_HOSTS helps:
gethostname(), gethostbyname(gethostname())
Can you try:
CORS_ORIGIN_WHITELIST = (
'https://example.com' )
I did something very similar to what you're doing and this configuration worked for me:
INSTALLED_APPS = [
...
'corsheaders',
...
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
CORS_ORIGIN_WHITELIST = [
'https://website.com'
]
I think its possible that you are missing a couple middlewares:
django.middleware.csrf.CsrfViewMiddleware
corsheaders.middleware.CorsPostCsrfMiddleware
otherwise I believe there is no problem with your CORS_ALLOWED_ORIGINS and CSRF_TRUSTED_ORIGINS settings
Are you sure that CORS_ALLOW_HEADERS allows you to set '*'?
Based on here, it looks like it is designed to be extended rather than replaced:
https://github.com/adamchainz/django-cors-headers/blob/2d22268ff59aa0b79820ea5b8983efd1e514ec4c/README.rst#cors_allow_headers
This test also led me to think that it is required to be extended rather than replaced
https://github.com/adamchainz/django-cors-headers/blob/5067faefeb22beffcf1b12dd7ad9d3fb6d4b26e4/src/corsheaders/checks.py#L20-L21
I would suggest trying that with the default or try extending it from the above link
I believe that is a library-specific value and not a browser or request CORS value
edit: added test link to github.com
As explained here, The CORS Access-Control-Allow-Origin line expects in one of these two formats:
Allow everything: probably not what you want
Access-Control-Allow-Origin: *
Allow a single specific origin:
Access-Control-Allow-Origin: [SCHEME]://[HOST]:[PORT_OPTIONAL]
Scheme: AKA protocol, this can be HTTP, HTTPS .
Host: This has to exactly match the requesting origin, so subdomain.domain.com or domain.com
Port: [Optional, but important for you] Leaving the port out is the same as putting default port :80 if your scheme is HTTP and :443 if your scheme is HTTPS.
Also, The server https://example.com which you are making the request to has to implement CORS to grant Script from your website access. Your Script can't grant itself permission to access another website.
I used a CORS extension named allow-cors-access-control from chrome web store.
Or, You can use a change-origin chrome plugin like this:
Moesif Orign & CORS Changer
You can make your local dev server (ex: localhost:8080) to appear to be coming from 172.16.1.157:8002 or any other domain.
Make sure to restart the server after adding the above configuration and clear the cache and hard reload. It worked for me.
I am running a local version of my react app on localhost:3000 and am trying to hit endpoints on a Django server hosted on heroku
Error encountered on each axios request: Access to XMLHttpRequest has been blocked by CORS policy: no 'Access-Control-Allow-Origin' header is present
Current settings.py for Django server:
ALLOWED_HOSTS=['*']
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'corsheaders',
'whitenoise.runserver_nostatic',
'django.contrib.staticfiles',
'accounts',
'blog',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
# Whitenoise
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
#'django.middleware.clickjacking.XFrameOptionsMiddleware',
# Machina
#'machina.apps.forum_permission.middleware.ForumPermissionMiddleware',
]
# CORS
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = False
Since I am allowing all origins to make requests, it seems like this type of CORS issue should be disabled. I have tried removing all other middleware except CorsMiddleware and it it did not make a difference.
A bit stumped at this point because my configuration seems correct as per https://github.com/adamchainz/django-cors-headers
Here is an example api call from my react app that is causing the "no 'Access-Control-Allow-Origin' header"
function callCSRFApi(){
return axios.request({
method: 'GET',
url: 'http://example.com/csrf/',
});
}
One thing to note is that I am hitting endpoints on the Django server even though it is not configured as a django_rest api.
I have also tried setting a custom middleware which should always define Access-Control-Allow-Origin, but still received that the header was missing:
class CorsMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if (request.method == "OPTIONS" and "HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META):
response = http.HttpResponse()
response["Content-Length"] = "0"
response["Access-Control-Max-Age"] = 86400
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "DELETE, GET, OPTIONS, PATCH, POST, PUT"
response["Access-Control-Allow-Headers"] = "accept, accept-encoding, authorization, content-type, dnt, origin, user-agent, x-csrftoken, x-requested-with"
return response
My thoughts are that the problem could be because we don't have the Django server configured as a django_rest app, or that django has logic where it can't be accessed from localhost.
Note:
I test in an incognito window after reading that others were having trouble with the cache
Have tried reverting to an earlier version of django-cors-headers
Have tried without CORS_ALLOW_CREDENTIALS = False
GET requests worked while running both the django server and react application on different ports of localhost
Python 3.7, Django 3.0.7, django-cors-headers 3.5.0, React 17.0.1, axios 0.21.0
Try setting CORS_ALLOW_CREDENTIALS = True in Django settings and { withCredentials: true } in the axios config.
I've had some problems with axios and CSRF myself and i like to just use the native fetch-API. This code works with Django and corsheaders:
export async function authenticateUser() {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': Cookies.get('csrftoken')
},
credentials: 'include',
});
return response.json()
}
Try to add a white list to your settings.py
cors
CORS_ALLOWED_ORIGINS = [
'http://localhost:3000',
]
The application backend (django) and frontend (react) are hosted on google cloud at the same app.
I've installed django-cors-headers and referred to it in settings.py:
INSTALLED_APPS = (
...
'corsheaders',
...
)
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...
]
CORS_ORIGIN_ALLOW_ALL = True
CORS_ORIGIN_REGEX_WHITELIST = (
'http://localhost:3000',
'https?\://myapp\.appspot\.com\/?',
'https?\://frontend-dot-myapp\.appspot\.com\/?',
)
As far as I understood CORS_ORIGIN_ALLOW_ALL should already resolve this problem, but it doesn't, I just got a bit desparate there trying things out. CORS_ORIGIN_WHITELIST didn't work as well for me that's why CORS_ORIGIN_REGEX_WHITELIST.
I haven't changed anything in this setup but now the Cross-Origin error appears. Since the last code change I installed django-silk to profile the app. Removing it doesn't appear to resolve the issue, so I'm not sure whether it's related.
Help greatly appreciated!
in requirements.txt:
django-cors-headers==3.2.1
EDIT
After a lot of trial and error I found adding the last two lines in urls.py cause the problem:
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', views.api_root),
path('_ah/warmup/', views.service_status),
path('_ah/start/', views.service_status),
...
However I don't understand why that causes the CORS error or how to resolve it.
I also had same issue in project solution that i got is
Place corsheaders.middleware.CorsMiddleware at the top
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
]
ALLOWED_HOSTS = ['*']
Add at the bottom of the settings.py
CORS_ORIGIN_ALLOW_ALL = True
This idea might be worth a shot. Try moving your 'corsheaders.middleware.CorsMiddleware' line to the top of the MIDDLEWARE section.
This is recommended in the pypi django-cors-headers documentation where they say "CorsMiddleware should be placed as high as possible, especially before any middleware that can generate responses such as Django’s CommonMiddleware or Whitenoise’s WhiteNoiseMiddleware. If it is not before, it will not be able to add the CORS headers to these responses."
Check here 'Content-Type':"application/json", if you type Contenttype or ContentType will give an error of CORS header blocked
Context: I need to configure OIDC using Django to allow my application to be accessed by users.
Problem: I'm using a library called mozilla-django-oidc that is pretty straight-forward, but when I test the configuration, it keeps returning the following error:
400 - Invalid redirect_uri
Cause: As I tested in Postman, this error occurs because the callback URL is wrong. When I searched more deeply mozilla-django-oidc, I've found it sets a standard URL path for callback (/oidc/callback/) which is not the URL allowed by the OIDC we need to access.
Key Question #1: How can I modify the mozilla-django-oidc code so that the standard callback URL path is what I need it to be?
Key Question #2: Is there anyway I can make these changes in my code, instead of mozilla-django-oidc?
Below are some settings of my Django code:
My Django - views.py
# Mozila OIDC configuration settings
OIDC_OP_AUTHORIZATION_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_OP_TOKEN_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_OP_USER_ENDPOINT = "https://XXXXXXXXXXXXXXXXXXXXX"
OIDC_RP_CLIENT_ID = "XXXXXXXXXXXXXXXXXXXXXXX"
OIDC_RP_CLIENT_SECRET = "XXXXXXXXXXXXXXXXXXXXXXX"
LOGIN_REDIRECT_URL = "https://myurl.com"
My Django - settings.py
# Add 'mozilla_django_oidc' to INSTALLED_APPS
INSTALLED_APPS = (
# ...
'django.contrib.auth',
'mozilla_django_oidc', # Load after auth
# ...
)
# Add 'mozilla_django_oidc' authentication backend
AUTHENTICATION_BACKENDS = (
'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
# ...
)
My Django - urls.py
urlpatterns = patterns(
# ...
url(r'^oidc/', include('mozilla_django_oidc.urls')),
# ...
)
mozilla-django-oidc - urls.py
urlpatterns = [
url(r'^callback/$', OIDCCallbackClass.as_view(),
name='oidc_authentication_callback'),
url(r'^authenticate/$', OIDCAuthenticateClass.as_view(),
name='oidc_authentication_init'),
url(r'^logout/$', views.OIDCLogoutView.as_view(), name='oidc_logout'),
]
Other parts of mozilla-django-oidc library can be found at https://github.com/mozilla/mozilla-django-oidc/tree/master/mozilla_django_oidc
I'm experiencing a CORS issue consuming a Django Rest Framework REST API from an Angular 6 app.
The API runs on http://localhost:55098/admin. It works fine when I invoke it with Insomnia. Angular app runs on http://localhost:4200.
When I first typed http://localhost:4200/cgestores it should redirect to http://localhost:55098/admin/cgestores, and it did, but I got a CORS error message (see below).
My configuration:
REST API: I'm using Python 3.7 (64 bit), Django (2.1.5) and Django Rest Framework (3.9.1)
My settings.py:
ALLOWED_HOSTS = [
'testserver',
'localhost'
]
CORS_ORIGIN_ALLOW_ALL = True
INSTALLED_APPS = [
# Add your apps here to enable them
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_swagger',
'corsheaders',
'admin_v20',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Angular client: I'm using Angular 6.4.1. The service (apiusuarios.service.ts):
import { Injectable } from '#angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse, HttpParams } from '#angular/common/http';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ApiusuariosService {
datos: iCentroGestor;
constructor(private http: HttpClient) { }
getMock(): any{
return [
{"gececo":"a", "gecdes":"one"},
{"gececo":"b", "gecdes":"two"},
{"gececo":"c", "gecdes":"three"}
];
}
getUsers(): Observable<any> {
return this.http.get(endpoint + 'cgestores').pipe(map(this.extractData));
}
}
export interface iCentroGestor {
gececo: string;
gecdes: string;
}
const endpoint = 'http://localhost:55098/admin/';
Method getMock() returns a fixed JSON value just for test purposes, and it works fine. The method that actually calls the API is getUsers(), and is the method that triggers the CORS error:
Access to XMLHttpRequest at 'http://localhost:55098/admin/cgestores/' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
(that's Chrome; Firefox and Edge return similar responses)
This seemed to be an issue for a lot of people, I checked these articles:
https://github.com/crs4/ome_seadragon/wiki/Enable-Django-CORS-(Cross-Origin-Resource-Sharing)-Headers-configuration
https://www.techiediaries.com/django-cors/
CORS django 'Access-Control-Allow-Origin'
No 'Access-Control-Allow-Origin' using django rest framework api
Django Python rest framework, No 'Access-Control-Allow-Origin' header is present on the requested resource in chrome, works in firefox
Django Rest Framework: XMLHttpRequest cannot load http://127.0.0.1:8000/xyz/api/abc
...and a few more, and also this video:
https://www.youtube.com/watch?v=OIbndrrUYiY
...and they all pretty much sayed the same: it's not an Angular problem, it's a server issue, and I should make the following modifications to my DRF project:
Install CORS headers package
Modify settings.py with the following:
Add CORS_ORIGIN_WHITELIST
Add corsheaders to INSTALLED_APPS
Add corsheaders.middleware.CorsMiddleware to MIDLEWARE_CLASSES
All done, but it still didn't work. After trying all that, I read that I could specify the header that I need on my response. So I tried this in my DRF project:
return Response(data=pDatos, status=pStatus, headers={"Access-Control-Allow-Origin":"*"})
... and bingo, it worked!
However, this is not really a solution, I don't like to have to depend on a specific parameter on a specific response. And, on top of that, I have no way to restrict who can access the site and who can't, which would be really easy to do with CORS_ORIGIN_WHITELIST (well, I could, but writing a parameter with a list of sites in place of the asterisk symbol doesn't seem to be the right option).
So, what am I doing wrong? Why do my settings.py options not work? I commented them out, and just left the return Response... and it still works, so settings.py is not really doing anything.
Sorry for this long post. Any ideas? Thanks in advance for your help.
Start by installing django-cors-headers using pip
pip install django-cors-headers
You need to add it to your project settings.py file:
INSTALLED_APPS = (
##...
'corsheaders'
)
Next you need to add corsheaders.middleware.CorsMiddleware middleware to the middleware classes in settings.py
MIDDLEWARE = (
'corsheaders.middleware.CorsMiddleware',
#...
)
You can then, either enable CORS for all domains by adding the following setting
CORS_ORIGIN_ALLOW_ALL = True
Or Only enable CORS for specified domains:
CORS_ORIGIN_ALLOW_ALL = False
CORS_ORIGIN_WHITELIST = (
'http://localhost:8000', # Allowed host, if CORS_ORIGIN_ALLOW_ALL is False
)
Found the answer myself, I'd like to share it.
I'm using Django 2.1.5. It appears that section MIDDLEWARE_CLASS is simply called MIDDLEWARE since version 2. When I changed it I was forced to comment out django.contrib.auth.middleware.SessionAuthenticacionMiddleware, and that did the magic. Now everything works as it should.