├── README.md └── auth.py /README.md: -------------------------------------------------------------------------------- 1 | # Bambu Labs Auth MFA and Verification authentication example 2 | 3 | This test harness demonstrates how to log into the Bambu Labs API using multi-factor authentication (MFA) or email verification codes when required and displays basic print information. 4 | 5 | ## Features 6 | 7 | - **User Authentication**: Prompts for username and password for login. 8 | - **Multi-factor Authentication Support**: Handles both email verification codes and MFA (Two-Factor Authentication). 9 | - **Task Retrieval**: Once logged in, retrieves details about your tasks including model title, weight, cost time, device name, and more. 10 | 11 | ## Prerequisites 12 | 13 | - **Python 3.x** 14 | - **Requests Library**: Install via `pip install requests` 15 | 16 | ## Usage 17 | 18 | 1. **Clone this repository** or copy the script. 19 | 20 | 2. **Run the script**: 21 | ```bash 22 | python auth.py 23 | -------------------------------------------------------------------------------- /auth.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import certifi 4 | import cloudscraper 5 | from requests.exceptions import HTTPError 6 | 7 | # Prompt the user for their Bambu Lab username and password 8 | bambuUsername = input("Enter your Bambu Lab username: ") 9 | bambuPassword = input("Enter your Bambu Lab password: ") 10 | 11 | # Slicer headers 12 | headers = { 13 | 'User-Agent': 'bambu_network_agent/01.09.05.01', 14 | 'X-BBL-Client-Name': 'OrcaSlicer', 15 | 'X-BBL-Client-Type': 'slicer', 16 | 'X-BBL-Client-Version': '01.09.05.51', 17 | 'X-BBL-Language': 'en-US', 18 | 'X-BBL-OS-Type': 'linux', 19 | 'X-BBL-OS-Version': '6.2.0', 20 | 'X-BBL-Agent-Version': '01.09.05.01', 21 | 'X-BBL-Executable-info': '{}', 22 | 'X-BBL-Agent-OS-Type': 'linux', 23 | 'accept': 'application/json', 24 | 'Content-Type': 'application/json' 25 | } 26 | 27 | scraper = cloudscraper.create_scraper(browser={'custom': 'chrome'}) 28 | 29 | # Perform the login request with custom headers 30 | def login(): 31 | auth_payload = { 32 | "account": bambuUsername, 33 | "password": bambuPassword, 34 | "apiError": "" 35 | } 36 | 37 | try: 38 | auth_response = scraper.post( 39 | "https://api.bambulab.com/v1/user-service/user/login", 40 | headers=headers, 41 | json=auth_payload, 42 | verify=certifi.where() 43 | ) 44 | auth_response.raise_for_status() 45 | if auth_response.text.strip() == "": 46 | raise ValueError("Empty response from server, possible Cloudflare block.") 47 | auth_json = auth_response.json() 48 | 49 | # If login is successful 50 | if auth_json.get("success"): 51 | return auth_json.get("accessToken") 52 | 53 | # Handle additional authentication scenarios 54 | login_type = auth_json.get("loginType") 55 | if login_type == "verifyCode": 56 | return handle_verification_code() 57 | elif login_type == "tfa": 58 | return handle_mfa(auth_json.get("tfaKey")) 59 | else: 60 | raise ValueError(f"Unknown login type: {login_type}") 61 | 62 | except HTTPError as http_err: 63 | print(f"HTTP error occurred: {http_err}") 64 | except json.JSONDecodeError as json_err: 65 | print(f"JSON decode error: {json_err}. Response content: {auth_response.text}") 66 | except Exception as err: 67 | print(f"Other error occurred: {err}") 68 | return None 69 | 70 | # Handle verification 71 | def handle_verification_code(): 72 | send_code_payload = { 73 | "email": bambuUsername, 74 | "type": "codeLogin" 75 | } 76 | 77 | try: 78 | send_code_response = scraper.post( 79 | "https://api.bambulab.com/v1/user-service/user/sendemail/code", 80 | headers=headers, 81 | json=send_code_payload, 82 | verify=certifi.where() 83 | ) 84 | send_code_response.raise_for_status() 85 | print("Verification code sent successfully. Please check your email.") 86 | verify_code = input("Enter your access code: ") 87 | 88 | verify_payload = { 89 | "account": bambuUsername, 90 | "code": verify_code 91 | } 92 | verify_response = scraper.post( 93 | "https://api.bambulab.com/v1/user-service/user/login", 94 | headers=headers, 95 | json=verify_payload, 96 | verify=certifi.where() 97 | ) 98 | verify_response.raise_for_status() 99 | if verify_response.text.strip() == "": 100 | raise ValueError("Empty response from server during verification, possible Cloudflare block.") 101 | return verify_response.json().get("accessToken") 102 | 103 | except HTTPError as http_err: 104 | print(f"HTTP error occurred during verification: {http_err}") 105 | except json.JSONDecodeError as json_err: 106 | print(f"JSON decode error during verification: {json_err}. Response content: {verify_response.text}") 107 | except Exception as err: 108 | print(f"Other error occurred during verification: {err}") 109 | return None 110 | 111 | # Handle MFA scenario 112 | def handle_mfa(tfa_key): 113 | tfa_code = input("Enter your MFA access code: ") 114 | verify_payload = { 115 | "tfaKey": tfa_key, 116 | "tfaCode": tfa_code 117 | } 118 | 119 | try: 120 | tfa_response = scraper.post( 121 | "https://bambulab.com/api/sign-in/tfa", 122 | headers=headers, 123 | json=verify_payload, 124 | verify=certifi.where() 125 | ) 126 | tfa_response.raise_for_status() 127 | if tfa_response.text.strip() == "": 128 | raise ValueError("Empty response from server during MFA, possible Cloudflare block.") 129 | cookies = tfa_response.cookies.get_dict() 130 | return cookies.get("token") 131 | 132 | except HTTPError as http_err: 133 | print(f"HTTP error occurred during MFA: {http_err}") 134 | except json.JSONDecodeError as json_err: 135 | print(f"JSON decode error during MFA: {json_err}. Response content: {tfa_response.text}") 136 | except Exception as err: 137 | print(f"Other error occurred during MFA: {err}") 138 | return None 139 | 140 | # Execute the login 141 | access_token = login() 142 | 143 | if not access_token: 144 | print("Unable to authenticate or verify. Exiting...") 145 | exit(1) 146 | 147 | # Perform the API request to fetch information with custom headers 148 | headers["Authorization"] = f"Bearer {access_token}" 149 | try: 150 | api_response = scraper.get( 151 | "https://api.bambulab.com/v1/user-service/my/tasks", 152 | headers=headers, 153 | verify=certifi.where() 154 | ) 155 | api_response.raise_for_status() 156 | if api_response.text.strip() == "": 157 | raise ValueError("Empty response from server during API request, possible Cloudflare block.") 158 | api_json = api_response.json() 159 | 160 | # Extract and display relevant information 161 | if api_json: 162 | hits = api_json.get("hits", [{}])[0] 163 | image_url = hits.get("cover") 164 | model_title = hits.get("title") 165 | model_weight = hits.get("weight") 166 | model_cost_time = hits.get("costTime") 167 | total_prints = api_json.get("total") 168 | device_name = hits.get("deviceName") 169 | device_model = hits.get("deviceModel") 170 | bed_type = hits.get("bedType") 171 | 172 | # Output the results 173 | print("Image URL:", image_url) 174 | print("Model Title:", model_title) 175 | print("Model Weight:", model_weight) 176 | print("Model Cost Time:", model_cost_time) 177 | print("Total Prints:", total_prints) 178 | print("Device Name:", device_name) 179 | print("Device Model:", device_model) 180 | print("Bed Type:", bed_type) 181 | else: 182 | print("Failed to parse API response.") 183 | 184 | except HTTPError as http_err: 185 | print(f"HTTP error occurred during API request: {http_err}") 186 | except json.JSONDecodeError as json_err: 187 | print(f"JSON decode error during API request: {json_err}. Response content: {api_response.text}") 188 | except Exception as err: 189 | print(f"Other error occurred during API request: {err}") 190 | exit(1) 191 | --------------------------------------------------------------------------------