"""Client API for authentication in Nexus."""importgetpassimporttimeimportwebbrowserfromhttpimportHTTPStatusimporthttpxfromcoloramaimportForefrompydanticimportEmailStrfromrich.consoleimportConsolefromrich.panelimportPanelimportqnexus.exceptionsasqnx_excfromqnexus.clientimportget_nexus_clientfromqnexus.client.utilsimportconsolidate_error,remove_token,write_tokenfromqnexus.configimportCONFIGconsole=Console()def_get_auth_client()->httpx.Client:"""Getter function for the Nexus auth client."""returnhttpx.Client(base_url=f"{CONFIG.url}/auth",timeout=None,verify=CONFIG.httpx_verify,)
[docs]deflogin()->None:""" Log in to Quantinuum Nexus using the web browser. (if web browser can't be launched, displays the link) """res=_get_auth_client().post("/device/device_authorization",headers={"Content-Type":"application/x-www-form-urlencoded"},data={"client_id":"scales","scope":"myqos"},)user_code=res.json()["user_code"]device_code=res.json()["device_code"]verification_uri_complete=res.json()["verification_uri_complete"]expires_in=res.json()["expires_in"]poll_interval=res.json()["interval"]webbrowser.open(verification_uri_complete,new=2)token_request_body={"grant_type":"urn:ietf:params:oauth:grant-type:device_code","device_code":device_code,"client_id":"scales",}print("🌐 Browser log in initiated.")console.print(Panel(f""" Confirm that the browser shows the following code and click 'allow device':{user_code} """,width=90,))print("Browser didn't open automatically? Use this link: "f"{Fore.BLUE+verification_uri_complete}")polling_for_seconds=0whilepolling_for_seconds<expires_in:time.sleep(poll_interval)polling_for_seconds+=poll_intervalresp=_get_auth_client().post("/device/token",headers={"Content-Type":"application/x-www-form-urlencoded"},data=token_request_body,)if(resp.status_code==HTTPStatus.BAD_REQUESTandresp.json().get("error")=="AUTHORIZATION_PENDING"):continueifresp.status_code==HTTPStatus.TOO_MANY_REQUESTS:continueifresp.status_code==HTTPStatus.OK:resp_json=resp.json()write_token("refresh_token",resp_json["refresh_token"])write_token("access_token",resp_json["access_token"],)get_nexus_client(reload=True)# spinner.stop()print(f"✅ Successfully logged in as {resp_json['email']} using the browser.")return# Fail for all other statusesconsolidate_error(res=resp,description="Browser Login")# spinner.stop()returnraiseqnx_exc.AuthenticationError("Browser login Failed, code has expired.")
[docs]deflogin_with_credentials()->None:"""Log in to Nexus using a username and password."""user_name=input("Enter your Nexus email: ")pwd=getpass.getpass(prompt="Enter your Nexus password: ")_request_tokens(user=user_name,pwd=pwd)print(f"✅ Successfully logged in as {user_name}.")
deflogin_no_interaction(user:EmailStr,pwd:str)->None:"""Log in to Nexus using a username and password. Please be careful with storing credentials in plain text or source code. """_request_tokens(user=user,pwd=pwd)print(f"✅ Successfully logged in as {user}.")
[docs]deflogout()->None:"""Clear tokens from file system and the client."""remove_token("refresh_token")remove_token("access_token")get_nexus_client(reload=True)print("Successfully logged out.")
def_request_tokens(user:EmailStr,pwd:str)->None:"""Method to send login request to Nexus auth api and save tokens."""body={"email":user,"password":pwd}try:resp=_get_auth_client().post("/login",json=body,)mfa_redirect_uri=resp.json().get("redirect_uri","")ifmfa_redirect_uri.startswith("/auth/mfa_challenge/"):mfa_code=input("Enter your MFA verification code: ")body["code"]=mfa_codebody.pop("password")resp=_get_auth_client().post("/mfa_challenge",json=body,)terms_redirect_uri=resp.json().get("redirect_uri","")ifterms_redirect_uri.startswith("/auth/terms_challenge"):message="Terms and conditions not accepted. To continue, "message+="please accept our new terms and conditions by signing in "message+="to the Nexus website https://nexus.quantinuum.com/auth/login."# logger.error(message)raiseqnx_exc.AuthenticationError(message)_response_check(resp,"Login")myqos_oat=resp.cookies.get("myqos_oat",None)myqos_id=resp.cookies.get("myqos_id",None)ifnotmyqos_oatornotmyqos_id:raiseqnx_exc.AuthenticationError("Authorization cookies missing from response.")write_token("refresh_token",myqos_oat)write_token("access_token",myqos_id)get_nexus_client(reload=True)finally:deluserdelpwddelbodydef_response_check(res:httpx.Response,description:str)->None:"""Consolidate as much error-checking of response"""# check if token has expired or is generally unauthorizedresp_json=res.json()ifres.status_code==HTTPStatus.UNAUTHORIZED:raiseqnx_exc.AuthenticationError((f"Authorization failure attempting: {description}."f"\n\nServer Response: {resp_json}"))ifres.status_code!=HTTPStatus.OK:raiseqnx_exc.AuthenticationError(f"HTTP error attempting: {description}.\n\nServer Response: {resp_json}")