Repository: inventree/inventree
Issue: Token authentication doesn't work for media files behind nginx proxy

- nginx proxy redirects auth requests to `/auth/`
- when using token auth (e.g. python interface or app) server returns either `302` or `403`
- token is being checked, but *user information not being added to the request*
- also request is serviced *before* token is checked

Changed files:
InvenTree/InvenTree/middleware.py

diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py
index b905e8679..3cd5aa74f 100644
--- a/InvenTree/InvenTree/middleware.py
+++ b/InvenTree/InvenTree/middleware.py
@@ -21,28 +21,15 @@ class AuthRequiredMiddleware(object):
 
         assert hasattr(request, 'user')
 
-        response = self.get_response(request)
+        # API requests are handled by the DRF library
+        if request.path_info.startswith('/api/'):
+            return self.get_response(request)
 
         if not request.user.is_authenticated:
             """
             Normally, a web-based session would use csrftoken based authentication.
-            However when running an external application (e.g. the InvenTree app),
-            we wish to use token-based auth to grab media files.
-
-            So, we will allow token-based authentication but ONLY for the /media/ directory.
-
-            What problem is this solving?
-            - The InvenTree mobile app does not use csrf token auth
-            - Token auth is used by the Django REST framework, but that is under the /api/ endpoint
-            - Media files (e.g. Part images) are required to be served to the app
-            - We do not want to make /media/ files accessible without login!
-
-            There is PROBABLY a better way of going about this?
-            a) Allow token-based authentication against a user?
-            b) Serve /media/ files in a duplicate location e.g. /api/media/ ?
-            c) Is there a "standard" way of solving this problem?
-
-            My [google|stackoverflow]-fu has failed me. So this hack has been created.
+            However when running an external application (e.g. the InvenTree app or Python library),
+            we must validate the user token manually.
             """
 
             authorized = False
@@ -56,20 +43,23 @@ class AuthRequiredMiddleware(object):
             elif request.path_info.startswith('/accounts/'):
                 authorized = True
 
-            elif 'Authorization' in request.headers.keys():
-                auth = request.headers['Authorization'].strip()
+            elif 'Authorization' in request.headers.keys() or 'authorization' in request.headers.keys():
+                auth = request.headers.get('Authorization', request.headers.get('authorization')).strip()
 
-                if auth.startswith('Token') and len(auth.split()) == 2:
-                    token = auth.split()[1]
+                if auth.lower().startswith('token') and len(auth.split()) == 2:
+                    token_key = auth.split()[1]
 
                     # Does the provided token match a valid user?
-                    if Token.objects.filter(key=token).exists():
+                    try:
+                        token = Token.objects.get(key=token_key)
 
-                        allowed = ['/api/', '/media/']
+                        # Provide the user information to the request
+                        request.user = token.user
+                        authorized = True
 
-                        # Only allow token-auth for /media/ or /static/ dirs!
-                        if any([request.path_info.startswith(a) for a in allowed]):
-                            authorized = True
+                    except Token.DoesNotExist:
+                        logger.warning(f"Access denied for unknown token {token_key}")
+                        pass
 
             # No authorization was found for the request
             if not authorized:
@@ -92,8 +82,7 @@ class AuthRequiredMiddleware(object):
 
                     return redirect('%s?next=%s' % (reverse_lazy('login'), request.path))
 
-        # Code to be executed for each request/response after
-        # the view is called.
+        response = self.get_response(request)
 
         return response