Opt-in access mode

I think it should make sense that something like django-login-required-middleware · PyPI would be included in Django. Building sites that are primarily behind a login is needlessly complex. It would be nicer if Django had something like settings.OPT_IN_ACCCESS, and a @public decorator (for both FBVs and CBVs), and maybe a mixin for CBVs.

This is likely a good idea.

Similar ideas pop up over time. There was this thread not too long ago on django-developers.

To quote @adamchainz there:

I personally am in favour of including this in Django at this point.

It seems like a desirable feature, it has circulated the community since 2008 (!), and there are at least two community packages implementing the pattern. No one on this thread is explicitly against it ever happening.

I’m in favour of the approach of a second middleware rather than a setting for AuthenticationMiddleware. We do try to avoid settings and I think this is a more sensible approach.

It would be relatively little code and documentation overhead - a middleware (maybe LoginRequiredAuthenticationMiddleware ?), a decorator (maybe @login_not_required ? - the opposite of @login_required), and a view mixin (LoginNotRequired ?).

Such a middleware would use the process_view() hook to see if a the resolved view was exempted before checking auth.

I think a decorator would be sufficient. It’s easy enough to use those with CBVs (either in the URL conf, or via method_decorator).

I’d suggest not too-many options. (e.g. if I want to customise auth for a single view, I’d mark it except and then implement the per-view auth in place, rather than have extra-options in the middleware, which would complicate usage, and probability of getting done.)

+1

1 Like

I guess we’d want a class based middleware with some simple hook points so it’s easy to customize a bit. A common use case would be to have allow lists. At least for the first shipping version that might not be needed in the middleware.

This kind of case is interesting…

<not something we’ll address here>
Because Django generally loads pluggable bits via an import string, rather than a class itself, you need to create a subclass in order to customise, which feels heavy, so folks want settings, but then we end up with far too many settings all prefixed COOKIE_ or EMAIL_ which are nothing but passthrough parameters for the underlying whatever.

My view is that we need to look at the first 1 or two steps there, so we can better encapsulate settings with the code they’re configuring.
</not something we’ll address here>

As you suggest, I’d start with no-config, and then perhaps allow a subclass to customise the check, for instance with an allowlist.

Thank you for bringing this up again @boxed . I hope we can get this in now, I’m happy to review any PRs, even a draft.

I don’t think we’d need any subclass hook. The middleware will be very simple, like a few lines long:

class LoginRequiredMiddleware(MiddlewareMixin):
    def process_view(self, request, view_func, view_args, view_kwargs):
        if not request.user.is_authenticated:  # also check if view exempt
            raise PermissionDenied(...)
        return None

A subclass to add extra checks could override process_view():

class MyLoginRequiredMiddleware(LoginRequiredMiddleware):
    def process_view(self, request, view_func, view_args, view_kwargs):
        super().process_view()
        if not request.user.id in SOME_ALLOW_LIST:  # also check again if view exempt
            raise PermissionDenied(...)

Any overridable allow() or deny() overridable wouldn’t really simplify things.

2 Likes

So I believe i have good news that this is already in progress and reviews are welcome :grin:

3 Likes