@@ -288,6 +288,8 @@ def api_request(
288288 if response ["Code" ] not in [1000 , 1001 ]:
289289 if response ["Code" ] == 9001 :
290290 self .__captcha_token = response ["Details" ]["HumanVerificationToken" ]
291+ elif response ["Code" ] == 12087 :
292+ del self .human_verification_token
291293
292294 raise ProtonAPIError (response )
293295 except TypeError as e :
@@ -363,7 +365,7 @@ def verify_modulus(self, armored_modulus):
363365
364366 return base64 .b64decode (verified .data .strip ())
365367
366- def authenticate (self , username , password , human_verification = None ):
368+ def authenticate (self , username , password ):
367369 """Authenticate user against API.
368370
369371 Args:
@@ -382,19 +384,7 @@ def authenticate(self, username, password, human_verification=None):
382384 if self .__clientsecret :
383385 payload ["ClientSecret" ] = self .__clientsecret
384386
385- additional_headers = {}
386-
387- if human_verification :
388- human_verification_header = {
389- "X-PM-Human-Verification-Token-Type" : human_verification [0 ],
390- "X-PM-Human-Verification-Token" : human_verification [1 ]
391- }
392- additional_headers .update (human_verification_header )
393-
394- info_response = self .api_request (
395- "/auth/info" , payload ,
396- additional_headers = additional_headers
397- )
387+ info_response = self .api_request ("/auth/info" , payload )
398388
399389 modulus = self .verify_modulus (info_response ["Modulus" ])
400390 server_challenge = base64 .b64decode (info_response ["ServerEphemeral" ])
@@ -419,6 +409,7 @@ def authenticate(self, username, password, human_verification=None):
419409 }
420410 if self .__clientsecret :
421411 payload ["ClientSecret" ] = self .__clientsecret
412+
422413 auth_response = self .api_request ("/auth" , payload )
423414
424415 if "ServerProof" not in auth_response :
@@ -663,6 +654,39 @@ def force_skip_alternative_routing(self, newvalue):
663654 """
664655 self .__force_skip_alternative_routing = bool (newvalue )
665656
657+ @property
658+ def human_verification_token (self ):
659+ return (
660+ self .s .headers .get ("X-PM-Human-Verification-Token-Type" , None ),
661+ self .s .headers .get ("X-PM-Human-Verification-Token" , None )
662+ )
663+
664+ @human_verification_token .setter
665+ def human_verification_token (self , newtuplevalue ):
666+ """Set human verification token:
667+
668+ Args:
669+ newtuplevalue (tuple): (token_type, token_value)
670+ """
671+ self .s .headers ["X-PM-Human-Verification-Token-Type" ] = newtuplevalue [0 ]
672+ self .s .headers ["X-PM-Human-Verification-Token" ] = newtuplevalue [1 ]
673+
674+ @human_verification_token .deleter
675+ def human_verification_token (self ):
676+ # Safest to use .pop() as it will onyl attempt to remove the key by name
677+ # while del can also remove the whole dict (in case of code/programming error)
678+ # Thus to prevent this, pop() is used.
679+
680+ try :
681+ self .s .headers .pop ("X-PM-Human-Verification-Token-Type" )
682+ except (KeyError , IndexError ):
683+ pass
684+
685+ try :
686+ self .s .headers .pop ("X-PM-Human-Verification-Token" )
687+ except (KeyError , IndexError ):
688+ pass
689+
666690 @property
667691 def UID (self ):
668692 return self ._session_data .get ("UID" , None )
0 commit comments