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.