I am a beginner level DRF developer. I am trying to integrate Keycloak with Django Rest Framework. Unfortunately, I was unable to find any type of help/blog/tutorial online.
You can use KeyCloack's Oauth2 API to authenticate and authorize your djagno users. Is is the same as implementing Sign-in with Google or any other provider.
My favorite package to implement social auth is python-social-auth, and it even has an existing backend for KeyCloack.
Here is how a configuration for Oauth2 against KeyCloack should look like:
First, setup social auth in your project like so
$ pip install social-auth-app-django
In your settings.py
INSTALLED_APPS = (
# ...
'social_django',
# ...
)
AUTHENTICATION_BACKENDS = (
'social_core.backends.keycloak.KeycloakOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
# Add you connection settings here
SOCIAL_AUTH_KEYCLOAK_KEY = 'test-django-oidc'
SOCIAL_AUTH_KEYCLOAK_SECRET = 'a7a41-245e-...'
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = \
'MIIBIjANBxxxdSD'
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = \
'https://iam.example.com/auth/realms/voxcloud-staff/protocol/openid-connect/auth'
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = \
'https://iam.example.com/auth/realms/voxcloud-staff/protocol/openid-connect/token'
In your urls.py
urlpatterns = [
...
path('auth/', include('social_django.urls', namespace='social'))
...
]
Then add this to your login page template:
Login with KeyCloack
I am enabling token/api-key authentication on my API. But once I enable it, I can no longer use the browsable API page of the DRF. I know I can disable the authentication while developing, but this is a question of curiosity: Can I add an api-key to the header of each request sent to the browsable API page? Can I do that by tweaking the Browser settings? Or is it possible to tweak the Browsable API page itself and hardcode the api-key into it?
The better way to handle the situation is to add the SessionAuthentication to the DEFAULT_AUTHENTICATION_CLASSES section in your settings
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
"rest_framework.authentication.SessionAuthentication",
],
}
More precisely,
# settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework.authentication.TokenAuthentication",
],
}
if DEBUG:
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].append(
"rest_framework.authentication.SessionAuthentication"
)
By doing this, you can either use your APIKey or session key to authenticate the requests.
I'm using Django to host a React application. I added the CSRF protection middleware in Django. I tried testing it by sending a http post with Postman, without the x-csrftoken in the header. To my surprise, I did not get a 403, but I was able to get data without the x-csrftoken. How is this possible?
Below you find my CSRF settings. My additional Django settings are very straightforward and include CORS.
...
# Cross Origin Resource Sharing Protection
CORS_ALLOWED_ORIGINS = [
'http://127.0.0.1:3000',
]
CORS_ORIGIN_ALLOW_ALL = False
CORS_ALLOW_CREDENTIALS = True
# Cross Site Request Forgery Protection
CSRF_TRUSTED_ORIGINS = []
MIDDLEWARE = [
...
'django.middleware.csrf.CsrfViewMiddleware',
]
If you are using anything other than SessionAuthentication CSRF will be disabled. In the docs.
I'm working with both the latest Django 3.0.2 and DRF 3.11.0 using djangorestframework-simplejwt 4.4.0. I did not have this problem before on a previous Django 2.2.0 project (and older lib versions), but I cannot figure out why I cannot access a view with readonly permissions set and default authentication looks okay in settings. For the simplejwt and firebaseauth library, I'm using the default settings (with correct credentials for firebaseauth, verified it works fine).
If I specify the authentication_classes as empty in the view, then it works fine, but this is just an example. I expected the defaults to work, from settings: unless I'm incorrect and should be explicitly set this way. I have also tried using gunicorn and waitress-serve, and it's the same result.
Here is the view:
class NoteList(generics.ListAPIView):
"""
List all Notes. Not used
"""
# authentication_classes = []
permission_classes = (permissions.AllowAny,)
serializer_class = NoteSerializer
def get_queryset(self):
notes = Note.objects.all()
return notes
Here is my settings:
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', ]
REST_FRAMEWORK = {
# When you enable API versioning, the request.version attribute will contain a string
# that corresponds to the version requested in the incoming client request.
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.NamespaceVersioning',
# Authentication settings
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.SessionAuthentication',
'drf_firebase_auth.authentication.FirebaseAuthentication',
],
# Permission settings
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly',
'rest_framework.permissions.IsAuthenticated',
'rest_framework.permissions.IsAdminUser',
'rest_framework.permissions.IsAuthenticatedOrReadOnly',
],
# For unit test cases
'TEST_REQUEST_DEFAULT_FORMAT': 'json',
# Pagination
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 50,
}
If ordering the above matters, what is the correct order to have them in?
Urls, if it matters:
urlpatterns = [
path('anotes/', NoteList.as_view(), name="lame-notes"),
]
Now, when I visit the URL anonymously, I always get:
GET /v1/anotes/
HTTP 401 Unauthorized
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
WWW-Authenticate: Bearer realm="api"
{
"detail": "Invalid Authorization header format, expecting: JWT <token>."
I've been stuck on this issue for a while and could not find any similar problems to solve it. On my older Django project, I don't get this 401 Unauthorized and am able to have ReadOnly work correctly.
The view or endpoint only works correctly if I'm logged in.
If I'm missing any information please let me know.
Permissions in REST framework are always defined as a list of permission classes.
Before running the main body of the view each permission in the list is checked. If any permission check fails an exceptions.PermissionDenied or exceptions.NotAuthenticated exception will be raised, and the main body of the view will not run.
So an anonymous user can check the first item in your permission list, but fails at second. You should decide what will your default behavior will be and keep that in the list.
hello i'm using django rest-auth and i have this problem in /password/change/ it allways return csrf token missing or incorrect problem :
I am making the request fr an android app I'm developing
my settings are :
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'rest_auth.registration',
'allauth',
'allauth.account',
'allauth.socialaccount',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}
versions :
django-rest-auth==0.9.1
djangorestframework==3.6.2
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
remove 'rest_framework.authentication.SessionAuthentication' from DEFAULT_AUTHENTICATION_CLASSES,if you still need browsable api view for DRF, use ModHeader in chrome。
after reading the doc of django rest framework official i and a lot of help come from #Todor comment i realize that i should only put TokenAuthentification in the rest authentication classes because sessionAuthentication expect a csrf value in the request but android can't give that so i use a token as in the doc in every request and that's it !
Are you properly storing and forwarding cookies from the Android networking library? I have very little familiarity with Ionic/Android, but the way that Django's CSRF checks work is this:
Check if a CSRF token is provided in the body as csrfmiddlewaretoken
If no such parameter exists, check for a cookie called csrftoken
if no such cookie exists, fall back to the HTTP_X_CSRFTOKEN header
The cookie name and header name can be customized in settings.
So what I'm getting at is, what method are you using above to send the CSRF token? On mobile, it's normally sorta tough to get a CSRF token for the request because the client generates the form (where on web, Django generates the form and injects the CSRF token).
That said, it is also common to make endpoints CSRF exempt, and it seems like you're using a third party library for these endpoints, so I'm not sure why it's requiring a CSRF token. You can check the project's documentation.
The other possibility is that you've bound your own view at that URL, and you're reaching that view instead of the one from the library you're using. It's sort of hard to tell. Why don't you first try the request using DRF's Browsable API?
I get this error when I was trying to host my website on Apache server.
If I run without Apache server (python manage.py runserver) everything is fine.
To solve this error:
Open Apache configuration file - httpd.conf
Add following line:
WSGIPassAuthorization On
Here's my quick solution that isn't good for production unless you fork the rest-framework repo with your changes... And well it disables the functionality for SessionAuthentication.
If you are going to be using your api with a browser-less front-end like a mobile app, which does not allow cross site requests to be made (as there is no browser from which a request can be made i.e. the app will not be able to open up links sent to you by others/you cannot navigate the web traditionally inside of it) Then it's as simple as this:
To remove the functionality, go to the rest_framework site package. Inside of it is a authentication.py file, and inside of it, there's is a class called 'SessionAuthentication'. In here there's a enforce_csrf() function which enforces the csrf by raising an exception when a csrf token isn't present in a request. Simply comment out its body and it will no longer care about csrf.
Here's what the authentication.py SessionAuthentication class looks like, do the following:
class SessionAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
Returns a `User` if the request session currently has a logged in user.
Otherwise returns `None`.
"""
# Get the session-based user from the underlying HttpRequest object
user = getattr(request._request, 'user', None)
# Unauthenticated, CSRF validation not required
if not user or not user.is_active:
return None
self.enforce_csrf(request)
# CSRF passed with authenticated user
return (user, None)
def enforce_csrf(self, request):
"""
Enforce CSRF validation for session based authentication.
"""
##### Comment Out Below: ###########
# check = CSRFCheck()
# # populates request.META['CSRF_COOKIE'], which is used in process_view()
# check.process_request(request)
# reason = check.process_view(request, None, (), {})
# if reason:
# # CSRF failed, bail with explicit error message
# raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)
So, in case you're wondering if this is a bad idea or not, because all requests are intentionally made inside a mobile app/controlled exclusively by the mobile app in most cases, it does not operate within the same environment as a browser. It's hard for a user to accidentally follow a link or for a script . Note, this does not completely remediate the vulnerability, but the odds of it happening are incredibly unlikely and would most likely not occur directly through the mobile app
I wanted use SessionAuthentication to have the current user in rest views, i resolved it sending authorization header with the token value in each request, i am using JSON web token authentication too
Well the solution is simple: you need to add the CSRF token when you make your request. How you would do that, specifically, we can't answer, because we have no idea how you're making the request. i.e. show some code.