Repository: ephios-dev/ephios

Issue:
OpenID Logout
**Is your feature request related to a problem? Please describe.**
After logging in via OpenID, our users are also logged in to the Nextcloud, which is used as an identity provider. If the user now presses in Ephios the logout button he remains logged in unnoticed in the Nextcloud. This leads to a security risk, for example, when this happens on public computers. 

**Describe the solution you'd like**
It should be possible to define an endpoint for the logout in the .env file like the other OIDC endpoints. To do this, the implementation of OpenID Connect must be supplemented with a logout function.

**Describe alternatives you've considered**
As a workaround, the OpenID Connect provider can be set so that a logout occurs automatically after a defined time.



Discussion:
The functionality that you describe is not currently part of the OIDC specification. There are specific implementations for some Identity Providers, e.g. https://github.com/mozilla/mozilla-django-oidc/issues/320 explains how to set this up with keycloak for our OIDC library. There is an additional spec (https://openid.net/specs/openid-connect-session-1_0.html) that is either in draft status or not yet implemented by Identity Providers, I wasn't quite sure which one is appropriate. 

So the feature you requested would most probably need some specific code for nextcloud similar to the issue linked above _unless_ you can just redirect the user to a URL on the nextcloud (GET request). But that would mean that the users always end up on the nextcloud login page after logging out from ephios

We did something similar between Nextcloud and Moodle. Here the endpoint index.php/apps/oidc/logout mentioned in the documentation of the Nextcloud OIDC Provider App can be specified in the Moodle OIDC plugin. As a result, the user is forwarded to Nextcloud and logged out when the logout button is clicked. Unfortunately, there is no redirection back to Moodle, but the successful logout does not create a security gap. So that would be preferable.
I suppose you need to hand something to the Identity Provider (token?) to reference the user that should be logged out? Do you know any documentation about how that should be done? If we need to pass something. we might not be able to include that as it would be provider-specific

Regarding the redirect afterwards I had a quick look at the code and https://github.com/H2CK/oidc/blob/master/lib/Controller/LogoutController.php#L266-L273 has a post_redirect_uri that can be passed. But again, this is specific to the provider and therefore can't make it into ephios core. 
About the settings: The identity provider needs a front-channel logout URL from the client and the client needs an IdP logout endpoint. I think a redirect is nice to have, but not absolutely necessary.

Here is the documentation of the Nextcloud app for the logout:
https://github.com/H2CK/oidc#endpoints

I think this might be helpful (this is what the Nextcloud app documentation refers to):
https://openid.net/specs/openid-connect-rpinitiated-1_0.html

The moodle plugin is part of the Microsoft Office 365 implementation in moodle - unfortunately without detailed documentation on the logout.

Resolving patch:
diff --git a/ephios/core/models/users.py b/ephios/core/models/users.py
--- a/ephios/core/models/users.py
+++ b/ephios/core/models/users.py
@@ -96,13 +96,6 @@ class VisibleUserProfileManager(BaseUserManager):
     def get_queryset(self):
         return super().get_queryset().visible()
 
-    # mozilla-django-oidc looks here for a method to create users
-    def create_user(self, username, email):
-        user = self.model(email=email)
-        user.set_unusable_password()
-        user.save()
-        return user
-
 
 class UserProfile(guardian.mixins.GuardianUserMixin, PermissionsMixin, AbstractBaseUser):
     email = EmailField(_("email address"), unique=True)
diff --git a/ephios/extra/auth.py b/ephios/extra/auth.py
new file mode 100644
--- /dev/null
+++ b/ephios/extra/auth.py
@@ -0,0 +1,20 @@
+from django.contrib.auth import get_user_model
+from mozilla_django_oidc.auth import OIDCAuthenticationBackend
+
+
+class EphiosOIDCAB(OIDCAuthenticationBackend):
+    def create_user(self, claims):
+        user = get_user_model()(email=claims.get("email"))
+        user.set_unusable_password()
+        user.first_name = claims.get("given_name", "")
+        user.last_name = claims.get("family_name", "")
+        user.save()
+
+        return user
+
+    def update_user(self, user, claims):
+        user.first_name = claims.get("given_name", "")
+        user.last_name = claims.get("family_name", "")
+        user.save()
+
+        return user
diff --git a/ephios/settings.py b/ephios/settings.py
--- a/ephios/settings.py
+++ b/ephios/settings.py
@@ -433,7 +433,7 @@ def GET_SITE_URL():
 
 if ENABLE_OIDC_CLIENT := env.bool("ENABLE_OIDC_CLIENT", False):
     INSTALLED_APPS.append("mozilla_django_oidc")
-    AUTHENTICATION_BACKENDS.append("mozilla_django_oidc.auth.OIDCAuthenticationBackend")
+    AUTHENTICATION_BACKENDS.append("ephios.extra.auth.EphiosOIDCAB")
     OIDC_RP_CLIENT_ID = env.str("OIDC_RP_CLIENT_ID")
     OIDC_RP_CLIENT_SECRET = env.str("OIDC_RP_CLIENT_SECRET")
     OIDC_RP_SIGN_ALGO = env.str("OIDC_RP_SIGN_ALGO")
@@ -441,3 +441,5 @@ def GET_SITE_URL():
     OIDC_OP_TOKEN_ENDPOINT = env.str("OIDC_OP_TOKEN_ENDPOINT")
     OIDC_OP_USER_ENDPOINT = env.str("OIDC_OP_USER_ENDPOINT")
     OIDC_OP_JWKS_ENDPOINT = env.str("OIDC_OP_JWKS_ENDPOINT")
+    OIDC_RP_SCOPES = env.str("OIDC_RP_SCOPES", "openid profile email")
+    LOGOUT_REDIRECT_URL = env.str("LOGOUT_REDIRECT_URL", None)


Test patch: