|
|
# Ultralytics YOLO 🚀, AGPL-3.0 license |
|
|
|
|
|
import requests |
|
|
from hub_sdk import HUB_API_ROOT, HUB_WEB_ROOT |
|
|
|
|
|
from ultralytics.hub.utils import PREFIX, request_with_credentials |
|
|
from ultralytics.utils import LOGGER, SETTINGS, emojis, is_colab |
|
|
|
|
|
API_KEY_URL = f"{HUB_WEB_ROOT}/settings?tab=api+keys" |
|
|
|
|
|
|
|
|
class Auth: |
|
|
""" |
|
|
Manages authentication processes including API key handling, cookie-based authentication, and header generation. |
|
|
|
|
|
The class supports different methods of authentication: |
|
|
1. Directly using an API key. |
|
|
2. Authenticating using browser cookies (specifically in Google Colab). |
|
|
3. Prompting the user to enter an API key. |
|
|
|
|
|
Attributes: |
|
|
id_token (str or bool): Token used for identity verification, initialized as False. |
|
|
api_key (str or bool): API key for authentication, initialized as False. |
|
|
model_key (bool): Placeholder for model key, initialized as False. |
|
|
""" |
|
|
|
|
|
id_token = api_key = model_key = False |
|
|
|
|
|
def __init__(self, api_key="", verbose=False): |
|
|
""" |
|
|
Initialize the Auth class with an optional API key. |
|
|
|
|
|
Args: |
|
|
api_key (str, optional): May be an API key or a combination API key and model ID, i.e. key_id |
|
|
""" |
|
|
# Split the input API key in case it contains a combined key_model and keep only the API key part |
|
|
api_key = api_key.split("_")[0] |
|
|
|
|
|
# Set API key attribute as value passed or SETTINGS API key if none passed |
|
|
self.api_key = api_key or SETTINGS.get("api_key", "") |
|
|
|
|
|
# If an API key is provided |
|
|
if self.api_key: |
|
|
# If the provided API key matches the API key in the SETTINGS |
|
|
if self.api_key == SETTINGS.get("api_key"): |
|
|
# Log that the user is already logged in |
|
|
if verbose: |
|
|
LOGGER.info(f"{PREFIX}Authenticated ✅") |
|
|
return |
|
|
else: |
|
|
# Attempt to authenticate with the provided API key |
|
|
success = self.authenticate() |
|
|
# If the API key is not provided and the environment is a Google Colab notebook |
|
|
elif is_colab(): |
|
|
# Attempt to authenticate using browser cookies |
|
|
success = self.auth_with_cookies() |
|
|
else: |
|
|
# Request an API key |
|
|
success = self.request_api_key() |
|
|
|
|
|
# Update SETTINGS with the new API key after successful authentication |
|
|
if success: |
|
|
SETTINGS.update({"api_key": self.api_key}) |
|
|
# Log that the new login was successful |
|
|
if verbose: |
|
|
LOGGER.info(f"{PREFIX}New authentication successful ✅") |
|
|
elif verbose: |
|
|
LOGGER.info(f"{PREFIX}Retrieve API key from {API_KEY_URL}") |
|
|
|
|
|
def request_api_key(self, max_attempts=3): |
|
|
""" |
|
|
Prompt the user to input their API key. |
|
|
|
|
|
Returns the model ID. |
|
|
""" |
|
|
import getpass |
|
|
|
|
|
for attempts in range(max_attempts): |
|
|
LOGGER.info(f"{PREFIX}Login. Attempt {attempts + 1} of {max_attempts}") |
|
|
input_key = getpass.getpass(f"Enter API key from {API_KEY_URL} ") |
|
|
self.api_key = input_key.split("_")[0] # remove model id if present |
|
|
if self.authenticate(): |
|
|
return True |
|
|
raise ConnectionError(emojis(f"{PREFIX}Failed to authenticate ❌")) |
|
|
|
|
|
def authenticate(self) -> bool: |
|
|
""" |
|
|
Attempt to authenticate with the server using either id_token or API key. |
|
|
|
|
|
Returns: |
|
|
bool: True if authentication is successful, False otherwise. |
|
|
""" |
|
|
try: |
|
|
if header := self.get_auth_header(): |
|
|
r = requests.post(f"{HUB_API_ROOT}/v1/auth", headers=header) |
|
|
if not r.json().get("success", False): |
|
|
raise ConnectionError("Unable to authenticate.") |
|
|
return True |
|
|
raise ConnectionError("User has not authenticated locally.") |
|
|
except ConnectionError: |
|
|
self.id_token = self.api_key = False # reset invalid |
|
|
LOGGER.warning(f"{PREFIX}Invalid API key ⚠️") |
|
|
return False |
|
|
|
|
|
def auth_with_cookies(self) -> bool: |
|
|
""" |
|
|
Attempt to fetch authentication via cookies and set id_token. User must be logged in to HUB and running in a |
|
|
supported browser. |
|
|
|
|
|
Returns: |
|
|
bool: True if authentication is successful, False otherwise. |
|
|
""" |
|
|
if not is_colab(): |
|
|
return False # Currently only works with Colab |
|
|
try: |
|
|
authn = request_with_credentials(f"{HUB_API_ROOT}/v1/auth/auto") |
|
|
if authn.get("success", False): |
|
|
self.id_token = authn.get("data", {}).get("idToken", None) |
|
|
self.authenticate() |
|
|
return True |
|
|
raise ConnectionError("Unable to fetch browser authentication details.") |
|
|
except ConnectionError: |
|
|
self.id_token = False # reset invalid |
|
|
return False |
|
|
|
|
|
def get_auth_header(self): |
|
|
""" |
|
|
Get the authentication header for making API requests. |
|
|
|
|
|
Returns: |
|
|
(dict): The authentication header if id_token or API key is set, None otherwise. |
|
|
""" |
|
|
if self.id_token: |
|
|
return {"authorization": f"Bearer {self.id_token}"} |
|
|
elif self.api_key: |
|
|
return {"x-api-key": self.api_key} |
|
|
# else returns None
|
|
|
|