On Fri 21 Aug 2026 at 10:44, Kejvan Redjamand <kejvan.redjamand@chalmers.se> wrote:
Hi,
We have a problem in authentication with shibboleth (SSO) and nav, which we would like to know if there is any resolution for.
Hi Kejvan, Thanks for the unusually well-researched report. Your diagnosis is correct, and you pointed at exactly the right method. This is a genuine regression in NAV, not a configuration problem on your end. We knew the rewrite of the auth system in 5.17 could be a problem, particularly for the REMOTE_USER backend, which is why we took particular care to note the need for proper testing in the 5.17.0 changelog.
It is a code defect probably introduced in 5.17.0 and probably still present, unchanged, in 5.19.0 (latest at time of writing).
Confirmed on both counts.
It seems to be some shortcomings in the code in file (`/opt/venvs/nav/lib/python3.13/site-packages/nav/web/auth/backends.py`)
def authenticate(self, request, user):
That signature is likely the entire bug. Django's `RemoteUserMiddleware` calls the backend like this: user = auth.authenticate(request, remote_user=username) and `django.contrib.auth.authenticate()` does not simply call every backend in turn - it first binds the credential keyword arguments against each backend's signature, and silently skips any backend that doesn't accept them: backend_signature = signature(backend.authenticate) try: backend_signature.bind(request, **credentials) except TypeError: # This backend doesn't accept these credentials as arguments. # Try the next one. continue Since `NAVRemoteUserBackend.authenticate()` names its parameter `user` and not `remote_user`, binding raises `TypeError`, and the backend is never called at all. Nothing in the class body ever runs - which is why no amount of `REMOTE_USER`, varname or autocreate configuration makes any difference.
... request.META["REMOTE_USER"]: "user@xxx.yyy" [INFO] nav.web.auth.signals] failed login: None
That log line is the smoking gun, and it also explains the `None`. Once `NAVRemoteUserBackend` is skipped, the credentials fall through to the remaining backends (`ModelBackend`, NAV's LDAP backend, allauth). Those all accept `**kwargs`, so they *do* bind, get handed no username and no password, and duly return `None`. Django then fires `user_login_failed`, and NAV's log receiver looks for the credential under the keys "username", "login" or "user" - but the actual key is "remote_user", so it finds nothing and logs `None`. Two symptoms, one root cause.
Auto creating accounts does not work, since the remote_user is not passed along right.
Right - account autocreation lives inside the backend that never gets invoked, so it can't fire. Same for the audit log entry that would normally record the account creation.
Is there any suggested solution?
Essentially a one-word change. If you need SSO working before the next release, you can patch your installation: --- a/nav/web/auth/backends.py +++ b/nav/web/auth/backends.py @@ from nav.web.auth import remote_user +from nav.web.auth.remote_user import CONFIG @@ - def authenticate(self, request, user): - if not remote_user.CONFIG.is_remote_user_enabled(): + def authenticate(self, request, remote_user): + if not CONFIG.is_remote_user_enabled(): return None - - user = super().authenticate(request, user) - return user + return super().authenticate(request, remote_user) The extra import is not gratuitous: naming the parameter `remote_user` shadows the imported `nav.web.auth.remote_user` module inside the method body, so `remote_user.CONFIG` on the next line would blow up. Renaming the parameter alone is not enough. The packaged fix will also correct the "failed login: None" log line, and add a regression test that goes through `django.contrib.auth.authenticate()` rather than calling the backend directly - calling it directly, with positional arguments, is precisely why our existing unit tests never caught this. I've filed this as https://github.com/Uninett/nav/issues/4168 so you can follow along. We'll target a 5.19.x patch release. Thanks again for taking the time to dig into the code and to verify the Apache side independently - it made this a five-minute diagnosis instead of a long back-and-forth. -- Sincerely, Morten Brekkevold Sikt – Norwegian Agency for Shared Services in Education and Research