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
Related
This is my scenario:
I have a legacy db and an Django api that is using it. Now we are developing another package with Django DRF that will be installed in the 'parent' app.
The parent app already have oauth authentication.
So in the parent app's settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': ('oauth2_provider.contrib.rest_framework.OAuth2Authentication',)
}
Then the routes are protected under this auth system. This is routes.py:
urlpatterns = [
path('api/v1/', include('myapp.routes', namespace='api')),
# OAuth 2.0
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider'))
]
I install my package, that basically is a DRF API with managed=False models, and it will be an API that will work against the legacy DB. (this is done and working ok)
pip install -m my_new_child_package
After I install the package and add it's urls I would like to have authentication as well. Basically apply same auth of the parent app.
So I am going to have this:
urlpatterns = [
path('api/v1/', include('myapp.routes', namespace='api')),
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider'))
# I want to protect routes under api/v2/ as well with existent auth system.
path('api/v2/', include('my_new_child_package.urls')),
]
Should I configure something in my child app, or can what other alternative do I have? A middleware?
i am trying to connect Okta with a custom Django (v.3.0.2) app i am coding, using the mozilla-django-oidc library. So far the initial user authentication and account creation (using Django's user model) works, but i don't understand what i need to do to have the Django AdminSite work.
The Adminsite, before introducing mozilla-django-oidc worked as expected. I created an admin user, named "admin" and the user was able to login.
To integrate the mozilla-django-oidc library i followed the instructions here: https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html. The instructions do not have any specific mention of the AdminSite.
When i access the AdminSite after the library integration, i have the following:
The AdminSite uses the default template - my assumption was that it
would also use Okta to authenticate.
The admin account "admin" that used to be able to login into the AdminSite does not work anymore
My goal is to be able to access the AdminSite. I don't mind if it will be over Okta or over the vanilla interface as long as i can access it.
Below are the relevant segments from the files (in order to integrate):
urls.py
urlpatterns = [
path('', static_site.site_index, name='site_index'),
path('admin/', admin.site.urls),
path('review/', include('review.urls')),
path('oidc/', include('mozilla_django_oidc.urls')),
]
settings.py
# OICD
AUTHENTICATION_BACKENDS = (
'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
)
OIDC_RP_CLIENT_ID = 'xxxxx'
OIDC_RP_CLIENT_SECRET = 'xxxx'
OIDC_RP_SIGN_ALGO = 'RS256'
OIDC_OP_JWKS_ENDPOINT = 'https://dev-xxx.okta.com/oauth2/default/v1/keys'
OIDC_RP_SCOPES = 'openid email profile'
OIDC_OP_AUTHORIZATION_ENDPOINT = 'https://dev-xxx.okta.com/oauth2/default/v1/authorize'
OIDC_OP_TOKEN_ENDPOINT = 'https://dev-xxx.okta.com/oauth2/default/v1/token'
OIDC_OP_USER_ENDPOINT = 'https://dev-xxx.okta.com/oauth2/default/v1/userinfo'
# Provided by mozilla-django-oidc
LOGIN_URL = reverse_lazy('oidc_authentication_callback')
# App urls
LOGIN_REDIRECT_URL = reverse_lazy('review:dashboard')
LOGOUT_REDIRECT_URL = reverse_lazy('site_index')
Any ideas or pointers welcomed!
The goal was achieved by adding the default auth backend to the settings:
settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
]
I don't get Okta auth for the admin, but since i am happy just to have the admin running, i will stop here.
I've come up with a solution for using the mozilla-django-oidc login with the django admin. It's a little hacky but it's a lot less intimidating to redirect the admin login page than to override AdminSite.
In my top-level urls.py I have
class CustomLogin(View):
def get(self, request, **kwargs):
return HttpResponseRedirect(
reverse('oidc_authentication_init') + (
'?next={}'.format(request.GET['next']) if 'next' in request.GET else ''
)
)
urlpatterns = [
path('oidc/', include("mozilla_django_oidc.urls")),
path('admin/login/', CustomLogin.as_view()),
path('admin/', admin.site.urls),
# the rest of my urls...
]
If you don't care about passing the ?next= value correctly you can skip the CustomLogin class and do the following instead
urlpatterns = [
path('oidc/', include("mozilla_django_oidc.urls")),
]
# This only works if you break up urlpatterns so the reverse below can find what it needs
urlpatterns += [
path('admin/login/', RedirectView.as_view(
url=reverse('oidc_authentication_init') + ?next=/admin/,
permanent=False
)),
path('admin/', admin.site.urls),
# the rest of my urls...
]
I added ?next=/admin/ because by default once you log in you will be redirected to settings.LOGIN_REDIRECT_URL which I'm already using for something else
If you're using the default primary identifier, "email", you can create a superuser with that same email which will give SU privileges to that SSO user. So for example, if you have an SSOuser with email testuser#example.com, you can then run python manage.py createsuperuser and when prompted, set the email to testuser#example.com; the username and password don't matter since you're not actually using them for authentication (if you remove 'django.contrib.auth.backends.ModelBackend' from AUTHENTICATION_BACKENDS). I currently have this working, although I am extending the mozilla backend with the steps recommended in https://mozilla-django-oidc.readthedocs.io/en/stable/installation.html#connecting-oidc-user-identities-to-django-users to prevent users from being created on the fly.
My settings.py:
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.RemoteUserBackend',
'django.contrib.auth.backends.ModelBackend',
]
MIDDLEWARE = [
# ...
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.RemoteUserMiddleware',
# ...
]
ModelBackend is used by the DRF Browsable API.
RemoteUserBackend is used by the frontend app.
If a user logs into the Browsable API, the frontend will send both the auth token and the session token. Both credentials are diferent django users.
AUTHENTICATION_BACKENDS are suposed to work by order, but AuthenticationMiddleware goes first in MIDDLEWARE , it's mandatory.
A session-authenticated user gets wrong data in the frontend app. Django ignores remote user credentials. The user must logout from the browsable API.
How can I fix this?
Just for the record. I simply did a logout from the frontend at page load.
I have a working Django REST API backend. I was previously using session authentication, but would like to move to token based for scaling across multiple servers. I have been researching this for a couple days now and I have not found an answer to my problem. I added the djangorestframework-jwt package to my application but when I try to authenticate is always returns:
{"non_field_errors":["Unable to login with provided credentials."]}
I see in the jwt package where this error is, and can follow the code back through the authentication process. I do not see any errors in the auth process. When I try to create a user with those credentials it says that a user already exists, so I know it is hitting the correct user table. I am not sure why the obtain_jwt_token endpoint will not authenticate my credentials. Below are relevant sections of my django app. Any help would be greatly appreciated. If I am leaving anything out that could help figure this out please let me know and I will upload it. Thanks,
app/settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
),
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100,}
app/urls.py
urlpatterns = patterns('',
# Api
url(r'^api/', include(router.urls)),
url(r'^api/stats', statsviews.StatsView.as_view()),
url(r'^api/testing', statsviews.TestView.as_view()),
url(r'^api/login', 'rest_framework_jwt.views.obtain_jwt_token'),
url(r'^api/logout', logout, {'next_page': '/api/login'}),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
)
curl command
curl -d "email=test#myemail.com&password=test123" http://webhost.mywebsite.com:8080/api/login/
I have a very similar setup to you. A simple app, utilizing vanilla DRF JWT authentication. The only difference that I can tell is that I have rest_framework_jwt included in my INSTALLED_APPS list:
INSTALLED_APPS = (
...
# Third Party Dependencies
'rest_framework',
'rest_framework_jwt',
'corsheaders',
....
Try adding that and see where it gets you.
I encountered the same problem too,and finally found the way out.
following the quick start guide (http://www.django-rest-framework.org/tutorial/quickstart/) , using python manage.py migrate to create table structure; using python manage.py createsuperuser to create an initial user named admin with a password of "password123"; (attention: the passwords mismatch in guides)
now it should be ok.
$ curl -X POST -d "username=admin&password=password123" http://127.0.0.1:8000/api-token-auth/
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwidXNlcl9pZCI6MiwiZW1haWwiOiJhZG1pbkA3amdvLmNvbSIsImV4cCI6MTQ3MDY0NjY4Mn0.Dg4KW5pHHJfuaRzjqHTu8kYIzkq8js9}
I have problem in connecting with Facebook backend of django-social-auth. I have created a Facebook app and in my project setting, I have provided its settings:
INSTALLED_APPS = (
...
'social_auth'
)
AUTHENTICATION_BACKENDS = [
"account.auth_backends.AuthenticationBackend",
'social_auth.backends.facebook.FacebookBackend',
'django.contrib.auth.backends.ModelBackend',
]
LOGIN_URL = "/"
LOGIN_REDIRECT_URLNAME = "home"
LOGOUT_URL = "/"
urlpatterns = patterns('',
...
url(r'', include('social_auth.urls')),
...
)
TEMPLATE_CONTEXT_PROCESSORS = (
...
'social_auth.context_processors.social_auth_by_type_backends',
)
SOCIAL_AUTH_EXPIRATION = 'expires'
FACEBOOK_APP_ID = '***************'
FACEBOOK_API_SECRET = '**************'
FACEBOOK_EXTENDED_PERMISSIONS = ['email', 'user_birthday', 'user_photos']
In 'Site Url' of my app on Facebook I have provided IP of my PC. Now when I connect with Facebook through my IP with my own account then it works fine. But when I try to connect with some other Facebook account then it give the error on permissions page:
"Sorry, something went wrong.We're working on getting this fixed as soon as we can."
Can someone has any idea?
In Facebook each app has certain permissions for Facebook Users, you are the lead developer, you can add testers and what not, Its on the app configuration interface.
For Facebook backend of django-social-auth to work properly then runserver on your IP instead of running on localhost.