1616_TTL = 30 * 24 * 60
1717
1818
19- def login (plugin , settings ):
19+ def login (plugin ):
2020 """
2121 Unified login entry point.
2222 User chooses between:
23- - Password login
2423 - Smart TV Device login
24+ - Password login
2525 """
2626 erase_password_in_old_config (plugin )
2727
@@ -33,70 +33,97 @@ def login(plugin, settings):
3333 ]
3434 )
3535 if choice == 0 :
36- return login_with_device_flow (plugin , settings )
36+ return login_with_device_flow (plugin )
3737 if choice == 1 :
38- return login_with_password (plugin , settings )
38+ return login_with_password (plugin )
3939 return False
4040
4141
4242# ---------------------------------------------------------------------------
4343# PASSWORD LOGIN
4444# ---------------------------------------------------------------------------
4545
46- def login_with_password (plugin , settings ):
46+ def login_with_password (plugin ):
4747 """
48- Get user password from UI, create a token with Arte API, persist it in storage
48+ Get user email and password from UI, create a token with Arte API, persist it in storage
4949 and update settings state to show user is logged in.
5050 """
51- # ensure user to log in is identified
52- usr = settings . username
53- if not usr :
54- msg = f" { plugin . addon . getLocalizedString ( 30020 ) } : { plugin . addon . getLocalizedString ( 30021 ) } "
55- plugin .notify (msg = msg , image = 'error' )
51+ # get email from user
52+ email = get_user_email ( plugin )
53+ if not email :
54+ xbmc . log ( 'Authentication aborted by user - no email entered' , level = xbmc . LOGWARNING )
55+ plugin .notify (msg = plugin . addon . getLocalizedString ( 30020 ) , image = 'error' )
5656 return False
5757
58- # ensure no user is not logged in
59- loggedin_usr = settings .user_email
60- tkn_data = get_cached_token (plugin , usr , True )
61- if len (loggedin_usr ) > 0 and tkn_data :
62- xbmc .log (
63- f"\" { loggedin_usr } \" already authenticated : { tkn_data ['access_token' ]} " ,
64- level = xbmc .LOGINFO )
65- # notify user that current token might be replaced
66- accept_to_replace = xbmcgui .Dialog ().yesno (
67- plugin .addon .getLocalizedString (30015 ),
68- plugin .addon .getLocalizedString (30016 ).format (new_user = usr , old_user = loggedin_usr ),
69- autoclose = 10000
70- )
71- # user didn't accept replacement, so leave
72- if not accept_to_replace :
73- xbmc .log ('Authentication aborted by user - keep initial token' , level = xbmc .LOGWARNING )
74- return False
58+ # if user already logged in with this email and token is valid, confirm s/he wants to override
59+ if not want_to_continue_or_override_auth (plugin , email ):
60+ return False
7561
7662 # get password
7763 pwd = get_user_password (plugin )
7864 if not pwd :
7965 xbmc .log ('Authentication aborted by user - no password entered' , level = xbmc .LOGWARNING )
80- msg = f"{ plugin .addon .getLocalizedString (30020 )} : { plugin .addon .getLocalizedString (30022 )} "
81- plugin .notify (msg = msg , image = 'error' )
66+ plugin .notify (msg = plugin .addon .getLocalizedString (30020 ), image = 'error' )
8267 return False
8368
8469 # get token for user and password
85- tokens = api .authenticate_in_arte (plugin , usr , pwd )
70+ tokens = api .authenticate_in_arte (plugin , email , pwd )
8671 if tokens is None :
8772 xbmc .log ('Authentication failed in arte' , level = xbmc .LOGERROR )
8873 msg = f"{ plugin .addon .getLocalizedString (30020 )} "
8974 plugin .notify (msg = msg , image = 'error' )
9075 return False
9176
9277 # store token
93- set_cached_token (plugin , usr , tokens )
94- update_settings_state (plugin , usr )
95- msg = plugin .addon .getLocalizedString (30017 ).format (user = usr )
78+ set_cached_token (plugin , email , tokens )
79+ set_auth_user_settings (plugin , email )
80+ msg = plugin .addon .getLocalizedString (30017 ).format (user = email )
9681 plugin .notify (msg = msg , image = 'info' )
9782 return True
9883
9984
85+ def get_user_email (plugin ):
86+ """
87+ Display a keyboard to get user email.
88+ Return None if user didn't enter an email or close the UI.
89+ """
90+ user_email = ''
91+ keyboard = xbmc .Keyboard (user_email , plugin .addon .getLocalizedString (30019 ), False )
92+ keyboard .doModal ()
93+ if keyboard .isConfirmed () is False :
94+ return None
95+ user_email = keyboard .getText ()
96+ if len (user_email ) == 0 :
97+ return None
98+ return user_email
99+
100+
101+ def want_to_continue_or_override_auth (plugin , new_user ):
102+ """
103+ If user already authenticated (with a valid token and) with same email,
104+ confirm that s/he wants to override her/his token.
105+ Return False if user confirms replacement, True otherwise.
106+ Return true, if token is invalid or emails are different.
107+ """
108+ # assuming that new user's email and old user's email are the same,
109+ # since token can be retrieved/is indexed by email.
110+ current_tkn = get_cached_token (plugin , new_user , True )
111+ if current_tkn :
112+ xbmc .log (f"\" { new_user } \" already authenticated : { current_tkn ['access_token' ]} " )
113+ # notify user that current token might be replaced
114+ accept_to_replace = xbmcgui .Dialog ().yesno (
115+ plugin .addon .getLocalizedString (30015 ),
116+ # old_user=new_user highlight the unsual situation
117+ plugin .addon .getLocalizedString (30016 ).format (new_user = new_user , old_user = new_user ),
118+ autoclose = 10000
119+ )
120+ # user didn't accept replacement, so leave
121+ if not accept_to_replace :
122+ xbmc .log ('Authentication aborted by user - keep initial token' , level = xbmc .LOGWARNING )
123+ return False
124+ return True
125+
126+
100127def get_user_password (plugin ):
101128 """
102129 Display a keyboard to get user password.
@@ -117,13 +144,14 @@ def get_user_password(plugin):
117144# SMART TV DEVICE FLOW LOGIN
118145# ---------------------------------------------------------------------------
119146
120- def login_with_device_flow (plugin , settings ):
147+ def login_with_device_flow (plugin ):
121148 """
122149 Smart TV Device Authorization Flow:
123150 1. Request device_code + user_code
124151 2. Show instructions and code to authenticate on another device
125152 3. Poll token endpoint until success
126- 4. Store token
153+ 4. Retrieve email from personal data endpoint
154+ 5. Store token
127155 """
128156 # Step 1 - request device_code
129157 device_info = api .device_authorization_request ()
@@ -146,11 +174,23 @@ def login_with_device_flow(plugin, settings):
146174 plugin .notify (plugin .addon .getLocalizedString (30020 ), image = 'error' )
147175 return False
148176
149- # Step 4 - store token
150- usr = settings .username
151- set_cached_token (plugin , usr , tokens )
152- update_settings_state (plugin , usr )
153- msg = plugin .addon .getLocalizedString (30017 ).format (user = usr )
177+ # Step 4 - retrieve email from personal data endpoint
178+ user_data = api .get_personal_data (tokens )
179+ if user_data is None :
180+ xbmc .log ('Failed to retrieve personal data from Arte API' , level = xbmc .LOGERROR )
181+ plugin .notify (plugin .addon .getLocalizedString (30020 ), image = 'error' )
182+ return False
183+
184+ email = user_data .get ('email' )
185+ if not email :
186+ xbmc .log ('Email not found in personal data from Arte API' , level = xbmc .LOGERROR )
187+ plugin .notify (plugin .addon .getLocalizedString (30020 ), image = 'error' )
188+ return False
189+
190+ # Step 5 - store token
191+ set_cached_token (plugin , email , tokens )
192+ set_auth_user_settings (plugin , email )
193+ msg = plugin .addon .getLocalizedString (30017 ).format (user = email )
154194 plugin .notify (msg = msg , image = 'info' )
155195 return True
156196
@@ -209,7 +249,7 @@ def logout(plugin, settings):
209249 # clear token locally
210250 set_cached_token (plugin , settings .username , '' )
211251 clear_cached_tokens (plugin )
212- update_settings_state (plugin , '' )
252+ set_auth_user_settings (plugin , '' )
213253 plugin .notify (msg = plugin .addon .getLocalizedString (30018 ), image = 'info' )
214254 return True
215255
@@ -218,7 +258,7 @@ def logout(plugin, settings):
218258# ---------------------------------------------------------------------------
219259
220260
221- def update_settings_state (plugin , email ):
261+ def set_auth_user_settings (plugin , email ):
222262 """Update setting state to know who belong the token to"""
223263 message = plugin .addon .getLocalizedString (30017 ).format (user = email )
224264 if email is None or len (email ) <= 0 :
@@ -261,4 +301,4 @@ def erase_password_in_old_config(plugin):
261301 to authenticate user.
262302 Deprecated since creation JUL2023, v1.3.0.
263303 """
264- return plugin .set_setting ('password' , '' )
304+ return plugin .set_setting ('password' , '' ) and plugin . set_setting ( 'username' , '' )
0 commit comments