Repository: paperless-ngx/paperless-ngx

Issue:
[BUG] OIDC: signup fails if no email server setup
### Description

When setting up OIDC I noticed that the default settings concerning user signup contradict each other which results in an exception that leads to paperless needing a restart. As seen below the default settings disallow signups in general but allow social signups.
![5476CC3D-6940-4A84-BCF0-8BE0D049E885](https://github.com/paperless-ngx/paperless-ngx/assets/39308834/d0d9213c-44d0-437f-bccd-af86c723cb59)

When setting up OIDC signups and leaving the settings as is the signup process is started as expected but fails at the final step.

![IMG_1370](https://github.com/paperless-ngx/paperless-ngx/assets/39308834/0ff0dc64-f4af-42c2-91ee-ae407a9e150e)
![IMG_1371](https://github.com/paperless-ngx/paperless-ngx/assets/39308834/83ee4515-74ec-4c1d-ad24-8b9558763a7d)

Afterwards, when trying to log in with a pre-existing account paperless is not loading.
![IMG_1372](https://github.com/paperless-ngx/paperless-ngx/assets/39308834/4669b648-d6f5-43ca-b96f-006e946d9b90)


After inspecting the logs I can see that paperless failed during the signup since an exception occurred.

The docs do not mention that you have to explicitly allow signups in general - even though it might make sense.

What I suggest should happen:
- check if signups are enabled before trying to perform a signup - or catch the exception when failing to do so
- add a meaningful log entry when the signup failed because it is disabled
- check if signups are enabled before starting the account creation flow in the web ui - and show a meaningful error message if not
- maybe change the default of SOCIALACCOUNT_ALLOW_SIGNUPS to False?
- maybe change the logic when setting SOCIALACCOUNT_ALLOW_SIGNUPS to True? e.g. let this flag overwrite ALLOW_SIGNUPS if ALLOW_SIGNUPS is not explicitly set
- add a remark to the docs section

Anyway, I know that the integration of social accounts came in very recently and therefore I expected some minor problems like this, but all in all it works really well so far. Thank you and keep up the excellent work!

### Steps to reproduce

1. Set up social login according to the documentation - only specifying the PAPERLESS_APPS and
PAPERLESS_SOCIALACCOUNT_PROVIDERS info.
2. Try to login using a social (oidc) account.

### Webserver logs

```bash

paperless         | [2024-02-23 09:22:50,260] [ERROR] [django.request] Internal Server Error: /paperless/accounts/social/signup/
paperless         | Traceback (most recent call last):
paperless         |   File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 534, in thread_handler
paperless         |     raise exc_info[1]
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 42, in inner
paperless         |     response = await get_response(request)
paperless         |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 534, in thread_handler
paperless         |     raise exc_info[1]
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 253, in _get_response_async
paperless         |     response = await wrapped_callback(
paperless         |                ^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 479, in __call__
paperless         |     ret: _R = await loop.run_in_executor(
paperless         |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/asgiref/current_thread_executor.py", line 40, in run
paperless         |     result = self.fn(*self.args, **self.kwargs)
paperless         |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/asgiref/sync.py", line 538, in thread_handler
paperless         |     return func(*args, **kwargs)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 104, in view
paperless         |     return self.dispatch(request, *args, **kwargs)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/socialaccount/views.py", line 45, in dispatch
paperless         |     return super(SignupView, self).dispatch(request, *args, **kwargs)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/views.py", line 95, in dispatch
paperless         |     response = super(RedirectAuthenticatedUserMixin, self).dispatch(
paperless         |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/views.py", line 227, in dispatch
paperless         |     return super(CloseableSignupMixin, self).dispatch(request, *args, **kwargs)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/views/generic/base.py", line 143, in dispatch
paperless         |     return handler(request, *args, **kwargs)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/views.py", line 123, in post
paperless         |     response = self.form_valid(form)
paperless         |                ^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/socialaccount/views.py", line 61, in form_valid
paperless         |     resp = helpers.complete_social_signup(self.request, self.sociallogin)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/socialaccount/helpers.py", line 244, in complete_social_signup
paperless         |     return complete_signup(
paperless         |            ^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/utils.py", line 241, in complete_signup
paperless         |     return perform_login(
paperless         |            ^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/utils.py", line 166, in perform_login
paperless         |     return _perform_login(request, login)
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/utils.py", line 176, in _perform_login
paperless         |     response = adapter.pre_login(request, login.user, **hook_kwargs)
paperless         |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/adapter.py", line 461, in pre_login
paperless         |     send_email_confirmation(request, user, signup=signup, email=email)
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/utils.py", line 399, in send_email_confirmation
paperless         |     email_address.send_confirmation(request, signup=signup)
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/models.py", line 93, in send_confirmation
paperless         |     confirmation.send(request, signup=signup)
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/models.py", line 126, in send
paperless         |     get_adapter().send_confirmation_mail(request, self, signup)
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/adapter.py", line 641, in send_confirmation_mail
paperless         |     self.send_mail(email_template, emailconfirmation.email_address.email, ctx)
paperless         |   File "/usr/local/lib/python3.11/site-packages/allauth/account/adapter.py", line 186, in send_mail
paperless         |     msg.send()
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/core/mail/message.py", line 298, in send
paperless         |     return self.get_connection(fail_silently).send_messages([self])
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/core/mail/backends/smtp.py", line 127, in send_messages
paperless         |     new_conn_created = self.open()
paperless         |                        ^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/site-packages/django/core/mail/backends/smtp.py", line 85, in open
paperless         |     self.connection = self.connection_class(
paperless         |                       ^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/smtplib.py", line 255, in __init__
paperless         |     (code, msg) = self.connect(host, port)
paperless         |                   ^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/smtplib.py", line 341, in connect
paperless         |     self.sock = self._get_socket(host, port, self.timeout)
paperless         |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/smtplib.py", line 312, in _get_socket
paperless         |     return socket.create_connection((host, port), timeout,
paperless         |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
paperless         |   File "/usr/local/lib/python3.11/socket.py", line 851, in create_connection
paperless         |     raise exceptions[0]
paperless         |   File "/usr/local/lib/python3.11/socket.py", line 836, in create_connection
paperless         |     sock.connect(sa)
paperless         | OSError: [Errno 99] Cannot assign requested address

```


### Browser logs

_No response_

### Paperless-ngx version

latest - 2.5.3

### Host OS

should be irrelevant but RasPi OS arm

### Installation method

Docker - official image

### Browser

_No response_

### Configuration changes

_No response_

### Other

_No response_

### Please confirm the following

- [X] I believe this issue is a bug that affects all users of Paperless-ngx, not something specific to my installation.
- [X] I have already searched for relevant existing issues and discussions before opening this report.
- [X] I have updated the title field above with a concise description.


Discussion:
Given that's allauth crashing, this seems better reported to them.  There's not even a place there where paperless code is involved.

Also, `OSError: [Errno 99] Cannot assign requested` is just a low level network problem that is probably something about your own system.
Hi @stumpylog thanks for the clarification. I am obviously not aware of how exactly you interact with django-allauth and where or how much paperless code is involved but the issue is not (only) about the error message that was printed to the logs and I think some if not all of my suggestions are still applicable.

I would assume that the two config options are handled by paperless, or am I wrong here? If so you could mitigate this exception altogether by not starting the signup flow at all when the value for PAPERLESS_ALLOW_SIGNUPS is set to False or by changing the logic of these flags (as suggested above).

Again, I do not know much about django and I did not look through the paperless code but when I use a library and in some scenarios that library might throw an exception effectively making my app unusable until it’s restarted I either assure that these scenarios cannot occur or at least catch the exception and show a meaningful message in the logs for an admin to check and preferably in the UI for the user to get a sense of what failed. So no matter if allauth crashed this could be handled by paperless. At least that’s my understanding, please correct me if I got something wrong.

My last suggestion was to add a remark to the docs, I think that should be possible even if nothing is changed code wise and regardless of why the error occurs in this scenario.
You do not need `PAPERLESS_ACCOUNT_ALLOW_SIGNUPS` enabled. I just checked this again and signed up for an account with only: 

```
PAPERLESS_APPS="allauth.socialaccount.providers.github,allauth.socialaccount.providers.openid_connect"
PAPERLESS_SOCIALACCOUNT_PROVIDERS='{"github": {"APPS": [{"provider_id": "github","name": "Github New","client_id": "***","secret": "***"}]}}'
```

The error above seems to be an email thing (and doesnt mention `PAPERLESS_ACCOUNT_ALLOW_SIGNUPS`?). I also did the above test without any email server setup so either theres a bug in allauth (please raise the issue there as noted, see https://github.com/pennersr/django-allauth/issues ) or you just have it misconfigured.

Feel free to add more if we're missing something here, otherwise I'll close this out.
Hi @shamoon thank you for your input. Based on your pointers I tried reproducing the crash with a clean install and investigated a bit further. I was able to reproduce it but you are correct, the ``PAPERLESS_ACCOUNT_ALLOW_SIGNUPS`` option has nothing to do with it. It simply worked when I enabled that option and restarted Paperless and thus I assumed there must be a causal relationship between these two, but the restart part actually let me log in without an error.

Nonetheless the crash on signup consistently occurs in two situations:
- you try to sign up with an email that is already in use
- you try to sign up with an invalid email address

In both of these cases the Ul successfully forces you to enter a valid email address that is not in use already (see screenshot below), but even when you then change the email address in the Ul, after you hit that "Sign up" button you are greeted with the server error screen and log error message I described above. Apparently (guessing from the experimenting and the log output i sent) the “old” email address that was provided by the OICD provider is used to send out a confirmation mail (even when no mail server is configured).

After restarting Paperless I was able to log in using OICD and the created profile in paperless had the changed valid email address connected.

![IMG_1384](https://github.com/paperless-ngx/paperless-ngx/assets/39308834/a3ced88a-f22c-4753-8ca8-5a8a7680a093)


Resolving patch:
diff --git a/src/paperless/settings.py b/src/paperless/settings.py
--- a/src/paperless/settings.py
+++ b/src/paperless/settings.py
@@ -441,6 +441,11 @@ def _parse_beat_schedule() -> dict:
 
 AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME")
 
+ACCOUNT_EMAIL_VERIFICATION = os.getenv(
+    "PAPERLESS_ACCOUNT_EMAIL_VERIFICATION",
+    "optional",
+)
+
 if AUTO_LOGIN_USERNAME:
     _index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
     # This overrides everything the auth middleware is doing but still allows


Test patch: