Repository: castagnait/plugin.video.netflix
Issue: MSLError: Not permitted to switch to the requested user profile

### Netflix add-on version

1.18.9+matrix.1

### Operative systems used

Android

### Kodi version used

Kodi 19 (Matrix)

### Description of the bug

When trying to play any show my with "my" profile, I got this error:
MSLError: Not permitted to switch to the requested user profile

Later I found out, that using the default/owner account works (but I don't wanna use that for me / on this device / not now). Switching back to "my" profile reproduced the error.

BTW: I see a bunch of "signatures" profile GUIDs in the log, not so sure I would call this non-personal... (didn't do any filtering though, as requested)

### Steps to reproduce the behavior

_No response_

### Debug log - mandatory

https://privatebin.net/?89b0119bccd8ef75#5ACi5RiPEJdYVHqbmeXmD5LncggzwZChHbiwiLNKJDgi

### Possible fix

_No response_

### Additional context

This in on Amazon Fire TV Stick 4k max and the profile works completely find on the build-in app and also in browser on other devices.

### Screenshots

_No response_

Changed files:
resources/lib/services/nfsession/msl/msl_utils.py, resources/lib/services/nfsession/msl/msl_request_builder.py, resources/lib/services/nfsession/msl/msl_requests.py

diff --git a/resources/lib/services/nfsession/msl/msl_request_builder.py b/resources/lib/services/nfsession/msl/msl_request_builder.py
index 51669708..df3ec03a 100644
--- a/resources/lib/services/nfsession/msl/msl_request_builder.py
+++ b/resources/lib/services/nfsession/msl/msl_request_builder.py
@@ -11,16 +11,24 @@ import json
 import base64
 import random
 import time
+from typing import TYPE_CHECKING
 
+from resources.lib.common.exceptions import MSLError
 from resources.lib.globals import G
 import resources.lib.common as common
+from resources.lib.services.nfsession.msl.msl_utils import (MSL_AUTH_USER_ID_TOKEN, MSL_AUTH_EMAIL_PASSWORD,
+                                                            MSL_AUTH_NETFLIXID)
 from resources.lib.utils.logging import measure_exec_time_decorator
 
+if TYPE_CHECKING:  # This variable/imports are used only by the editor, so not at runtime
+    from resources.lib.services.nfsession.nfsession_ops import NFSessionOperations
+
 
 class MSLRequestBuilder:
     """Provides mechanisms to create MSL requests"""
 
-    def __init__(self):
+    def __init__(self, nfsession):
+        self.nfsession: 'NFSessionOperations' = nfsession
         self.current_message_id = None
         self.rndm = random.SystemRandom()
         # Set the Crypto handler
@@ -124,14 +132,16 @@ class MSLRequestBuilder:
 
     def _add_auth_info(self, header_data, auth_data):
         """User authentication identifies the application user associated with a message"""
-        # Warning: the user id token contains also contains the identity of the netflix profile
-        # therefore it is necessary to use the right user id token for the request
-        if auth_data.get('user_id_token'):
+        if auth_data['auth_scheme'] == MSL_AUTH_USER_ID_TOKEN:
+            # Authentication scheme with: User ID token
+            # Make requests by using by default main netflix profile, since the user id token contains also the identity
+            # of the netflix profile, to send data to the right profile (e.g. for continue watching) must be used
+            # SWITCH_PROFILE scheme to switching MSL profile.
+            # 25/09/2022: SWITCH_PROFILE auth scheme has been disabled on netflix website backend and not works anymore.
             if auth_data['use_switch_profile']:
-                # The SWITCH_PROFILE is a custom Netflix MSL user authentication scheme
-                # that is needed for switching profile on MSL side
-                # works only combined with user id token and can not be used with all endpoints
-                # after use it you will get user id token of the profile specified in the response
+                # The SWITCH_PROFILE is a custom Netflix MSL user authentication scheme, that is needed for switching
+                # profile on MSL side, works only combined with user id token and can not be used with all endpoints
+                # after use it you will get the user id token of the requested profile in the response.
                 header_data['userauthdata'] = {
                     'scheme': 'SWITCH_PROFILE',
                     'authdata': {
@@ -140,10 +150,10 @@ class MSLRequestBuilder:
                     }
                 }
             else:
-                # Authentication with user ID token containing the user identity (netflix profile)
                 header_data['useridtoken'] = auth_data['user_id_token']
-        else:
-            # Authentication with the user credentials
+        elif auth_data['auth_scheme'] == MSL_AUTH_EMAIL_PASSWORD:
+            # Authentication scheme with: Email password
+            # Make requests by using main netflix profile only (you cannot update continue watching to other profiles)
             credentials = common.get_credentials()
             header_data['userauthdata'] = {
                 'scheme': 'EMAIL_PASSWORD',
@@ -152,13 +162,15 @@ class MSLRequestBuilder:
                     'password': credentials['password']
                 }
             }
-            # Authentication with user Netflix ID cookies
-            # This not works on android,
-            #   will raise: User authentication data does not match entity identity
-            # header_data['userauthdata'] = {
-            #     'scheme': 'NETFLIXID',
-            #     'authdata': {
-            #         'netflixid': cookies['NetflixId'],
-            #         'securenetflixid': cookies['SecureNetflixId']
-            #     }
-            # }
+        elif auth_data['auth_scheme'] == MSL_AUTH_NETFLIXID:
+            # Authentication scheme with: Netflix ID cookies
+            # Make requests by using the same netflix profile used/set on nfsession (website)
+            header_data['userauthdata'] = {
+                'scheme': 'NETFLIXID',
+                'authdata': {
+                    'netflixid': self.nfsession.session.cookies['NetflixId'],
+                    'securenetflixid': self.nfsession.session.cookies['SecureNetflixId']
+                }
+            }
+        else:
+            raise MSLError(f'Authentication scheme "{auth_data["auth_scheme"]}" is not supported.')
diff --git a/resources/lib/services/nfsession/msl/msl_requests.py b/resources/lib/services/nfsession/msl/msl_requests.py
index d4e874f6..311169d8 100644
--- a/resources/lib/services/nfsession/msl/msl_requests.py
+++ b/resources/lib/services/nfsession/msl/msl_requests.py
@@ -12,7 +12,6 @@ import base64
 import json
 import time
 import zlib
-from typing import TYPE_CHECKING
 
 import httpx
 
@@ -22,13 +21,11 @@ from resources.lib.globals import G
 
 from resources.lib.services.nfsession.msl.msl_request_builder import MSLRequestBuilder
 from resources.lib.services.nfsession.msl.msl_utils import (ENDPOINTS, create_req_params, generate_logblobs_params,
-                                                            MSL_DATA_FILENAME)
+                                                            MSL_DATA_FILENAME, MSL_AUTH_NETFLIXID,
+                                                            MSL_AUTH_USER_ID_TOKEN)
 from resources.lib.utils.esn import get_esn
 from resources.lib.utils.logging import LOG, measure_exec_time_decorator
 
-if TYPE_CHECKING:  # This variable/imports are used only by the editor, so not at runtime
-    from resources.lib.services.nfsession.nfsession_ops import NFSessionOperations
-
 
 class MSLRequests(MSLRequestBuilder):
     """Provides methods to make MSL requests"""
@@ -40,9 +37,13 @@ class MSLRequests(MSLRequestBuilder):
         'Host': 'www.netflix.com'
     }
 
+    # Define the user authentication scheme to be used on the MSL HTTP requests
+    # https://github.com/Netflix/msl/wiki/User-Authentication-%28Configuration%29
+    # Values can be: MSL_AUTH_NETFLIXID, MSL_AUTH_EMAIL_PASSWORD, MSL_AUTH_USER_ID_TOKEN
+    MSL_AUTH_SCHEME = MSL_AUTH_NETFLIXID
+
     def __init__(self, msl_data, nfsession):
-        super().__init__()
-        self.nfsession: 'NFSessionOperations' = nfsession
+        super().__init__(nfsession)
         self._load_msl_data(msl_data)
         self.msl_switch_requested = False
 
@@ -123,6 +124,9 @@ class MSLRequests(MSLRequestBuilder):
         :param: force_auth_credential: force the use of authentication with credentials
         :return: auth data that will be used in MSLRequestBuilder _add_auth_info
         """
+        # TODO: This _check_user_id_token method need to be revisited,
+        #  since at today 25/09/2022 the SWITCH_PROFILE auth scheme do not works anymore
+
         # Warning: the user id token contains also contains the identity of the netflix profile
         # therefore it is necessary to use the right user id token for the request
         current_profile_guid = G.LOCAL_DB.get_active_profile_guid()
@@ -159,12 +163,15 @@ class MSLRequests(MSLRequestBuilder):
     def chunked_request(self, endpoint, request_data, esn, disable_msl_switch=True, force_auth_credential=False):
         """Do a POST request and process the chunked response"""
         self._mastertoken_checks()
-        auth_data = self._check_user_id_token(disable_msl_switch, force_auth_credential)
+
+        auth_data = {'auth_scheme': self.MSL_AUTH_SCHEME}
+        if self.MSL_AUTH_SCHEME == MSL_AUTH_USER_ID_TOKEN:
+            auth_data.update(self._check_user_id_token(disable_msl_switch, force_auth_credential))
         LOG.debug('Chunked request will be executed with auth data: {}', auth_data)
 
         chunked_response = self._process_chunked_response(
             self._post(endpoint, self.msl_request(request_data, esn, auth_data)),
-            save_uid_token_to_owner=auth_data['user_id_token'] is None)
+            save_uid_token_to_owner=auth_data.get('user_id_token') is None)
         return chunked_response['result']
 
     def _post(self, endpoint, request_data):
diff --git a/resources/lib/services/nfsession/msl/msl_utils.py b/resources/lib/services/nfsession/msl/msl_utils.py
index 52b3d13f..be6bf921 100644
--- a/resources/lib/services/nfsession/msl/msl_utils.py
+++ b/resources/lib/services/nfsession/msl/msl_utils.py
@@ -34,6 +34,10 @@ ENDPOINTS = {
     'logblobs': CHROME_PLAYAPI_URL + 'logblob/1'
 }
 
+MSL_AUTH_NETFLIXID = 'NETFLIXID'  # Netflix ID cookies user authentication
+MSL_AUTH_EMAIL_PASSWORD = 'EMAIL_PASSWORD'  # Email password user authentication
+MSL_AUTH_USER_ID_TOKEN = 'USER_ID_TOKEN'  # User ID token user authentication
+
 MSL_DATA_FILENAME = 'msl_data.json'
 
 EVENT_START = 'start'      # events/start : Video starts