_auth.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. # Copyright 2023 The HuggingFace Team. All rights reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Contains a helper to get the token from machine (env variable, secret or config file)."""
  15. import configparser
  16. import logging
  17. import os
  18. import warnings
  19. from pathlib import Path
  20. from threading import Lock
  21. from .. import constants
  22. from ._runtime import is_colab_enterprise, is_google_colab
  23. _IS_GOOGLE_COLAB_CHECKED = False
  24. _GOOGLE_COLAB_SECRET_LOCK = Lock()
  25. _GOOGLE_COLAB_SECRET: str | None = None
  26. logger = logging.getLogger(__name__)
  27. def get_token() -> str | None:
  28. """
  29. Get token if user is logged in.
  30. Note: in most cases, you should use [`huggingface_hub.utils.build_hf_headers`] instead. This method is only useful
  31. if you want to retrieve the token for other purposes than sending an HTTP request.
  32. Token is retrieved in priority from the `HF_TOKEN` environment variable. Otherwise, we read the token file located
  33. in the Hugging Face home folder. Returns None if user is not logged in. To log in, use [`login`] or
  34. `hf auth login`.
  35. Returns:
  36. `str` or `None`: The token, `None` if it doesn't exist.
  37. """
  38. return _get_token_from_google_colab() or _get_token_from_environment() or _get_token_from_file()
  39. def _get_token_from_google_colab() -> str | None:
  40. """Get token from Google Colab secrets vault using `google.colab.userdata.get(...)`.
  41. Token is read from the vault only once per session and then stored in a global variable to avoid re-requesting
  42. access to the vault.
  43. """
  44. # If it's not a Google Colab or it's Colab Enterprise, fallback to environment variable or token file authentication
  45. if not is_google_colab() or is_colab_enterprise():
  46. return None
  47. # `google.colab.userdata` is not thread-safe
  48. # This can lead to a deadlock if multiple threads try to access it at the same time
  49. # (typically when using `snapshot_download`)
  50. # => use a lock
  51. # See https://github.com/huggingface/huggingface_hub/issues/1952 for more details.
  52. with _GOOGLE_COLAB_SECRET_LOCK:
  53. global _GOOGLE_COLAB_SECRET
  54. global _IS_GOOGLE_COLAB_CHECKED
  55. if _IS_GOOGLE_COLAB_CHECKED: # request access only once
  56. return _GOOGLE_COLAB_SECRET
  57. try:
  58. from google.colab import userdata # type: ignore
  59. from google.colab.errors import Error as ColabError # type: ignore
  60. except ImportError:
  61. return None
  62. try:
  63. token = userdata.get("HF_TOKEN")
  64. _GOOGLE_COLAB_SECRET = _clean_token(token)
  65. except userdata.NotebookAccessError:
  66. # Means the user has a secret call `HF_TOKEN` and got a popup "please grand access to HF_TOKEN" and refused it
  67. # => warn user but ignore error => do not re-request access to user
  68. warnings.warn(
  69. "\nAccess to the secret `HF_TOKEN` has not been granted on this notebook."
  70. "\nYou will not be requested again."
  71. "\nPlease restart the session if you want to be prompted again."
  72. )
  73. _GOOGLE_COLAB_SECRET = None
  74. except userdata.SecretNotFoundError:
  75. # Means the user did not define a `HF_TOKEN` secret => warn
  76. warnings.warn(
  77. "\nThe secret `HF_TOKEN` does not exist in your Colab secrets."
  78. "\nTo authenticate with the Hugging Face Hub, create a token in your settings tab "
  79. "(https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session."
  80. "\nYou will be able to reuse this secret in all of your notebooks."
  81. "\nPlease note that authentication is recommended but still optional to access public models or datasets."
  82. )
  83. _GOOGLE_COLAB_SECRET = None
  84. except ColabError as e:
  85. # Something happen but we don't know what => recommend to open a GitHub issue
  86. warnings.warn(
  87. f"\nError while fetching `HF_TOKEN` secret value from your vault: '{str(e)}'."
  88. "\nYou are not authenticated with the Hugging Face Hub in this notebook."
  89. "\nIf the error persists, please let us know by opening an issue on GitHub "
  90. "(https://github.com/huggingface/huggingface_hub/issues/new)."
  91. )
  92. _GOOGLE_COLAB_SECRET = None
  93. _IS_GOOGLE_COLAB_CHECKED = True
  94. return _GOOGLE_COLAB_SECRET
  95. def _get_token_from_environment() -> str | None:
  96. # `HF_TOKEN` has priority (keep `HUGGING_FACE_HUB_TOKEN` for backward compatibility)
  97. return _clean_token(os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN"))
  98. def _get_token_from_file() -> str | None:
  99. try:
  100. return _clean_token(Path(constants.HF_TOKEN_PATH).read_text())
  101. except FileNotFoundError:
  102. return None
  103. def get_stored_tokens() -> dict[str, str]:
  104. """
  105. Returns the parsed INI file containing the access tokens.
  106. The file is located at `HF_STORED_TOKENS_PATH`, defaulting to `~/.cache/huggingface/stored_tokens`.
  107. If the file does not exist, an empty dictionary is returned.
  108. Returns: `dict[str, str]`
  109. Key is the token name and value is the token.
  110. """
  111. tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
  112. if not tokens_path.exists():
  113. stored_tokens = {}
  114. config = configparser.ConfigParser()
  115. try:
  116. config.read(tokens_path)
  117. stored_tokens = {token_name: config.get(token_name, "hf_token") for token_name in config.sections()}
  118. except configparser.Error as e:
  119. logger.error(f"Error parsing stored tokens file: {e}")
  120. stored_tokens = {}
  121. return stored_tokens
  122. def _save_stored_tokens(stored_tokens: dict[str, str]) -> None:
  123. """
  124. Saves the given configuration to the stored tokens file.
  125. Args:
  126. stored_tokens (`dict[str, str]`):
  127. The stored tokens to save. Key is the token name and value is the token.
  128. """
  129. stored_tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
  130. # Write the stored tokens into an INI file
  131. config = configparser.ConfigParser()
  132. for token_name in sorted(stored_tokens.keys()):
  133. config.add_section(token_name)
  134. config.set(token_name, "hf_token", stored_tokens[token_name])
  135. stored_tokens_path.parent.mkdir(parents=True, exist_ok=True)
  136. with stored_tokens_path.open("w") as config_file:
  137. config.write(config_file)
  138. def _get_token_by_name(token_name: str) -> str | None:
  139. """
  140. Get the token by name.
  141. Args:
  142. token_name (`str`):
  143. The name of the token to get.
  144. Returns:
  145. `str` or `None`: The token, `None` if it doesn't exist.
  146. """
  147. stored_tokens = get_stored_tokens()
  148. if token_name not in stored_tokens:
  149. return None
  150. return _clean_token(stored_tokens[token_name])
  151. def _save_token(token: str, token_name: str) -> None:
  152. """
  153. Save the given token.
  154. If the stored tokens file does not exist, it will be created.
  155. Args:
  156. token (`str`):
  157. The token to save.
  158. token_name (`str`):
  159. The name of the token.
  160. """
  161. tokens_path = Path(constants.HF_STORED_TOKENS_PATH)
  162. stored_tokens = get_stored_tokens()
  163. stored_tokens[token_name] = token
  164. _save_stored_tokens(stored_tokens)
  165. logger.info(f"The token `{token_name}` has been saved to {tokens_path}")
  166. def _clean_token(token: str | None) -> str | None:
  167. """Clean token by removing trailing and leading spaces and newlines.
  168. If token is an empty string, return None.
  169. """
  170. if token is None:
  171. return None
  172. return token.replace("\r", "").replace("\n", "").strip() or None