Repository: learningequality/kolibri

Issue:
Manage os user at login/log out
When in app_mode, an OS user can log out, and then they see this screen, which prompts them to use a password, rather than continuing to use the OS user information. 

```
[
    {
        "id": "PASSWORD_NOT_SPECIFIED",
        "metadata": {
            "field": "password",
            "message": "Username is valid, but password needs to be set before login."
        }
    }
]
```

![image](https://github.com/learningequality/kolibri/assets/17235236/0e07d0e7-5cf4-475e-8bfd-729941e4dffc)



Discussion:

Resolving patch:
diff --git a/kolibri/core/auth/api.py b/kolibri/core/auth/api.py
--- a/kolibri/core/auth/api.py
+++ b/kolibri/core/auth/api.py
@@ -2,6 +2,7 @@
 from __future__ import print_function
 from __future__ import unicode_literals
 
+import logging
 import time
 from datetime import datetime
 from datetime import timedelta
@@ -16,6 +17,7 @@
 from django.contrib.auth.models import AnonymousUser
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.exceptions import PermissionDenied
+from django.core.exceptions import ValidationError
 from django.db.models import Func
 from django.db.models import OuterRef
 from django.db.models import Q
@@ -77,6 +79,7 @@
 from kolibri.core.auth.permissions.general import DenyAll
 from kolibri.core.device.utils import allow_guest_access
 from kolibri.core.device.utils import allow_other_browsers_to_connect
+from kolibri.core.device.utils import APP_AUTH_TOKEN_COOKIE_NAME
 from kolibri.core.device.utils import valid_app_key_on_request
 from kolibri.core.logger.models import UserSessionLog
 from kolibri.core.mixins import BulkCreateMixin
@@ -88,6 +91,9 @@
 from kolibri.plugins.app.utils import interface
 
 
+logger = logging.getLogger(__name__)
+
+
 class OptionalPageNumberPagination(ValuesViewsetPageNumberPagination):
     """
     Pagination class that allows for page number-style pagination, when requested.
@@ -846,6 +852,16 @@ def post(self, request):
 
 @method_decorator(ensure_csrf_cookie, name="dispatch")
 class SessionViewSet(viewsets.ViewSet):
+    def _check_os_user(self, request, username):
+        auth_token = request.COOKIES.get(APP_AUTH_TOKEN_COOKIE_NAME)
+        if auth_token:
+            try:
+                user = FacilityUser.objects.get_or_create_os_user(auth_token)
+                if user is not None and user.username == username:
+                    return user
+            except ValidationError as e:
+                logger.error(e)
+
     def create(self, request):
         username = request.data.get("username", "")
         password = request.data.get("password", "")
@@ -885,7 +901,16 @@ def create(self, request):
             unauthenticated_user = FacilityUser.objects.get(
                 username__exact=username, facility=facility_id
             )
-        user = authenticate(username=username, password=password, facility=facility_id)
+        user = None
+        if interface.enabled and valid_app_key_on_request(request):
+            # If we are in app context, then try to get the automatically created OS User
+            # if it matches the username, without needing a password.
+            user = self._check_os_user(request, username)
+        if user is None:
+            # Otherwise attempt full authentication
+            user = authenticate(
+                username=username, password=password, facility=facility_id
+            )
         if user is not None and user.is_active:
             # Correct password, and the user is marked "active"
             login(request, user)


Test patch: