paperless-ngx
paperless-ngx copied to clipboard
Feature: Add OpenID Connect SSO support via django-allauth
Proposed change
I use OpenID Connect (OIDC) for SSO with my self-hosted web apps. I've been excited to try out paperless-ngx for a while, but there is currently no support for OpenID Connect. This change adds support for OpenID Connect for SSO via django-allauth, with the potential for also supporting django-allauth's numerous SSO options.
OpenID Connect support enables SSO use via self-hosted providers like Authentik and Keycloak.
~Note: This PR is based on my upstream PR in django-allauth. This code works with a small Dockerfile change (see below) but should be considered a working concept until my upstream PR is merged.~ django-allauth 0.52.0 has been released which contains support for OpenID Connect!
Sample OIDC configuration:
PAPERLESS_SSO_OIDC_NAME="Test OIDC"
PAPERLESS_SSO_OIDC_URL="https://some.oidc.server.example.com"
PAPERLESS_SSO_OIDC_CLIENT_ID="some-client-id-from-your-oidc-server"
PAPERLESS_SSO_OIDC_SECRET="accompanying-secret-from-your-oidc-server"
With no SSO configured, this PR does not change the login behavior:
However, SSO options will now appear on the login screen when configured:
If SSO is configured, it is also possible to hide the password form via a new setting PAPERLESS_LOGIN_HIDE_PASSWORD_FORM
:
Type of change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Other (please explain)
Checklist:
- [x] I have read & agree with the contributing guidelines.
- [x] If applicable, I have tested my code for new features & regressions on both ✅ mobile & ✅ desktop devices, using the latest version of major browsers.
- [ ] If applicable, I have checked that all tests pass, see documentation.
- [x] I have run all
pre-commit
hooks, see documentation. - [ ] I have made corresponding changes to the documentation as needed.
- [x] I have checked my modifications for any breaking changes.
Hello @smkent,
thank you very much for submitting this PR to us!
This is what will happen next:
- My robotic colleagues will check your changes to see if they break anything. You can see the progress below.
- Once that is finished, human contributors from paperless-ngx review your changes. Since this is a non-trivial change, a review from at least two contributors is required.
- Please improve anything that comes up during the review until your pull request gets approved.
- Your pull request will be merged into the
dev
branch. Changes there will be tested further. - Eventually, changes from you and other contributors will be merged into
main
and a new release will be made.
Please allow up to 7 days for an initial review. We're all very excited about new pull requests but we only do this as a hobby. If any action will be required by you, please reply within a month.
Pull Request Test Coverage Report for Build 3909301335
- 0 of 0 changed or added relevant lines in 0 files are covered.
- 60 unchanged lines in 1 file lost coverage.
- Overall coverage decreased (-0.9%) to 91.757%
Files with Coverage Reduction | New Missed Lines | % |
---|---|---|
paperless/settings.py | 60 | 77.78% |
<!-- | Total: | 60 |
Totals | |
---|---|
Change from base Build 3896206563: | -0.9% |
Covered Lines: | 5443 |
Relevant Lines: | 5932 |
💛 - Coveralls
This is awesome, thanks for the PR. Let's keep an eye on your upstream PR and can circle back to this hopefully soon. When the time comes I think we should update the documentation to reflect this too. Bravo 👏
Looking forward for this PR being approved!
Not sure if this will be useful at all but I did something similar a while ago and did a write up about it. https://ciphermenial.github.io/posts/adding-oauth-to-paperless/
please merge! Using a central auth is an essential feature in my opinion
This PR is a draft for the reasons stated when it opened. Once django-allauth has merged and released, it will get reviewed and merged.
I'm waiting the merge because it's a really useful feature for my usecase ! Thank you for your work, it's amazing !
well well https://github.com/pennersr/django-allauth/pull/3165 !
Alas just waiting on the maintainer of that package to push a new release first, but yes, good news regardless!
Version 0.52.0 of django-allauth has just been released. Can't wait to test this with authelia.
I've tested Django allauth over on Tandoor recipes, works pretty well Authelia!
@smkent Any free time to resolve the conflicts and mark this as ready? I think people would love to see it in
I am wondering about the order of things with this vs #2147 , which I know is a beast but would like to get to soon. My thinking was it would be better to merge that in before this, but not like that’s a hard requirement or anything
I've updated this PR with django-allauth 0.52.0 and merged in dev
to resolve the merge conflicts.
I'm interested in writing unit tests for this feature. Please let me know what else I can do to help!
I have no idea how to write testing for the feature, but perhaps the allauth tests can be looked to for an example. It would be best to have the feature covered.
Recently Gitea had implemented some oauth2 tests which can give some idea on the proper flow of tests and which endpoints to call. While oidc is not entirely oauth2, I think it can at least give some general hints about what to test.
I'd like to start testing the image out and see how it works, but one thing still lacking from the PR is documentation of the feature. There's a number of new settings to document and I think one or two setup guides would be very helpful. These could be generic, or could be a little more specific to some of the common tools like Authelia and Authentik
Hi, I already did some basic testing with authelia last week and the setup was very easy except one issue I encountered.
Just adding a new OIDC client in authelia and setting 4 envs went pretty smoothly, but authelia also requires a redirect_uri
to be set on both sides, which must be exactly the same.
Authentication always failed because django-allauth set the redirect_uri
to https://
which I wasn't even using. I couldn't find anything in the documentation to change this, and also manually setting the PAPERLESS_URL
to http://
didn't help. So I set up a local reverse proxy to not bother with django ssl certs and finally got this working.
Hope this helps if someone is encountering the same issue, and pls let me know if there is a way to change this config.
Is there a way to test this/does a Docker image already exist? I just switched all my services to Keycloak and can't wait to try out paperless-ngx! :)
redirect_uri
Hi, I already did some basic testing with authelia last week and the setup was very easy except one issue I encountered. Just adding a new OIDC client in authelia and setting 4 envs went pretty smoothly, but authelia also requires a
redirect_uri
to be set on both sides, which must be exactly the same.Authentication always failed because django-allauth set the
redirect_uri
tohttps://
which I wasn't even using. I couldn't find anything in the documentation to change this, and also manually setting thePAPERLESS_URL
tohttp://
didn't help. So I set up a local reverse proxy to not bother with django ssl certs and finally got this working.Hope this helps if someone is encountering the same issue, and pls let me know if there is a way to change this config.
how do you set the redirect_uri
in Authelia?
I've tested Django allauth over on Tandoor recipes, works pretty well Authelia!
how did you set the redirect_uris
in Authelia?
how do you set the
redirect_uri
in Authelia?
I would say that question is better suited for Authelia and not related to this issue here, but to be sure it is well documented here: https://www.authelia.com/configuration/identity-providers/open-id-connect/. You just set the redirect_uris
in your config, but again this issue and this repo is not about Authelia, but about implementing OIDC support for paperless-ngx via django-allauth. This means any OIDC provider should work.
@hendrik1120 thanks for your testing, I think I'll want to use this setup as well now since I'm already using HTTPS for everything as well as Authelia.
It works! Whoop Whoop! 🎉🪩🎉
Step-by-step repo for a docker container (@mwllgr asked). This is a very "hacky" approach but works ™ - don't use in production etc.
-
create a directory for all source files
mkdir oidc-patch
-
get the current sources
git checkout https://github.com/paperless-ngx/paperless-ngx
-
fetch the files from the pull request
git fetch origin pull/1746/head:oidc-patch
-
edit the file
oidc-patch/docker/docker-entrypoint.sh
We need to install django-allauth on container startup since this wasn't done during container creation, so add in e.g line 82pip install -v "django-allauth~=0.52"
Note: this means startup will take a while because django-allauth is installed everytime, of course this would need to go into the Dockerfile, but hey - we are testing -
adjust your docker-compose You're pretty much just mapping the current
/dev
directory into the container as well as overwrite the entrypoint for allauth installation. You need to adjust the paths to where you cloned the PR files.
volumes:
[...]
### oidc-patch
- $DOCKERDIR/paperless/oidc-patch/src:/usr/src/paperless/src
- $DOCKERDIR/paperless/oidc-patch/docker/docker-entrypoint.sh:/sbin/docker-entrypoint.sh
- Set the environment variables
environment:
[...]
### oidc-patch
PAPERLESS_SSO_OIDC_NAME: "Authelia"
PAPERLESS_SSO_OIDC_URL: "<authelia-url>"
PAPERLESS_SSO_OIDC_CLIENT_ID: "<client-id>"
PAPERLESS_SSO_OIDC_SECRET: "<client-secret>"
PAPERLESS_LOGIN_HIDE_PASSWORD_FORM: "false"
- In Authelia / your OIDC provider create a new config
- id: <client-id>
description: Paperless OIDC
secret: <client-secret>
scopes:
- openid
- profile
- email
redirect_uris:
- https://<paperless-url>/accounts/authelia/login/callback/
Note: The callback URL is HTTPS and has a trailing slash and I believe the authelia
part in the URL stems from PAPERLESS_SSO_OIDC_NAME
, but I didn't dig into this.
There's also the pre-built image ghcr.io/smkent/paperless-ngx:feature-oidc-allauth
@DennisGaida That's detailed setup, thanks. I may be able to document setup on this side enough with those.
There's also the pre-built image
ghcr.io/smkent/paperless-ngx:feature-oidc-allauth
Oh... well that would have been easier to use ;-)
setupwise if it were merged you pretty much only need the four OIDC
environment variables (hide login being optional). Setup in Authelia (don't know about others) was easy. The redirect_url
follows some kind of logic, that would need to be documented. That the redirect URL is https is fine by me, everything should be https anyways also locally.
What also would need to be documented is how user mapping is done. Luckily my paperless-user as well as the authelia-user are the same, but I wouldn't know what OIDC property is used for that (e.g. openid.sub
, email.email
, profile.username
). In my case it just works, but for production this would need to be made clear, i.e. "your profile.username needs to be the same as your paperless username" or something like that.
There's also the pre-built image
ghcr.io/smkent/paperless-ngx:feature-oidc-allauth
@DennisGaida That's detailed setup, thanks. I may be able to document setup on this side enough with those.
this image works perfect.
Codecov Report
Merging #1746 (54542b1) into dev (40db244) will decrease coverage by
0.91%
. The diff coverage is29.88%
.
@@ Coverage Diff @@
## dev #1746 +/- ##
==========================================
- Coverage 93.11% 92.20% -0.91%
==========================================
Files 140 142 +2
Lines 5997 6083 +86
==========================================
+ Hits 5584 5609 +25
- Misses 413 474 +61
Flag | Coverage Δ | |
---|---|---|
backend | 92.20% <29.88%> (-0.91%) |
:arrow_down: |
Flags with carried forward coverage won't be shown. Click here to find out more.
Impacted Files | Coverage Δ | |
---|---|---|
src/paperless/allauth_custom.py | 0.00% <0.00%> (ø) |
|
src/paperless/urls.py | 100.00% <ø> (ø) |
|
src/paperless/settings.py | 77.63% <43.75%> (-6.42%) |
:arrow_down: |
src/documents/templatetags/django_settings.py | 83.33% <83.33%> (ø) |
:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more
One thing I have noticed:
- A new user is created for the OIDC user
- OIDC breaks internal authentication
I now have two users: "user" and "user8". user8 represents the OIDC user. the 8 seems to be some kind of autoincrement. When I delete the user in sqlite in auth_user
as well as in socialaccount_socialaccount
, and repeat the OIDC process I get a new user with a new "id", e.g. user7. With the OIDC changes enabled I can log in via OIDC - I can not login with the original user using regular login (wrong password).
Checking the DB logs (I added 'django.db.backends': { 'handlers': ['console'], 'level': 'DEBUG'},
in line 630 in settings.py
), I see that my useraccount is queried and found, but somehow a new user is created. Maybe this is the default and is as it is supposed to be? A new user means new UI settings and views, and of course I only want one user.
I got around the duplicate user issue now by manually editing the socialaccount_socialaccount
table, setting the socialaccount_socialaccount.user_id
to my original user id (of "user"). I can now login via OIDC and I am the real user. I can also access the django admin panel like that. The other use that was created kind of is an orphan now and I just went ahead and deleted it... this of course shouldn't be the process and the socialaccount should just have been linked I suppose?
Once I disable the OIDC changes, I can login again with the original user.
Of course I'd love just one user and for both authentication methods to work at the same time.
@stumpylog what kind of documentation do you have in mind? For people with authelia and oidc already set up, it's just as easy as setting 4 variables and using paperless with https. Only documentation for the variables would be needed and a warning about the redirect uri https issue.
Oidc in general really only makes sense to set up if you have a lot of services to log in to. Providing a complete setup for keycloak or authelia with all the security considerations will be a long journey. These services also already have their own documentation available on how to integrate common apps, see https://www.authelia.com/integration/openid-connect/. Including paperless in there would also be an option.