mozilla / mozilla-django-oidc

A django OpenID Connect library
https://mozilla-django-oidc.readthedocs.io
Mozilla Public License 2.0
449 stars 168 forks source link

How to use this with Django-rest-framework? (Client-side OAuth2 flow?) #502

Open ghost opened 1 year ago

ghost commented 1 year ago

With the browsable api everything works and I can access my endpoints and admin page using the OIDC provider login, but I am unsure how to format a curl call to include authentication without the browser-session.

I am trying to set up an Oauth2 flow where the front-end gets an access token from the OIDC provider and sends that to my django rest backend to exchange it for a django token. Does that work with this package? Or should I user a server-side OAuth flow where the callback is send to my django server?

Any help is much appreciated!

afmorielo commented 1 year ago

Hello there! I am not one of the developers of this library, but I use it in a project with Django Rest Framework. In my project I use what you refer to as a server-side OAuth Flow:

1) My settings.py has several variables, including:

OIDC_STORE_ACCESS_TOKEN = True

This means that after login, the access token will be stored in the Django Session object (https://mozilla-django-oidc.readthedocs.io/en/stable/settings.html#OIDC_STORE_ACCESS_TOKEN) The Django Rest Framework is configured as follows:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    )
}

2) My urls.py file have something similar to

urlpatterns = [
  path('admin/', admin.site.urls),
  path(...),
  path(...),
  path('oidc/', include('mozilla_django_oidc.urls')),
]

3) I login via https://myappurl.com/oidc/authenticate

And after login I redirect to my app home page The access token is now stored in the Django Session object, server-side

4) I make requests to the api in the front-end normally, like a POST or GET to https://myappurl.com/api/

5) The front-end calls to the api trigger functions in the back-end and I can use the access token there

def list_products(request):
    token = request.session['oidc_access_token']
    # now I have the token and I can use it if needed
afmorielo commented 1 year ago

If you are using curl to make requests, there is no way that I know of to get the access token because there will not be a session created (sessions are created for browsers). Whenever I need to test something in the REST api using curl I either: 1) use Django Tokens authentication OR 2) have an access token string saved beforehand and pass it in the request HTTP header/body. But I usually avoid using curl because I can generally test the api calls in the browser.

afmorielo commented 1 year ago

Finally, you could of course implement every call to your OIDC Provider yourself. I use Keycloak as an OIDC Provider. But then again, if you are doing that, I don't see the reason to use this library.

ghost commented 1 year ago

Thanks for the info!

ghost commented 1 year ago

If you are using curl to make requests, there is no way that I know of to get the access token because there will not be a session created (sessions are created for browsers). Whenever I need to test something in the REST api using curl I either: 1) use Django Tokens authentication OR 2) have an access token string saved beforehand and pass it in the request HTTP header/body. But I usually avoid using curl because I can generally test the api calls in the browser.

A rather late follow-up, but did you manage to combine this package with token authentication instead of the session authentication? Or do you use token authentication without mozilla-django-oidc?

afmorielo commented 1 year ago

Hello there!

I believe you are referring to Django token authentication, in which each user is assigned a token stored in the app's database managed by Django. I have experience using this authentication method.

These tokens are created and managed by Django, often through the Django Admin web app. Therefore, these "Django tokens" are distinct from OAuth tokens, to the best of my knowledge. They are typically used as an authentication option in a REST API when OAuth is not in use. To use them, you include the "Django tokens" in every API request, enabling it to validate the requesting user, much like a password.

To implement OAuth tokens with the mozilla-django-oidc library, I had to switch to session authentication in Django. Fortunately, Django allows you to enable multiple authentication systems simultaneously. You can choose between them as needed by configuring the DEFAULT_AUTHENTICATION_CLASSES variable in your settings.py file. It will attempt each authentication method in order until it finds a valid one. Here's an example configuration:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}