Running an OpenID-Connect Provider like Kanidm has many advantages, of of them being able to configure it as Single-Sign-On (SSO) for apps that would otherwise require you to have a different set of credentials for each app/account.

I have a working Paperless-ngx running holding about a thousand documents, and it has been great so far. But SSO has always been something that was missing yet. So when I got myself a Kanidm I set out to change that.

Kanidm 1.10.4 running at https://idm.skowalak.de
Paperless-ngx v2.20.15 running at https://paperless.skowalak.de

This is the basic config I had before starting (simplified for brevity):

{ config, ... }:
{
  sops = {
    secrets = { };
    templates."paperless.env" = { };
  };

  services.nginx.virtualHosts."paperless.skowalak.de" = {
    forceSSL = true;
    locations."/".proxyPass = "http://localhost:28981";
  };

  services.paperless = {
    enable = true;

    settings = {
      PAPERLESS_URL = "https://paperless.skowalak.de";
    };
    environmentFile = config.sops.templates."paperless.env".path;
  };
}

Configure Kanidm

I wanted to connect my Kanidm account sebastian@idm.skowalak.de to my Paperless-ngx account sebastian. Both accounts did already exist. So on the Kanidm side the following config was added by using the kanidm cli:

kanidm group create paperless_users
# Successfully created group 'paperless_users'

kanidm group add-members paperless_users sebastian
# Successfully added ["sebastian"] to group "paperless_users"

kanidm system oauth2 create paperless 'Paperless-ngx' 'https://paperless.skowalak.de'
# Success

kanidm system oauth2 add-redirect-url paperless 'https://paperless.skowalak.de/accounts/oidc/kanidm/login/callback/'
# Success

kanidm system oauth2 update-scope-map paperless paperless_users openid profile email
# Success

kanidm system oauth2 prefer-short-username paperless
# Success

kanidm system oauth2 show-basic-secret paperless
# <redacted>

So far so uninteresting, only the second-to-last command is actually interesting. By setting prefer-short-username we tell Kanidm not to use the full Security Principal Name (SPN), which in this case would be sebastian@idm.skowalak.de but instead use the short version without the domain: sebastian.

Configure Paperless

The OIDC client secret is added to sops-nix.

paperless:
  kanidm_client_secret: <redacted>

Then the Paperless-ngx config is amended. Paperless-ngx does OIDC through the openid_connect provider from django-allauth.

{ config, ... }:
{
  sops = {
    secrets = {
      "paperless/kanidm_client_secret" = { };
    };
    templates."paperless.env" = {
      content = ''
        PAPERLESS_SOCIALACCOUNT_PROVIDERS='{"openid_connect":{"OAUTH_PKCE_ENABLED":true,"APPS":[{"provider_id":"kanidm","name":"Kanidm","client_id":"paperless","secret":"${
          config.sops.placeholder."paperless/kanidm_client_secret"
        }","settings":{"server_url":"https://idm.skowalak.de/oauth2/openid/paperless/.well-known/openid-configuration"}}],"SCOPE":["openid","profile","email"]}}'
      '';
      owner = config.services.paperless.user;
    };
  };

  # omitted nginx config here, because nothing changed

  services.paperless = {
    enable = true;

    settings = {
      PAPERLESS_APPS = "allauth.socialaccount.providers.openid_connect";

      # Disable local self-registration but allow remote registration in
      # Kanidm.
      PAPERLESS_ACCOUNT_ALLOW_SIGNUPS = false;
      PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS = true;
      PAPERLESS_SOCIAL_ACCOUNT_DEFAULT_GROUPS = "users";

      # After everything is verified to work, these two switches can be turned
      # on to hide the Paperless-ngx login screen and instead redirect to
      # Kanidm directly.
      PAPERLESS_REDIRECT_LOGIN_TO_SSO = false;
      PAPERLESS_DISABLE_REGULAR_LOGIN = false;

      PAPERLESS_URL = "https://paperless.skowalak.de";
    };
    environmentFile = config.sops.templates."paperless.env".path;
  };
}

Finally, Paperless-ngx user accounts need to be linked to their Kanidm account counterparts. This is done by logging into Paperless-ngx with old credentials, goint into profile settings and clicking on Kanidm under Connect new social account. After that, first party Paperless-ngx login can be disabled.