Files
SolianForPython/core/LoginServices.py
2025-09-11 12:46:52 +00:00

247 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import json
import uuid
import platform
import hashlib
class AuthClient:
def __init__(self, base_url):
self.base_url = base_url
self.stage = 'find-account'
self.error = None
self.account_identifier = ''
self.device_id = ''
self.challenge = None
self.factors = []
self.selected_factor_id = None
self.password = ''
self.user_store = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' # 您可以自定义此User-Agent字符串
def generate_device_id(self):
"""生成设备指纹
:return: 设备指纹ID字符串
"""
platform_info = platform.system() + platform.release() + platform.machine() # 修正了这里的拼接错误
unique_id = uuid.getnode()
device_info = f"{platform_info}-{unique_id}"
# 使用SHA-256哈希生成设备ID
self.device_id = hashlib.sha256(device_info.encode()).hexdigest()
return self.device_id
def check_account_validity(self, account_identifier):
"""检查账户有效性
:param account_identifier: 用户的账户标识符(邮箱或用户名)
:return: 挑战信息(字典)
"""
self.account_identifier = account_identifier
self.error = None
if not self.account_identifier:
self.error = 'Please enter your email or username.'
return None
try:
response = requests.post(
f"{self.base_url}/auth/challenge",
headers={'Content-Type': 'application/json', 'User-Agent': self.user_store,'device_name': "A Python login app.By NRFF & Deepseek"}, # 添加自定义User-Agent
data=json.dumps({
'platform': 1,
'account': self.account_identifier,
'device_id': self.generate_device_id(),
})
)
if not response.ok:
raise Exception(response.text or 'Account not found.')
self.challenge = response.json()
self.stage = 'select-factor'
return self.challenge
except Exception as e:
self.error = str(e)
return None
def get_authentication_factors(self):
"""获取可用的认证因素
:return: 可用的认证因素列表(列表)
"""
self.error = None
if not self.challenge:
self.error = 'No challenge information available.'
return None
try:
response = requests.get(
f"{self.base_url}/auth/challenge/{self.challenge['id']}/factors",
headers={'User-Agent': self.user_store,'device_name': "A Python login app.By NRFF & Deepseek"} # 添加自定义User-Agent
)
if not response.ok:
raise Exception('Could not fetch authentication factors.')
available_factors = response.json()
self.factors = [factor for factor in available_factors if factor['id'] not in self.challenge.get('blacklist_factors', [])]
if len(self.factors) == 0:
self.error = 'No available authentication factors.'
return None
else:
self.stage = 'select-factor'
return self.factors
except Exception as e:
self.error = str(e)
return None
def request_verification_code(self, selected_factor_id):
"""请求验证码
:param selected_factor_id: 用户选择的认证因素ID字符串类型
:return: None
"""
self.selected_factor_id = selected_factor_id
self.error = None
if not self.selected_factor_id:
self.error = 'No authentication factor selected.'
return None
selected_factor = self.get_selected_factor()
if not selected_factor:
self.error = 'Selected factor not found.'
return None
hint = {'contact': selected_factor['contact']}
try:
response = requests.post(
f"{self.base_url}/auth/challenge/{self.challenge['id']}/factors/{self.selected_factor_id}",
headers={'Content-Type': 'application/json', 'User-Agent': self.user_store, 'device_name': "A Python login app.By NRFF & Deepseek"}, # 添加自定义User-Agent
data=json.dumps(hint)
)
if not response.ok:
raise Exception(response.text or 'Failed to send code.')
self.stage = 'enter-code'
except Exception as e:
self.error = str(e)
self.stage = 'select-factor'
return None
def get_selected_factor(self):
"""获取选择的认证因素
:return: 选择的认证因素(字典)或 None
"""
if not self.selected_factor_id:
return None
return next((factor for factor in self.factors if factor['id'] == self.selected_factor_id), None)
def verify_factor(self, selected_factor_id, password):
"""验证所选的认证因素
:param selected_factor_id: 用户选择的认证因素ID字符串类型
:param password: 用户输入的密码或验证码(字符串类型)
:return: None
"""
self.selected_factor_id = selected_factor_id
self.password = password
self.error = None
if not self.selected_factor_id or not self.password:
self.error = 'Please enter your password/code.'
return None
try:
response = requests.patch(
f"{self.base_url}/auth/challenge/{self.challenge['id']}",
headers={'Content-Type': 'application/json', 'User-Agent': self.user_store,'device_name': "A Python login app.By NRFF & Deepseek"}, # 添加自定义User-Agent
data=json.dumps({
'factor_id': self.selected_factor_id,
'password': self.password,
})
)
if not response.ok:
raise Exception(response.text or 'Verification failed.')
self.challenge = response.json()
self.password = ''
if self.challenge['step_remain'] == 0:
self.stage = 'token-exchange'
else:
self.stage = 'select-factor'
except Exception as e:
self.error = str(e)
self.stage = 'select-factor'
def exchange_token(self):
"""交换令牌以完成登录
:return: None
"""
self.error = None
if not self.challenge:
self.error = 'No challenge information available.'
return None
try:
response = requests.post(
f"{self.base_url}/auth/token",
headers={'Content-Type': 'application/json', 'User-Agent': self.user_store,'device_name': "A Python login app.By NRFF & Deepseek"}, # 添加自定义User-Agent
data=json.dumps({
'grant_type': 'authorization_code',
'code': self.challenge['id'],
})
)
if not response.ok:
raise Exception(response.text or 'Token exchange failed.')
token_info = response.json()
token = token_info['token']
#self.user_store.fetchUser()
redirect_uri = 'redirect_uri_from_query' # 这里需要根据实际情况获取
if redirect_uri:
print(f"Redirecting to: {redirect_uri}")
else:
print("Navigating to home page.")
return token
except Exception as e:
self.error = str(e)
self.stage = 'select-factor'
def get_factor_name(self, factor_type):
"""根据认证因素类型返回相应的名称
:param factor_type: 认证因素类型(整数)
:return: 认证因素名称(字符串)
"""
factor_names = {
0: 'Password',
1: 'Email',
2: 'Authenticator App',
}
return factor_names.get(factor_type, 'Unknown Factor')
# 示例调用
if __name__ == "__main__":
auth_client = AuthClient(base_url='https://api.solian.app/id')
account_identifier = ''#你的账号
selected_factor_id = "enter-code" # 用户需要选择一个因素
password = ''#你的密码
# 第一步:检查账户有效性
challenge = auth_client.check_account_validity(account_identifier)
if auth_client.error:
print(f"Error in check account validity: {auth_client.error}")
elif challenge:
print(f"Challenge information: {challenge}")
# 第二步:获取可用的认证因素
factors = auth_client.get_authentication_factors()
if auth_client.error:
print(f"Error in get authentication factors: {auth_client.error}")
elif factors:
print(f"Available factors: {factors}")
# 假设用户选择第一个因素
selected_factor_id = factors[0]['id']
print(f"Selected factor ID: {selected_factor_id}")
# 第三步:请求验证码(如果需要)
if factors[0]['type'] == 1:
auth_client.request_verification_code(selected_factor_id)
if auth_client.error:
print(f"Error in request verification code: {auth_client.error}")
elif auth_client.stage == 'enter-code':
print("Verification code requested. Please enter the code.")
# 第四步:验证所选的认证因素
auth_client.verify_factor(selected_factor_id, password)
if auth_client.error:
print(f"Error in verify factor: {auth_client.error}")
elif auth_client.stage == 'token-exchange':
print("Factor verified. Ready to exchange token.")
# 第五步:交换令牌以完成登录
if auth_client.stage == 'token-exchange':
token = auth_client.exchange_token()
if auth_client.error:
print(f"Error in exchange token: {auth_client.error}")
else:
print("Login successful!")
print(f"Token: {token}")
else:
print("Login failed.")
else:
print("No available authentication factors.")