auth.py 4.6 KB

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