mirror of https://github.com/zulip/zulip.git
docs: Add documentation for the new custom auth wrapper setting.
(cherry picked from commit 5c8d588b52
)
This commit is contained in:
parent
0ebe623b42
commit
bfcde65449
|
@ -1069,6 +1069,99 @@ configure the JWT secret and algorithm via `JWT_AUTH_KEYS` in
|
|||
`/etc/zulip/settings.py`; see the inline comment documentation in that
|
||||
file for details.
|
||||
|
||||
## Configuring a custom Python wrapper around the `authenticate` mechanism
|
||||
|
||||
Zulip supports configuring a custom authentication function that will
|
||||
work as a wrapper around every login attempt to Zulip, enabling custom
|
||||
logging, additional authentication checks, and more.
|
||||
|
||||
This mechanism protects the web login and the mobile login process
|
||||
used to obtain an API key, but **will not be called** when processing
|
||||
API requests by the Zulip mobile apps or other API clients that have
|
||||
already obtained an API key (a step that typically happens once per
|
||||
device during first-time login).
|
||||
|
||||
:::{note}
|
||||
Knowledge of [how authentication backends work in Django][django-authenticate-details]
|
||||
as well as some familiarity with Zulip's authentication implementation in
|
||||
`zproject/backends.py` are required.
|
||||
|
||||
This feature is beta and has some rough edges as well as requiring
|
||||
significantly more expertise than other authentication features; we do
|
||||
not recommend using it without specific advice from Zulip support, but
|
||||
we document it here for completeness.
|
||||
:::
|
||||
|
||||
You can write custom Python logic that will wrap such `authenticate()`
|
||||
calls by specifying in `/etc/zulip/settings.py`:
|
||||
|
||||
```python3
|
||||
def custom_auth_wrapper(
|
||||
auth_func, *args, **kwargs
|
||||
):
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
|
||||
backend = args[0]
|
||||
backend_name = backend.name
|
||||
request = args[1]
|
||||
ip_address = request.META["REMOTE_ADDR"]
|
||||
backend.logger.info("%s backend. ip is %s", backend_name, ip_address)
|
||||
|
||||
user_profile = auth_func(*args, **kwargs)
|
||||
if user_profile is not None and user_profile.delivery_email == "protecteduser@example.com":
|
||||
if backend_name == "email" and ip_address != "x.x.x.x":
|
||||
raise JsonableError("Your IP address is not allowed to log in as this user.")
|
||||
|
||||
return user_profile
|
||||
|
||||
# We need to actually specify to use this function defined above as the
|
||||
# custom authentication wrapper:
|
||||
CUSTOM_AUTHENTICATION_WRAPPER_FUNCTION = custom_auth_wrapper
|
||||
```
|
||||
|
||||
`auth_func` is the underlying `authenticate` function belonging to the
|
||||
authentication backend class currently being processed. If you have
|
||||
more than one backend enabled, this will be executed multiple times -
|
||||
each time with `auth_func` being the `authenticate` function of the
|
||||
backend class currently being processed.
|
||||
|
||||
Therefore, your `custom_auth_wrapper` can inspect the received
|
||||
arguments, process them as desired and eventually call the original
|
||||
`auth_func` to obtain the underlying authentication result, to analyze
|
||||
and potentially return it. The simple code demonstrated above checks
|
||||
whether `auth_func` succeeds, and if so, whether the resulting user
|
||||
account belongs to `protecteduser@example.com`. Such authentication,
|
||||
if it's using the `EmailAuthBackend` should only be allowed if made
|
||||
from a pre-defined IP address `x.x.x.x`, so if these restrictions are
|
||||
violated, a JSON error response will be generated.
|
||||
|
||||
The example demonstrates the possibility of making the logic dependent
|
||||
on the specific authentication backend being used, but unless you're
|
||||
very familiar with the various backends used by Zulip, a safer
|
||||
approach is to keep things general.
|
||||
|
||||
:::{important}
|
||||
|
||||
Using `CUSTOM_AUTHENTICATION_WRAPPER_FUNCTION` with social
|
||||
authentication backends (Google, GitHub, and anything else that
|
||||
subclasses `SocialAuthMixin` in `zproject/backends.py`) is not
|
||||
recommended.
|
||||
|
||||
Due to the different way that social authentication backends process
|
||||
authentication attempts using a 3rd party site that provides the
|
||||
user's identity, it is **not** a good approach to modify their own
|
||||
`authenticate` calls directly.
|
||||
|
||||
If you need to use this feature in combination with those backends,
|
||||
you should make your logic be applied when processing the
|
||||
`ZulipDummyBackend` - which is the final layer of the authentication
|
||||
checks for whether authentication should succeed. If you want to
|
||||
reject authentication requests e.g. based on IP address of the
|
||||
request, this is where it should happen.
|
||||
:::
|
||||
|
||||
[django-authenticate-details]: https://docs.djangoproject.com/en/dev/topics/auth/customizing/#writing-an-authentication-backend
|
||||
|
||||
## Adding more authentication backends
|
||||
|
||||
Adding an integration with any of the more than 100 authentication
|
||||
|
|
Loading…
Reference in New Issue