auth.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. # Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
  2. import requests
  3. from ultralytics.hub.utils import HUB_API_ROOT, HUB_WEB_ROOT, PREFIX, request_with_credentials
  4. from ultralytics.utils import IS_COLAB, LOGGER, SETTINGS, emojis
  5. API_KEY_URL = f"{HUB_WEB_ROOT}/settings?tab=api+keys"
  6. class Auth:
  7. """
  8. Manages authentication processes including API key handling, cookie-based authentication, and header generation.
  9. The class supports different methods of authentication:
  10. 1. Directly using an API key.
  11. 2. Authenticating using browser cookies (specifically in Google Colab).
  12. 3. Prompting the user to enter an API key.
  13. Attributes:
  14. id_token (str or bool): Token used for identity verification, initialized as False.
  15. api_key (str or bool): API key for authentication, initialized as False.
  16. model_key (bool): Placeholder for model key, initialized as False.
  17. """
  18. id_token = api_key = model_key = False
  19. def __init__(self, api_key="", verbose=False):
  20. """
  21. Initialize Auth class and authenticate user.
  22. Handles API key validation, Google Colab authentication, and new key requests. Updates SETTINGS upon successful
  23. authentication.
  24. Args:
  25. api_key (str): API key or combined key_id format.
  26. verbose (bool): Enable verbose logging.
  27. """
  28. # Split the input API key in case it contains a combined key_model and keep only the API key part
  29. api_key = api_key.split("_")[0]
  30. # Set API key attribute as value passed or SETTINGS API key if none passed
  31. self.api_key = api_key or SETTINGS.get("api_key", "")
  32. # If an API key is provided
  33. if self.api_key:
  34. # If the provided API key matches the API key in the SETTINGS
  35. if self.api_key == SETTINGS.get("api_key"):
  36. # Log that the user is already logged in
  37. if verbose:
  38. LOGGER.info(f"{PREFIX}Authenticated ✅")
  39. return
  40. else:
  41. # Attempt to authenticate with the provided API key
  42. success = self.authenticate()
  43. # If the API key is not provided and the environment is a Google Colab notebook
  44. elif IS_COLAB:
  45. # Attempt to authenticate using browser cookies
  46. success = self.auth_with_cookies()
  47. else:
  48. # Request an API key
  49. success = self.request_api_key()
  50. # Update SETTINGS with the new API key after successful authentication
  51. if success:
  52. SETTINGS.update({"api_key": self.api_key})
  53. # Log that the new login was successful
  54. if verbose:
  55. LOGGER.info(f"{PREFIX}New authentication successful ✅")
  56. elif verbose:
  57. LOGGER.info(f"{PREFIX}Get API key from {API_KEY_URL} and then run 'yolo login API_KEY'")
  58. def request_api_key(self, max_attempts=3):
  59. """
  60. Prompt the user to input their API key.
  61. Returns the model ID.
  62. """
  63. import getpass
  64. for attempts in range(max_attempts):
  65. LOGGER.info(f"{PREFIX}Login. Attempt {attempts + 1} of {max_attempts}")
  66. input_key = getpass.getpass(f"Enter API key from {API_KEY_URL} ")
  67. self.api_key = input_key.split("_")[0] # remove model id if present
  68. if self.authenticate():
  69. return True
  70. raise ConnectionError(emojis(f"{PREFIX}Failed to authenticate ❌"))
  71. def authenticate(self) -> bool:
  72. """
  73. Attempt to authenticate with the server using either id_token or API key.
  74. Returns:
  75. (bool): True if authentication is successful, False otherwise.
  76. """
  77. try:
  78. if header := self.get_auth_header():
  79. r = requests.post(f"{HUB_API_ROOT}/v1/auth", headers=header)
  80. if not r.json().get("success", False):
  81. raise ConnectionError("Unable to authenticate.")
  82. return True
  83. raise ConnectionError("User has not authenticated locally.")
  84. except ConnectionError:
  85. self.id_token = self.api_key = False # reset invalid
  86. LOGGER.warning(f"{PREFIX}Invalid API key ⚠️")
  87. return False
  88. def auth_with_cookies(self) -> bool:
  89. """
  90. Attempt to fetch authentication via cookies and set id_token. User must be logged in to HUB and running in a
  91. supported browser.
  92. Returns:
  93. (bool): True if authentication is successful, False otherwise.
  94. """
  95. if not IS_COLAB:
  96. return False # Currently only works with Colab
  97. try:
  98. authn = request_with_credentials(f"{HUB_API_ROOT}/v1/auth/auto")
  99. if authn.get("success", False):
  100. self.id_token = authn.get("data", {}).get("idToken", None)
  101. self.authenticate()
  102. return True
  103. raise ConnectionError("Unable to fetch browser authentication details.")
  104. except ConnectionError:
  105. self.id_token = False # reset invalid
  106. return False
  107. def get_auth_header(self):
  108. """
  109. Get the authentication header for making API requests.
  110. Returns:
  111. (dict): The authentication header if id_token or API key is set, None otherwise.
  112. """
  113. if self.id_token:
  114. return {"authorization": f"Bearer {self.id_token}"}
  115. elif self.api_key:
  116. return {"x-api-key": self.api_key}
  117. # else returns None