sda.api.graph_auth ================== .. py:module:: sda.api.graph_auth .. autoapi-nested-parse:: MS Graph API authentication via MSAL. Two authentication modes are supported, selected automatically by :func:`get_token_auto`: * **CI / service-principal mode** — all three ``AZURE_*`` environment variables (``AZURE_TENANT_ID``, ``AZURE_CLIENT_ID``, ``AZURE_CLIENT_SECRET``) must be set. Uses :func:`get_access_token` (client-credentials flow). * **Interactive desktop mode** (SDA-Desktop registration) — default when the env vars are not set. Uses :func:`get_access_token_interactive` which tries ``PublicClientApplication.acquire_token_silent()`` first (reads the persistent token cache at ``~/.sda/token_cache.bin``), then falls back to ``acquire_token_interactive()`` (browser popup), and finally falls back to ``acquire_token_by_device_flow()`` if no display is detected. .. note:: **Token cache concurrency** — the cache file ``~/.sda/token_cache.bin`` is read and written without a file lock. Simultaneous calls from multiple processes on the same machine may produce a race condition on the cache file. In practice this is rare because the silent-refresh path is read-only unless a new token is issued. A proper file lock is left as a future improvement. Attributes ---------- .. autoapisummary:: sda.api.graph_auth.GRAPH_SCOPE sda.api.graph_auth.GRAPH_DELEGATED_SCOPE sda.api.graph_auth.ConfidentialClientApplication sda.api.graph_auth.PublicClientApplication sda.api.graph_auth.SerializableTokenCache Functions --------- .. autoapisummary:: sda.api.graph_auth.get_access_token sda.api.graph_auth.get_access_token_interactive sda.api.graph_auth.get_token_auto Module Contents --------------- .. py:data:: GRAPH_SCOPE :value: ['https://graph.microsoft.com/.default'] .. py:data:: GRAPH_DELEGATED_SCOPE :value: ['https://graph.microsoft.com/Files.Read'] .. py:data:: ConfidentialClientApplication .. py:data:: PublicClientApplication .. py:data:: SerializableTokenCache .. py:function:: get_access_token(tenant_id, client_id, client_secret) Acquire an access token using client credentials flow (CI / service principal). :param tenant_id: Azure AD tenant ID. :type tenant_id: :py:class:`str` :param client_id: Azure AD application (client) ID. :type client_id: :py:class:`str` :param client_secret: Azure AD client secret. :type client_secret: :py:class:`str` :returns: Bearer access token. :rtype: :py:class:`str` :raises RuntimeError: If the token acquisition fails. .. py:function:: get_access_token_interactive(tenant_id, client_id, cache_path = _DEFAULT_CACHE_PATH) Acquire an access token via interactive user login (desktop flow). Tries the following in order: 1. **Silent** — return cached / refreshed token without user interaction. 2. **Interactive** — open a browser window for the user to sign in (only if a display is detected; see :func:`_has_display`). 3. **Device-code** — print a URL and code the user can enter on any device (fallback for headless / SSH environments). The token is cached in ``cache_path`` (default ``~/.sda/token_cache.bin``) and silently refreshed on subsequent calls so the browser only opens once. .. warning:: **Concurrency** — the cache file is not file-locked. Concurrent processes may race on the file. This is a known v1 limitation. :param tenant_id: Azure AD tenant ID for Spark Cleantech. :type tenant_id: :py:class:`str` :param client_id: Public application (client) ID registered in Azure AD. :type client_id: :py:class:`str` :param cache_path: Path to the serialised token cache file. :type cache_path: :py:class:`Path`, *optional* :returns: Bearer access token. :rtype: :py:class:`str` :raises RuntimeError: If all authentication attempts fail. .. py:function:: get_token_auto(cache_path = _DEFAULT_CACHE_PATH) Acquire a Graph API access token, auto-detecting the auth mode. - If the current request set a delegated token via :func:`sda.api.graph_context.set_request_graph_token` (SDA-Cloud web session), that token is returned immediately (even when CI env vars are set). - If **all three** ``AZURE_TENANT_ID``, ``AZURE_CLIENT_ID``, and ``AZURE_CLIENT_SECRET`` environment variables are set, uses the client-credentials (CI) flow. - If only *some* are set, raises :class:`RuntimeError` naming the missing variables (partial configuration is treated as a misconfiguration, not a silent fallback to interactive mode). - Otherwise, uses the interactive desktop flow with the hardcoded Spark tenant and :data:`~sda.api.graph_sharepoint_ids.SPARK_DESKTOP_CLIENT_ID` from :mod:`sda.api.graph_sharepoint_ids`. :returns: Bearer access token. :rtype: :py:class:`str` :raises RuntimeError: If CI env vars are partially set, or if authentication fails.