mozilla / mozilla-django-oidc

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

Does not authenticate correctly with DRF #328

Closed Venefilyn closed 3 years ago

Venefilyn commented 4 years ago

I've setup a login and callback endpoint directly from mozilla_django_oidc. It redirects to OIDC provider, logs me in, redirects me back, and also creates a correct user on the user table.

However, the session never persists. There is a sessionid cookie being set, though request.user.is_authenticated returns false. This is with DRF, though I don't see why it would have much of an impact on authenticating.

Hopefully there's just a simple oversight..


Here's the configuration I've used so far:

# settings
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'mozilla_django_oidc',  # Load after auth
    'django.contrib.contenttypes',
    # ...
]
# ...
OIDC_DRF_AUTH_BACKEND = 'mozilla_django_oidc.auth.OIDCAuthenticationBackend'
OIDC_STORE_ID_TOKEN = True
OIDC_AUTHENTICATION_CALLBACK_URL = "api:oidc_authentication_callback"
# ... 

REST_FRAMEWORK = {
    # ...
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'mozilla_django_oidc.contrib.drf.OIDCAuthentication'
    ],
}

MIDDLEWARE = [
    # ...
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ...
    'mozilla_django_oidc.middleware.SessionRefresh',
]

requirements.txt:

django-filters
aniso8601==6.0.0
asn1crypto==1.1.0
certifi==2018.8.24
cffi==1.13.0
chardet==3.0.4
coreapi==2.3.3
coreschema==0.0.4
cryptography==2.7
Django==1.11.15
django-common-helpers==0.9.2
django-cors-headers==2.4.0
django-cron==0.5.1
django-debug-toolbar==1.8
django-filter==2.1.0
django-rest-multiple-models==2.1.1
djangorestframework==3.10.3
djangorestframework-bulk==0.2.1
djangorestframework-stubs==0.2.0
drf-url-filters==0.5.1
drf-yasg==1.17.0
factory-boy==2.11.1
Faker==1.0.0
gunicorn==19.4.5
idna==2.7
inflection==0.3.1
itypes==1.1.0
Jinja2==2.10
josepy==1.2.0
MarkupSafe==1.0
memory-profiler==0.55.0
mozilla-django-oidc==1.2.2
openapi-codec==1.3.2
promise==2.2.1
psutil==5.6.3
psycopg2==2.7.3.1
pycparser==2.19
pyOpenSSL==19.0.0
python-dateutil==2.7.5
pytz==2018.5
requests==2.19.1
ruamel.yaml==0.15.70
Rx==1.6.1
simplejson==3.16.0
singledispatch==3.4.0.3
six==1.11.0
sqlparse==0.2.4
text-unidecode==1.2
uritemplate==3.0.0
urllib3==1.23
voluptuous==0.11.5
whitenoise==3.3.1

Edit: with session data in database matching the sessionid:

>>> Session.objects.filter(expire_date__gte=timezone.now())[0].get_decoded()
{'oidc_state': 'IhjyFCKahTrE4KEYBChELPkYKXFdtlnz', 'oidc_login_next': None}

I'm beginning to suspect this is Django rather than this library

Venefilyn commented 4 years ago

After adding AUTHENTICATION_BACKENDS = [mozilla_django_oidc.auth.OIDCAuthenticationBackend] there seems to be progress, although now getting NoReverseUrl, despite having it defined

DEFAULT_CALLBACK_CLASS = 'mozilla_django_oidc.views.OIDCAuthenticationCallbackView'
CALLBACK_CLASS_PATH = import_from_settings('OIDC_CALLBACK_CLASS', DEFAULT_CALLBACK_CLASS)
OIDCCallbackClass = import_string(CALLBACK_CLASS_PATH)

DEFAULT_AUTHENTICATE_CLASS = 'mozilla_django_oidc.views.OIDCAuthenticationRequestView'
AUTHENTICATE_CLASS_PATH = import_from_settings(
    'OIDC_AUTHENTICATE_CLASS', DEFAULT_AUTHENTICATE_CLASS
)

OIDCAuthenticateClass = import_string(AUTHENTICATE_CLASS_PATH)
# namespaced urls
urlpatterns = [
    url(r'^login/$', OIDCAuthenticateClass.as_view(), name='login'),
    url(r'^login/$', OIDCAuthenticateClass.as_view(), name='oidc_authentication_init'),
    url(r'^auth/$', OIDCCallbackClass.as_view(), name='oidc_authentication_callback_alt')
]
# also tried with mozilla urls
urlpatterns = [
    url(r'^oidc/', include('mozilla_django_oidc.urls')),
    url(r'^login/$', OIDCAuthenticateClass.as_view(), name='login'),
    url(r'^auth/$', OIDCCallbackClass.as_view(), name='oidc_authentication_callback_alt')
]

Reverse for 'oidc_authentication_init' not found. 'oidc_authentication_init' is not a valid view function or pattern name.

image

Venefilyn commented 4 years ago

Right, putting

url(r'^api/oidc/', include('mozilla_django_oidc.urls')),

in project root removed the error (presumably due to namespacing) although I would like to specify the URLs myself. Though is_authenticated() still returns false.

Venefilyn commented 4 years ago

I've submitted another PR to help with middleware redirects. It seems the issue with authentication lies with DRF.

Running user.is_authenticated() inside a DRF view always returns false. Whereas outside DRF in plain Django it returns true. This happens despite only having Mozilla Django OIDC in the REST_FRAMEWORK.DEFAULT_AUTHENTICATION_CLASSES setting

Venefilyn commented 4 years ago

Turns out we need to add SessionAuthentication to DRF auth classes, similar to #265, otherwise it will not work

'DEFAULT_AUTHENTICATION_CLASSES': [
    'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
    'rest_framework.authentication.SessionAuthentication',
],