marcelo225 / django-keycloak-auth Goto Github PK
View Code? Open in Web Editor NEWDjango Keycloak Auth
License: MIT License
Django Keycloak Auth
License: MIT License
I am using this library with Django Rest Framework. If I were able to integrate the Keycloak authentication mechanisms found in this library in the built-in API explorer, it would make developing with authenticated endpoints much easier.
I would like to advocate for a pluggable way to "Log In" to the default Django Rest Framework API Explorer. It should be documented, as this seems like a great entry-level use-case.
Hi there ! Thanks for your amazing work on this lib first.
We have a project and we have to use following keycloak configuration :
It is for us impossible to use realms roles (too many users) and to duplicate keycloak-front's roles to keycloak-back's roles (technical reason coming from our system team - no discussion).
Would it be ok for you if I set up a modification for a PR, changing this :
client_access = resource_access.get(self.client_id, None) if resource_access is not None else None
to this ?
client_access = resource_access.get(settings.KEYCLOAK_CONFIG['KEYCLOAK_CLIENT_FRONT_ID'], None) if resource_access is not None else None
KEYCLOAK_CLIENT_FRONT_ID must be added in project's settings.py.
Modification done in "keycloak.py", line 134
Best regards !
Edit : sorry if it's not clear, the goal is to take a list of supported clients, not a single one.
Hi,
I know it is slightly out of the scope of this library, but since the information is already there, I think it could be useful to include the Keycloak user UUID in the request object along with the roles. The information is available in the 'sub' attribute of the decoded token.
I can make a PR if you're interested.
Hi,
Using actions instead of request methods to authorize resources would allow support for custom actions and more fined-grained control (multiple actions can use the same request method).
Updating line 87 of middleware.py like below would do the trick :
require_view_role = view_roles.get(self.action, [None])
I can make a PR if you want, and update the README accordingly.
Is there the possibility to perform the authorization based on oauth2 scopes instead of keycloak roles?
Would you consider implementing this possibility or merging a PR that implements the feature?
Hello, we are implementing keycloak at my job, and we decided to use your awesome library.
I have an issue/question.
I created a client called django-client
in keycloak and left all the default configurations as is.
When setting LOCAL_DECODE=True
the JWT authorization fails because the keycloak sends aud=['account']
however,
you have
if audience is None:
audience = self.client_id # "django-client" in my case
....
payload = jwt.decode(token, key=key, algorithms=['RS256'], audience=audience, options=options)
This results in an error because the django-client
is not in the intended aud
coming from keycloak
I was able to add a custom Dedicated Scope
to my Client
with the same name (django-client
), following the answers here. making aud=['account', 'django-client']
which makes it pass the JWT check.
but I think this is not a real solution.
from my research all created Clients
have aud='account'
by default from here
and you can remove this aud
if you want and assign custom scopes instead.
I think that using audience = self.client_id
by default is not correct here.
if audience is None:
audience = self.client_id
if audience is None:
audience = None
"account"
`aud` should be either skipped if None
```python
if audience is None:
audience = 'account'
LOCAL_DECODE_AUD
in the config that should indicate that the developer wants to enforce a specific aud
, which could be anything "account", CLIENT_ID, "www.supersecretwebapp.com", etc..
I am not sure if my issue is very coherent and up to code. please reach out if you need more info about this.
P.S. A useful resource about JWT aud
vs client-id
here
So your config currently works great for REST style API, where you control the permissions on the request method.
One of the API classes I work with exposes the functions as endpoints, so there can be many functions that act as endpoint, and they can all be POST. I created a decorator to control permissions in this circumstance, which works well for me. Was wondering if you see value in adding something like this to your code base.
decorator:
def valid_roles(access_roles):
def decorator(func):
@wraps(func)
def wrapped_func(self, request, *args, **kwargs):
if len(set(request.roles) & set(access_roles)) == 0:
raise exceptions.PermissionDenied(exceptions.PermissionDenied.default_detail)
return func(self, request, *args, **kwargs)
return wrapped_func
return decorator
Example use:
class ExternalDemandActionAPI(ActionAPIView):
permission_classes = []
authentication_classes = []
keycloak_roles = {
'GET': ['api:get'],
'POST': ['api:add', 'api:get', 'api:mod'],
'UPDATE': ['api:mod', ],
'DELETE': ['api:del', ],
'PATCH': ['api:mod'],
}
@valid_roles(['api:demand:add'])
def add_demand(self, request, params, *args, **kwargs):
logger.debug(f'{params}')
return params
My Django Rest Framework application is still integrating auth. I wanted to test this library a bit before diving deep.
There is a docstring in the example APIView
from the documentation that suggests that "Other HTTP methods will be allowed."
However, once that keycloak_roles
attribute is assigned (at least in a ModelViewSet
), it prevents other methods from being accessed.
Example APIView
:
class JudgementView(views.APIView):
"""
Judgement endpoint
This endpoint has configured keycloak roles only GET method.
Other HTTP methods will be allowed.
"""
keycloak_roles = {
'GET': ['judge'],
}
The following are from my project:
settings.py
:
# Exempt URIS
KEYCLOAK_EXEMPT_URIS = [
'/schema/swagger', '/schema/redoc', '/schema/spectacular'
]
KEYCLOAK_CONFIG = {
'KEYCLOAK_SERVER_URL': 'http://localhost:8080/auth',
'KEYCLOAK_REALM': 'master',
'KEYCLOAK_CLIENT_ID': 'client-backend',
'KEYCLOAK_CLIENT_SECRET_KEY': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
}
views.py
:
class ApplicationViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows applications to be viewed or edited.
"""
queryset = Application.objects.all()
permission_classes = [permissions.AllowAny]
serializer_class = ApplicationSerializer
keycloak_roles = {
# 'GET': ['director', 'judge', 'employee'],
'POST': ['director', 'judge', ],
# 'UPDATE': ['director', 'judge', ],
# 'DELETE': ['director', 'judge', ],
# 'PATCH': ['director', 'judge', 'employee'],
}
I make no effort to properly authenticate, yet, but I would expect this /applications
route to be able to see the list
view (GET
). Here is the result of attempting to perform a request with any method on the /applications
route.
{"detail": "Authentication credentials were not provided."}
Am I misunderstanding how to properly exempt specific methods? My application would support unauthenticated requests for some shared routes with specific methods (such as being able to submit an "Application
" without having an account with POST
).
Hi,
First of all, thanks for this library !
It could be useful to support realm roles as well instead of just client roles.
It shoudn't be too difficult to implement, my guess is slightly modifying keycloak.py like below would do the trick (lines 131 - 134) :
if not (self.client_id in token_decoded['resource_access'] or self.client_id in token_decoded['realm_access']) :
return None
resource_access = token_decoded['resource_access'].get(self.client_id, None)
realm_access = token_decoded['realm_access']
return resource_access.get('roles', None) or realm_access.get('roles', None)
I can make a PR for this if you want.
Hello, is there a way to use this lib with function based views?
I am trying to use the library, have set everything as described in the documentation. I get the JWT from a keycloak server I have set up and use with java clients.
However, at my django app, request.roles
is empty, and request.userinfo
does not exist. The APIs are all as they would be without any authentication
Maybe some DEFAULT_AUTHENTICATION_CLASSES
or AUTH_USER_MODEL
or something similar is needed in the configuration ?
Currently the only thing I have used is as in README MIDDLEWARE
entry
Thank you
Hi,
I'd like my backend to get some userinfo from keycloak via the bearer token.
is there a way to pass userinfo to the view ?
I've added
request.userinfo = self.keycloak.userinfo(token)
at the end of the middleware.py and works perfectly. Is it ok or there is a better way ?
best regards
Tem algo que seja necessário desenvolver na lib?
In the documentation you cite the Auth URL for "http://localhost:8080/auth" but I see that this URL no longer exists in the latest version of keycloak. It is currently "http://localhost:8080".
/auth is deprecated.!?!?!?!
Adding "django_keycloak_auth.middleware.KeycloakMiddleware",
to the settings MIDDLEWARE
array causes the below error when accessing /admin:
Traceback (most recent call last):
File "/Users/thomasfex/development/github/ItkBackend/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/thomasfex/development/github/ItkBackend/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 171, in _get_response
response = middleware_method(request, callback, callback_args, callback_kwargs)
File "/Users/thomasfex/development/github/ItkBackend/env/lib/python3.8/site-packages/django_keycloak_auth/middleware.py", line 91, in process_view
is_api_view = True if str(view_func.cls.__qualname__) == "WrappedAPIView" else False
AttributeError: 'function' object has no attribute 'cls'
[03-11-23 13:38:09] django.server 500 "GET /admin/ HTTP/1.1" 500 95736
I think we can fix it on line 90 of middleware.py like so:
try:
is_api_view = True if str(view_func.cls.__qualname__) == "WrappedAPIView" else False
except AttributeError as e:
is_api_view = False
Is this a logical fix? I can open a pr if that looks right.
Seems that "HTTP_AUTHORIZATION" is not present in the request.META.
Maybe I forgot a config on keycloak side?
I have all the setup done. I can introspect using Postman. But when I try to access an API endpoint from my DRF, I get "detail": "Authentication credentials were not provided."
Does Django need to authenticate with Keycloak to introspect tokens? I am not sure what I am missing and any help is greatly appreciated.
I am running both the API endpoint and keycloak via docker-compose.
Thanks.
Hi everyone,
I hope this message finds you well. I've come across an issue while working with the latest versions of the module, specifically when attempting to exempt the admin path, as I used to do in older versions.
Here's a snippet of my code:
KEYCLOAK_EXEMPT_URIS = ["admin/"]
However, upon implementing this, I encountered the following error:
AttributeError: 'function' object has no attribute 'cls'
if hasattr(view_func.cls, "keycloak_roles") and request.method not in view_func.cls.keycloak_roles:
"GET /admin/ HTTP/1.1" 500 145
Interestingly, removing the "admin/" part from KEYCLOAK_EXEMPT_URIS resolved the issue for me. I would appreciate some guidance or clarification on whether this behavior is considered an issue in the current module versions.
Thank you for your time and assistance.
I found the problem is that you are trying to get an error from method raise_for_status. But this method does not return an error, but raised does.
I created a PR that solves this problem. #23
So I am attempting to use this application to allow Netbox to use the Keycloak API for Authentication. I was wondering if anyone has had any success in this area. I have also attempted to use a basic project app as well and have had little success. Any help is appreciated.
i couldn't find this file requirements-development.txt in the project so i couldn't install the dependencies
Seems that "HTTP_AUTHORIZATION" is not present in the request.META.
Maybe I forgot a config on keycloak side?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.