### Install msal-extensions with Portalocker Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/README.md Install the Microsoft Authentication Extensions for Python with the portalocker library for improved performance. This is the recommended installation method. ```bash pip install "msal-extensions[portalocker]" ``` -------------------------------- ### Install Base msal-extensions Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/README.md Install the base package for Microsoft Authentication Extensions for Python. ```bash pip install msal-extensions ``` -------------------------------- ### Install MSAL Extensions Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Install the base msal-extensions package. Use this command to install the package with portalocker support. Use this command for development installations. ```bash pip install msal-extensions ``` ```bash pip install "msal-extensions[portalocker]" ``` ```bash pip install -e ".[portalocker]" ``` -------------------------------- ### Install Optional Portalocker Dependency Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/module-overview.md Demonstrates how to install the MSAL Extensions with the optional 'portalocker' extra. This is recommended for enhanced file locking capabilities on certain platforms. ```bash pip install msal-extensions[portalocker] ``` -------------------------------- ### Initialize LibSecretAgent Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/platform-specific.md Instantiate `LibSecretAgent` with schema, attributes, and optional label and attribute types. Ensure PyGObject and libsecret are installed. ```python from msal_extensions.libsecret import LibSecretAgent from gi.repository import Secret agent = LibSecretAgent( schema_name="msal_tokens", attributes={"app": "myapp", "user": "alice"}, label="MSAL Token Cache", attribute_types={ "app": Secret.SchemaAttributeType.STRING, "user": Secret.SchemaAttributeType.STRING, } ) ``` -------------------------------- ### Install Linux Dependencies for libsecret Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Installs necessary system packages and Python libraries for libsecret-based persistence on Debian/Ubuntu systems. ```bash # Debian/Ubuntu sudo apt install libgirepository1.0-dev libcairo2-dev python3-dev gir1.2-secret-1 pip install pygobject # Or without Cairo: sudo apt install libgirepository1.0-dev python3-dev gir1.2-secret-1 pip install wheel PYGOBJECT_WITHOUT_PYCAIRO=1 pip install --no-build-isolation pygobject ``` -------------------------------- ### Install PyGObject Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/wiki/Encryption-on-Linux Installs the PyGObject library after its system dependencies have been met. This allows MSAL Extensions for Python to interface with libsecret for secure storage. ```bash pip install pygobject ``` -------------------------------- ### Validate libsecret Functionality (Linux) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Performs a trial run to check if libsecret is functional on Linux systems. If it fails, it suggests common fixes such as installation, starting the D-Bus service, or ensuring a graphical environment is used. ```python from msal_extensions.libsecret import trial_run try: trial_run() print("libsecret is functional") except RuntimeError as e: print(f"libsecret unavailable: {e}") # Common fixes: # 1. Install: sudo apt install gnome-keyring gir1.2-secret-1 # 2. Start service: systemctl --user start dbus # 3. Use graphical environment (not SSH) ``` -------------------------------- ### Handle Keychain Operations with Error Handling Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Demonstrates how to use the Keychain class and catch KeychainError exceptions. This example specifically checks if the error indicates an item was not found, allowing for alternative actions like creating a new password. ```python from msal_extensions.osx import Keychain, KeychainError try: with Keychain() as keychain: password = keychain.get_generic_password("service", "account") except KeychainError as e: if e.exit_status == KeychainError.ITEM_NOT_FOUND: print("Password not found, creating new one...") else: raise ``` -------------------------------- ### DataBlob Usage Example Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Demonstrates how to use the `DataBlob` structure with `WindowsDataProtectionAgent` to protect plaintext data. The encrypted output is a bytes object. ```python from msal_extensions.windows import DataBlob, WindowsDataProtectionAgent agent = WindowsDataProtectionAgent() encrypted = agent.protect("plaintext") # encrypted is bytes object safe to use in Python ``` -------------------------------- ### Compile-Time Dependencies Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/module-overview.md Specifies the core MSAL library version required for compilation. Ensure this version is installed before building or running the extensions. ```text msal >= 1.29, < 2 ``` -------------------------------- ### Install PyGObject Dependencies on Debian Linux Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/wiki/Encryption-on-Linux Installs the necessary system libraries required for PyGObject to function with libsecret on Debian-based Linux distributions. This is a prerequisite for using MSAL Extensions for Python's encryption features on Linux. ```bash sudo apt install libgirepository1.0-dev libcairo2-dev python3-dev gir1.2-secret-1 ``` -------------------------------- ### Build Encrypted Custom Data Persistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/README.md Builds a persistence instance for storing custom data, attempting encryption and falling back to plain text if necessary. This example demonstrates saving and loading arbitrary JSON data. ```python def build_persistence(location, fallback_to_plaintext=False): """Build a suitable persistence instance based your current OS""" try: return build_encrypted_persistence(location) except: # pylint: disable=bare-except if not fallback_to_plaintext: raise logging.warning("Encryption unavailable. Opting in to plain text.") return FilePersistence(location) persistence = build_persistence("storage.bin", fallback_to_plaintext=False) print("Type of persistence: {}".format(persistence.__class__.__name__)) print("Is this persistence encrypted?", persistence.is_encrypted) data = { # It can be anything, here we demonstrate an arbitrary json object "foo": "hello world", "bar": "", "service_principle_1": "blah blah...", } persistence.save(json.dumps(data)) assert json.loads(persistence.load()) == data ``` -------------------------------- ### Handle PersistenceNotFound Exception Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Catch PersistenceNotFound to initialize persistence data if it doesn't exist. This example shows initializing FilePersistence. ```python from msal_extensions import FilePersistence, PersistenceNotFound persistence = FilePersistence("cache.bin") try: data = persistence.load() except PersistenceNotFound: # No data saved yet, initialize it persistence.save("{}") ``` -------------------------------- ### Handle PersistenceNotFound Exception Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Raised when attempting to load data from a non-existent persistence instance. This example shows how to catch and handle this specific error. ```python from msal_extensions import FilePersistence, PersistenceNotFound persistence = FilePersistence("nonexistent.bin") try: data = persistence.load() except PersistenceNotFound as e: print(f"File not found: {e}") ``` -------------------------------- ### Initialize and Save with KeychainPersistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Demonstrates how to initialize KeychainPersistence and save token data. Ensure Keychain dependencies are available. ```python from msal_extensions import KeychainPersistence persistence = KeychainPersistence("~/.signal_file") persistence.save("token_data") ``` -------------------------------- ### Recommended Pattern for Desktop Apps Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Demonstrates the recommended pattern for desktop applications using MSAL Extensions. It attempts to use encrypted persistence and falls back to plaintext file persistence if encryption fails. ```python from msal_extensions import ( build_encrypted_persistence, FilePersistence, PersistedTokenCache ) import msal def build_app(): # Try encrypted, fallback to plaintext try: persistence = build_encrypted_persistence("~/.app/cache.bin") except Exception: persistence = FilePersistence("~/.app/cache.bin") cache = PersistedTokenCache(persistence) app = msal.PublicClientApplication( "client_id", authority="https://login.microsoftonline.com/common", token_cache=cache ) return app app = build_app() ``` -------------------------------- ### Typical Desktop Application Usage with MSAL Python Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/README.md Demonstrates how to create an encrypted token persistence, initialize a persistent token cache, and integrate it with MSAL's PublicClientApplication for automatic token persistence and multi-process SSO. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal # Create encrypted persistence (Windows/macOS/Linux) persistence = build_encrypted_persistence("~/.app/token_cache.bin") # Create persistent token cache cache = PersistedTokenCache(persistence) # Use with MSAL app = msal.PublicClientApplication("client_id", token_cache=cache) # Tokens are automatically persisted across sessions # Multiple processes share the same cache file with SSO ``` -------------------------------- ### Get File Last Modified Timestamp Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Demonstrates how to get the last modified time of a file using FilePersistence and calculate its age in seconds. This is useful for cache invalidation or tracking file changes. ```python import time persistence = FilePersistence("cache.bin") persistence.save("data") last_mod = persistence.time_last_modified() current = time.time() print(f"Age: {current - last_mod} seconds") ``` -------------------------------- ### Initialize KeychainPersistence with Defaults Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Use this snippet for basic initialization of KeychainPersistence when default service and account names are acceptable. ```python from msal_extensions import KeychainPersistence # Basic usage with defaults persistence = KeychainPersistence("~/.msal_cache_signal") ``` -------------------------------- ### Initialize FilePersistenceWithDataProtection with Entropy Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Shows how to initialize FilePersistenceWithDataProtection, with and without entropy. Using entropy is recommended for enhanced security. ```python from msal_extensions import FilePersistenceWithDataProtection # Without entropy persistence = FilePersistenceWithDataProtection("~/token_cache.bin") # With entropy (recommended) persistence = FilePersistenceWithDataProtection( "~/token_cache.bin", entropy="my_application_key_12345" ) ``` -------------------------------- ### Get MSAL Extensions Version Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Retrieves the current version of the msal_extensions package. ```python __version__ = "1.3.1" ``` -------------------------------- ### Handle PersistenceEncryptionError Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Catch PersistenceEncryptionError to handle failures during the encryption process when saving data. This example uses build_encrypted_persistence. ```python from msal_extensions import build_encrypted_persistence, PersistenceEncryptionError persistence = build_encrypted_persistence("cache.bin") try: persistence.save(token_data) except PersistenceEncryptionError as e: print(f"Cannot encrypt: {e}") ``` -------------------------------- ### Initialize FilePersistence with Different Paths Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Demonstrates initializing FilePersistence with user-expanded, absolute, and relative paths. Parent directories are created if they do not exist. ```python from msal_extensions import FilePersistence # Basic usage persistence = FilePersistence("~/token_cache.bin") # With absolute path persistence = FilePersistence("/var/lib/myapp/cache.bin") # With relative path persistence = FilePersistence("./cache/tokens.bin") ``` -------------------------------- ### trial_run Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/platform-specific.md Tests the functionality of the libsecret integration in the current environment by performing a sequence of save, load, and clear operations. ```APIDOC ## Function: trial_run ### Description Tests whether libsecret is functioning properly in the current environment. This function performs a series of operations including creating a temporary schema and agent, attempting to save and load test data, and then clearing the test data. It raises a `RuntimeError` if any of these operations fail, indicating that libsecret is not working as expected. ### Behavior 1. Creates a temporary schema and agent. 2. Attempts to save test data. 3. Attempts to load test data. 4. Clears test data. 5. Raises `RuntimeError` if any operation fails. ### Throws - **RuntimeError** - If libsecret is not functioning (e.g., running in a headless environment like SSH, or if the gnome-keyring service is not running). - **ImportError** - If the GLib module is not available. ### Purpose This function is typically called during the initialization of `LibsecretPersistence` to validate the environment before proceeding with secret storage operations. ### Common Failures & Solutions - **Headless SSH session**: Libsecret is not supported in headless SSH sessions as it requires a graphical environment. Ensure you are running in a desktop session. - **Service not running**: If the `gnome-keyring` service is not running, install it using your system's package manager (e.g., `sudo apt install gnome-keyring`). - **D-Bus unavailable**: Ensure the D-Bus session is running by executing `systemctl --user start dbus`. - **Missing dependencies**: Install necessary system packages, such as `gir1.2-secret-1` and `python3-gi` (e.g., `sudo apt install gir1.2-secret-1 python3-gi`). ### Example ```python from msal_extensions.libsecret import trial_run try: trial_run() print("libsecret is functioning") except RuntimeError as e: print(f"libsecret not available: {e}") ``` ``` -------------------------------- ### Initialize PersistedTokenCache with Encrypted Persistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/token-cache.md Demonstrates how to build platform-appropriate encrypted persistence and initialize a PersistedTokenCache. This cache can then be used with MSAL's PublicClientApplication for token management. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal # Build platform-appropriate encrypted persistence persistence = build_encrypted_persistence("token_cache.bin") # Create persisted token cache cache = PersistedTokenCache(persistence) # Use with MSAL PublicClientApplication app = msal.PublicClientApplication( client_id="my_client_id", token_cache=cache ) ``` -------------------------------- ### Initializing Cache on PersistenceNotFound Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/errors.md This code demonstrates how to handle `PersistenceNotFound` by initializing an empty cache. It's commonly used when a cache file or entry does not exist on the first run. ```python from msal_extensions import FilePersistence, PersistenceNotFound persistence = FilePersistence("cache.bin") try: data = persistence.load() except PersistenceNotFound: # Initialize with empty cache persistence.save("{}") print("Initialized new cache") ``` -------------------------------- ### Initialize and Load with FilePersistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Shows how to initialize FilePersistence and load string content from the specified file. This is useful for retrieving previously saved data. ```python persistence = FilePersistence("~/my_data.txt") data = persistence.load() print(data) ``` -------------------------------- ### Handle Missing Cache (First Run) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use this snippet when the token cache might not exist on the first run. It catches `PersistenceNotFound` and prompts the user to authenticate. ```python from msal_extensions import PersistedTokenCache, PersistenceNotFound cache = PersistedTokenCache(persistence) try: tokens = cache.search("access_token") except PersistenceNotFound: print("Cache doesn't exist; user must authenticate first") tokens = [] ``` -------------------------------- ### Get File Location with FilePersistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Illustrates how to retrieve the expanded file path managed by FilePersistence. This can be helpful for debugging or external file operations. ```python persistence = FilePersistence("~/token_cache.bin") print(persistence.get_location()) # /home/user/token_cache.bin ``` -------------------------------- ### Recommended Pattern for Multi-User Apps Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Illustrates a recommended pattern for multi-user applications. It uses a factory class to manage separate encrypted token caches for each account, identified by a unique account ID. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal import os class AppFactory: def __init__(self, app_id): self.app_id = app_id self.cache_dir = os.path.expanduser("~/.app/cache") os.makedirs(self.cache_dir, exist_ok=True) def get_app_for_account(self, account_id): cache_file = os.path.join(self.cache_dir, f"{account_id}.bin") persistence = build_encrypted_persistence(cache_file) cache = PersistedTokenCache(persistence) return msal.PublicClientApplication( self.app_id, token_cache=cache ) factory = AppFactory("my_client_id") app = factory.get_app_for_account("user@example.com") ``` -------------------------------- ### get_location Method Signature Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Abstract method to get the location identifier for the persistence. Implementations must return the file path or resource identifier. ```python @abc.abstractmethod def get_location(self) -> str: ... ``` -------------------------------- ### Initialize and Save with FilePersistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Demonstrates how to initialize FilePersistence with a file path and save string content to it. The file path is expanded using os.path.expanduser(). ```python from msal_extensions import FilePersistence persistence = FilePersistence("~/my_data.txt") persistence.save("my sensitive data") ``` -------------------------------- ### FilePersistence (Plaintext) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use FilePersistence for simple plaintext data storage. It allows saving, loading, checking modification time, and getting the file location. ```python from msal_extensions import FilePersistence # Create persistence = FilePersistence("~/data.bin") # Save persistence.save("my string data") # Load data = persistence.load() # Returns str # Check modification time mod_time = persistence.time_last_modified() # Returns float (Unix timestamp) # Get location path = persistence.get_location() # Returns str ``` -------------------------------- ### Initialize and Save with LibsecretPersistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Shows how to initialize LibsecretPersistence with custom attributes and save token data. Requires PyGObject and libsecret dependencies. ```python from msal_extensions import LibsecretPersistence persistence = LibsecretPersistence( "~/.signal_file", attributes={"app": "myapp", "user": "alice"} ) persistence.save("token_data") ``` -------------------------------- ### Use Keychain Context Manager Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/platform-specific.md Illustrates the usage of the Keychain class as a context manager. Ensure proper resource cleanup by using the 'with' statement, which automatically handles the release of Keychain references. ```python from msal_extensions.osx import Keychain with Keychain() as keychain: keychain.set_generic_password("service", "account", "password") # Keychain reference is released here ``` -------------------------------- ### Reuse Cache Persistence Objects Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md To improve performance, reuse a single instance of PersistedTokenCache instead of creating a new object each time. This avoids repeated initialization and setup costs. ```python # ❌ Bad: Create new object each time def get_cache(): return PersistedTokenCache( build_encrypted_persistence("cache.bin") ) # ✅ Good: Reuse object _cache = None def get_cache(): global _cache if _cache is None: _cache = PersistedTokenCache( build_encrypted_persistence("cache.bin") ) return _cache ``` -------------------------------- ### LibsecretPersistence (Linux) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use LibsecretPersistence on Linux for secure data storage using the libsecret standard. It offers the same interface for saving, loading, checking modification time, and getting the file location. ```python from msal_extensions import LibsecretPersistence # Create (Linux only) persistence = LibsecretPersistence( "~/.signal_file", attributes={"app": "myapp", "user": "alice"} ) # Same interface as FilePersistence: persistence.save("data from libsecret") data = persistence.load() mod_time = persistence.time_last_modified() path = persistence.get_location() # Check if encrypted assert persistence.is_encrypted ``` -------------------------------- ### Initialize and Save with FilePersistenceWithDataProtection Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Demonstrates initializing FilePersistenceWithDataProtection with a file path and an optional entropy string, then saving encrypted content. This class uses Windows DPAPI for encryption. ```python from msal_extensions import FilePersistenceWithDataProtection persistence = FilePersistenceWithDataProtection("token_cache.bin", entropy="my_app") persistence.save("token_data") ``` -------------------------------- ### Initialize KeychainPersistence with Custom Names Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Configure KeychainPersistence with custom service and account names for specific application and user identification within the keychain. ```python persistence = KeychainPersistence( signal_location="~/.msal_cache_signal", service_name="MyApp-MSAL", account_name="alice@example.com" ) ``` -------------------------------- ### KeychainPersistence (macOS) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use KeychainPersistence on macOS for secure data storage in the system's keychain. It provides the same interface for saving, loading, checking modification time, and getting the file location. ```python from msal_extensions import KeychainPersistence # Create (macOS only) persistence = KeychainPersistence( "~/.signal_file", service_name="MyApp", account_name="user@example.com" ) # Same interface as FilePersistence: persistence.save("data from Keychain") data = persistence.load() mod_time = persistence.time_last_modified() path = persistence.get_location() # Check if encrypted assert persistence.is_encrypted ``` -------------------------------- ### FilePersistenceWithDataProtection (Windows) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use FilePersistenceWithDataProtection on Windows for encrypted data storage using DPAPI. It supports saving, loading, checking modification time, and getting the file location, similar to FilePersistence. ```python from msal_extensions import FilePersistenceWithDataProtection # Create (Windows only) persistence = FilePersistenceWithDataProtection( "~/data.bin", entropy="optional_entropy_string" ) # Same interface as FilePersistence: persistence.save("encrypted data") data = persistence.load() mod_time = persistence.time_last_modified() path = persistence.get_location() # Check if encrypted if persistence.is_encrypted: print("Data is encrypted with DPAPI") ``` -------------------------------- ### Initialize Multiple KeychainPersistence Instances Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Create separate KeychainPersistence instances for different users or accounts within the same application, ensuring distinct storage. ```python alice_persistence = KeychainPersistence( "~/.msal_cache_alice", service_name="MyApp-MSAL", account_name="alice@example.com" ) bob_persistence = KeychainPersistence( "~/.msal_cache_bob", service_name="MyApp-MSAL", account_name="bob@example.com" ) ``` -------------------------------- ### Using CrossPlatLock as a Context Manager Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/locking.md Illustrates the basic usage of CrossPlatLock with a 'with' statement to ensure the lock is automatically acquired and released. ```python with CrossPlatLock("/path/to/lock"): # Protected code here # Lock is held while this block executes ``` -------------------------------- ### Token Cache Persistence in a Single Process Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/token-cache.md Demonstrates setting up an encrypted token cache persistence and a `PersistedTokenCache` instance for use within a single Python process. The token cache is automatically persisted after modifications. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal persistence = build_encrypted_persistence("token_cache.bin") cache = PersistedTokenCache(persistence) app = msal.PublicClientApplication("client_id", token_cache=cache) # Token cache is automatically persisted after modifications result = app.acquire_token_interactive(scopes=["scope"]) ``` -------------------------------- ### Test Libsecret Functionality Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/platform-specific.md Execute `trial_run` to verify libsecret is operational. This function performs save, load, and clear operations on temporary data. It raises `RuntimeError` or `ImportError` if libsecret is not functioning or dependencies are missing. ```python from msal_extensions.libsecret import trial_run try: trial_run() print("libsecret is functioning") except RuntimeError as e: print(f"libsecret not available: {e}") ``` -------------------------------- ### Build Encrypted Persistence for Desktop App Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Builds an encrypted token persistence object for a single-user desktop application. Initializes a PublicClientApplication with the configured token cache. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal persistence = build_encrypted_persistence("~/.myapp/token_cache.bin") cache = PersistedTokenCache(persistence) app = msal.PublicClientApplication( client_id="my_client_id", token_cache=cache ) ``` -------------------------------- ### Instantiate MSAL PublicClientApplication with Token Cache Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/README.md Instantiates a PublicClientApplication from MSAL Python using a pre-configured persisted token cache. ```python app = msal.PublicClientApplication("my_client_id", token_cache=cache) ``` -------------------------------- ### Package Structure Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/module-overview.md Illustrates the directory layout of the msal_extensions package, showing the main modules and their roles. ```text msal_extensions/ ├── __init__.py # Public API exports ├── persistence.py # Persistence implementations ├── token_cache.py # PersistedTokenCache class ├── cache_lock.py # CrossPlatLock (portalocker-based) ├── filelock.py # CrossPlatLock (fallback) ├── windows.py # Windows DPAPI implementation ├── osx.py # macOS Keychain implementation └── libsecret.py # Linux libsecret implementation ``` -------------------------------- ### Build Encrypted Persistence Instance Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Automatically selects and builds an encrypted persistence instance based on the operating system. Includes a fallback to unencrypted storage if an error occurs during initialization. ```python from msal_extensions import build_encrypted_persistence, FilePersistence try: persistence = build_encrypted_persistence("token_cache.bin") except Exception: # Fallback to unencrypted storage persistence = FilePersistence("token_cache.bin") print(f"Encrypted: {persistence.is_encrypted}") ``` -------------------------------- ### Initialize LibsecretPersistence with Defaults Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Use this snippet for basic initialization of LibsecretPersistence when default schema name and attributes are sufficient. ```python from msal_extensions import LibsecretPersistence # Basic usage with defaults persistence = LibsecretPersistence("~/.msal_cache_signal") ``` -------------------------------- ### Import Main APIs from msal_extensions Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/README.md Imports all public APIs including persistence classes, a factory function, token cache, and locking mechanisms from the msal_extensions package. ```python from msal_extensions import ( # Persistence classes FilePersistence, FilePersistenceWithDataProtection, KeychainPersistence, LibsecretPersistence, # Factory function build_encrypted_persistence, # Token cache PersistedTokenCache, # Locking CrossPlatLock, LockError, ) ``` -------------------------------- ### Automatic Lock File Cleanup with Context Manager Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/locking.md Demonstrates how lock files are automatically removed upon exiting a 'with' block using the CrossPlatLock context manager. ```python with CrossPlatLock("/tmp/lock"): # Lock file exists here pass # Lock file is deleted here ``` -------------------------------- ### Public API Exports from __init__.py Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/module-overview.md Shows the version and public symbols exported by the msal_extensions package, including persistence and token cache related classes. ```python __version__ = "1.3.1" from .persistence import ( FilePersistence, build_encrypted_persistence, FilePersistenceWithDataProtection, KeychainPersistence, LibsecretPersistence, ) from .token_cache import PersistedTokenCache, CrossPlatLock, LockError ``` -------------------------------- ### Acquiring a Lock with Error Handling Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/locking.md Demonstrates how to use CrossPlatLock within a try-except block to handle potential LockError exceptions when acquiring a lock. ```python from msal_extensions import CrossPlatLock, LockError try: with CrossPlatLock("/tmp/my_lock"): # Perform protected operation pass except LockError as e: print(f"Could not acquire lock: {e}") ``` -------------------------------- ### Build Persistence with Plaintext Fallback Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/errors.md This pattern is recommended for desktop applications. It attempts to build encrypted persistence and falls back to plaintext if encryption is unavailable, logging a warning. ```python import logging from msal_extensions import ( build_encrypted_persistence, FilePersistence, PersistedTokenCache ) def build_persistence(location, fallback_to_plaintext=False): """Build persistence with automatic fallback.""" try: return build_encrypted_persistence(location) except Exception: if not fallback_to_plaintext: raise logging.warning("Encryption unavailable. Using plaintext.") return FilePersistence(location) persistence = build_persistence("token_cache.bin", fallback_to_plaintext=True) cache = PersistedTokenCache(persistence) ``` -------------------------------- ### Token Cache Persistence for Multiple Processes (SSO) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/token-cache.md Illustrates how to use the same shared token cache file across multiple Python processes to enable Single Sign-On (SSO). Each process initializes its own `PersistedTokenCache` pointing to the same file. ```python # Process 1 (same code) persistence = build_encrypted_persistence("shared_token_cache.bin") cache = PersistedTokenCache(persistence) app1 = msal.PublicClientApplication("client_id", token_cache=cache) # Process 2 (different process, same cache file) persistence = build_encrypted_persistence("shared_token_cache.bin") cache = PersistedTokenCache(persistence) app2 = msal.PublicClientApplication("client_id", token_cache=cache) # Process 2 automatically sees tokens acquired by Process 1 accounts = app2.get_accounts() ``` -------------------------------- ### Initialize Multiple LibsecretPersistence Instances for Users Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Create distinct LibsecretPersistence instances for different users by specifying unique attributes, ensuring proper secret segregation. ```python alice_persistence = LibsecretPersistence( "~/.msal_cache_alice", schema_name="com.example.myapp.tokens", attributes={"user": "alice@example.com"} ) bob_persistence = LibsecretPersistence( "~/.msal_cache_bob", schema_name="com.example.myapp.tokens", attributes={"user": "bob@example.com"} ) ``` -------------------------------- ### build_encrypted_persistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/persistence.md Automatically builds a suitable encrypted persistence instance based on the current operating system. It uses Windows DPAPI on Windows, macOS Keychain on macOS, and libsecret on Linux. ```APIDOC ## build_encrypted_persistence(location) ### Description Automatically builds a suitable encrypted persistence instance based on the current operating system. ### Method `build_encrypted_persistence` ### Parameters #### Path Parameters - **location** (str) - Required - File path for persistence storage; will be expanded using `os.path.expanduser()` ### Returns `BasePersistence` — A platform-specific encrypted persistence instance: `FilePersistenceWithDataProtection` on Windows, `KeychainPersistence` on macOS, `LibsecretPersistence` on Linux. ### Throws - `RuntimeError` - When called on an unsupported platform (neither Windows, macOS, nor Linux) - `Exception` - Deferred to runtime for Windows/macOS; raised during initialization for Linux if encryption dependencies are unavailable ### Example ```python from msal_extensions import build_encrypted_persistence, FilePersistence try: persistence = build_encrypted_persistence("token_cache.bin") except Exception: # Fallback to unencrypted storage persistence = FilePersistence("token_cache.bin") print(f"Encrypted: {persistence.is_encrypted}") ``` ``` -------------------------------- ### Create Token Cache with Plaintext Storage Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use `FilePersistence` for simple plaintext storage of tokens. This is less secure than encrypted storage and should be used with caution. ```python from msal_extensions import FilePersistence, PersistedTokenCache persistence = FilePersistence("~/.myapp/cache.bin") cache = PersistedTokenCache(persistence) ``` -------------------------------- ### String Parameters Convention Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Details the conventions for string parameters used throughout the library, including content, file paths, names, and schemas. ```APIDOC ## String Parameters All string parameters throughout the library use native Python strings (`str`): - **Persist content**: `str` (JSON, plaintext, etc.) - **File paths**: `str` (will be expanded via `os.path.expanduser()`) - **Service/account names**: `str` - **Schema names**: `str` ``` -------------------------------- ### Build Encrypted Persistence for Platform Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Builds an encrypted persistence object tailored to the current operating system. Falls back to plaintext file persistence if platform-specific encryption is unavailable or fails. ```python from msal_extensions import build_encrypted_persistence, FilePersistence import sys try: # Try platform-specific encryption persistence = build_encrypted_persistence("token_cache.bin") except RuntimeError as e: print(f"Unsupported platform: {e}") sys.exit(1) except Exception: # Fallback to plaintext if encryption unavailable persistence = FilePersistence("token_cache.bin") ``` -------------------------------- ### Protect Generic Data with Encryption Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use `build_encrypted_persistence` and `CrossPlatLock` to securely save and load generic data, such as configuration settings or API keys, to a file. Ensure the lock file is used to prevent race conditions. ```python from msal_extensions import build_encrypted_persistence, CrossPlatLock import json persistence = build_encrypted_persistence("~/.myapp/config.bin") # Save data data = {"api_key": "secret123", "settings": {"theme": "dark"}} with CrossPlatLock("~/.myapp/config.lock"): persistence.save(json.dumps(data)) # Load data with CrossPlatLock("~/.myapp/config.lock"): loaded = json.loads(persistence.load()) ``` -------------------------------- ### Handle Keychain Errors Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/platform-specific.md Demonstrates how to catch and handle specific KeychainError exceptions, such as when an item is not found. This is useful for gracefully managing scenarios where stored credentials are missing. ```python from msal_extensions.osx import Keychain, KeychainError try: with Keychain() as keychain: password = keychain.get_generic_password("myservice", "myaccount") except KeychainError as e: if e.exit_status == KeychainError.ITEM_NOT_FOUND: print("Password not found in Keychain") else: print(f"Keychain error: {e.message}") ``` -------------------------------- ### Exception Hierarchy Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/module-overview.md Illustrates the inheritance structure of exceptions within the library, categorized by module. Useful for understanding how to catch specific errors. ```text IOError └── persistence.PersistenceError ├── PersistenceNotFound ├── PersistenceEncryptionError └── PersistenceDecryptionError RuntimeError ├── filelock.LockError (if portalocker not available) └── (portalocker.exceptions.LockException if portalocker available) OSError └── osx.KeychainError (on macOS) ``` -------------------------------- ### Build Encrypted Persistence (Factory Function) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use the build_encrypted_persistence factory function to automatically select the appropriate platform-specific encrypted persistence implementation (Windows, macOS, or Linux). Handles unsupported platforms with a RuntimeError. ```python from msal_extensions import build_encrypted_persistence # Automatically selects platform-appropriate implementation try: persistence = build_encrypted_persistence("data.bin") # Returns FilePersistenceWithDataProtection (Windows) # Returns KeychainPersistence (macOS) # Returns LibsecretPersistence (Linux) except RuntimeError: print("Unsupported platform") ``` -------------------------------- ### Build Persistence with Encryption Fallback Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Creates a persistence object that attempts to use encrypted storage but falls back to plaintext FilePersistence if encryption dependencies are missing or an error occurs. Logs a warning when fallback is used. ```python from msal_extensions import ( build_encrypted_persistence, FilePersistence, PersistedTokenCache ) import logging def build_persistence_with_fallback(location): try: return build_encrypted_persistence(location) except ImportError: logging.warning("Encryption dependencies not available, using plaintext") return FilePersistence(location) except Exception as e: logging.warning(f"Encryption failed: {e}, using plaintext") return FilePersistence(location) persistence = build_persistence_with_fallback("token_cache.bin") cache = PersistedTokenCache(persistence) ``` -------------------------------- ### Initialize PersistedTokenCache with Default Lock Location Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Demonstrates creating a PersistedTokenCache instance using a default lock file location derived from the persistence file path. This is suitable for single-process or when default synchronization behavior is desired. ```python from msal_extensions import \ build_encrypted_persistence, PersistedTokenCache # Default lock location persistence = build_encrypted_persistence("token_cache.bin") cache = PersistedTokenCache(persistence) # Lock will be at "token_cache.bin.lockfile" ``` -------------------------------- ### Create Token Cache with Encryption Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use `build_encrypted_persistence` to automatically select the appropriate encryption based on the platform. This is suitable for securely storing tokens for MSAL applications. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal # Auto-select encryption based on platform persistence = build_encrypted_persistence("~/.myapp/token_cache.bin") # Create persisted cache cache = PersistedTokenCache(persistence) # Use with MSAL app = msal.PublicClientApplication( client_id="your_client_id", token_cache=cache ) ``` -------------------------------- ### Manage Multiple PersistedTokenCache Instances with Separate Locks Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Illustrates how to create and manage multiple independent token caches, each with its own persistence file and dedicated lock file. This is essential for applications handling multiple user accounts or distinct application contexts. ```python from msal_extensions import \ build_encrypted_persistence, PersistedTokenCache # Multiple locks for multiple caches user1_cache = PersistedTokenCache( build_encrypted_persistence("user1_cache.bin"), lock_location="/tmp/user1.lock" ) user2_cache = PersistedTokenCache( build_encrypted_persistence("user2_cache.bin"), lock_location="/tmp/user2.lock" ) ``` -------------------------------- ### Build Encrypted Token Cache Persistence Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/README.md Builds a suitable persistence instance for a token cache based on the current OS. It attempts to create an encrypted persistence and falls back to plain text if encryption is unavailable. ```python def build_persistence(location, fallback_to_plaintext=False): """Build a suitable persistence instance based your current OS""" try: return build_encrypted_persistence(location) except: if not fallback_to_plaintext: raise logging.warning("Encryption unavailable. Opting in to plain text.") return FilePersistence(location) persistence = build_persistence("token_cache.bin") print("Type of persistence: {}".format(persistence.__class__.__name__)) print("Is this persistence encrypted?", persistence.is_encrypted) cache = PersistedTokenCache(persistence) ``` -------------------------------- ### Build Encrypted Persistence for Multi-User Desktop App Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Creates a function to build a PersistedTokenCache for a specific user, allowing separate caches for different users in a multi-user desktop application. Initializes PublicClientApplications with user-specific token caches. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache import msal def get_cache_for_user(username): location = f"~/.myapp/cache/{username}.bin" persistence = build_encrypted_persistence(location) return PersistedTokenCache(persistence) # Each user gets separate cache user1_cache = get_cache_for_user("alice") user2_cache = get_cache_for_user("bob") app1 = msal.PublicClientApplication("client_id", token_cache=user1_cache) app2 = msal.PublicClientApplication("client_id", token_cache=user2_cache) ``` -------------------------------- ### Migrate MSAL Python Cache to MSAL Extensions Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/wiki/Migrate-MSAL-Python-Cache-to-MSAL-Extensions Use this code to migrate an existing MSAL Python cache file to the MSAL Extensions format. It reads the old cache, initializes the MSAL Extensions persistence layer with the same file path, saves the old cache content into the new format, and then initializes the MSAL Extensions cache object. ```python import os import msal from msal_extensions import build_persistence, PersistedTokenCache old_cache = msal.SerializableTokenCache() # MSAL Python Cache Object if os.path.exists("my_cache.bin"): old_cache.deserialize(open("my_cache.bin", "r").read()) # Read the existing cache into MSAL Python cache_object persistence = build_persistence("my_cache.bin") # MSAL Extensions Persistence layer initialization using same location persistence.save(old_cache.serialize()) # Perform save to make sure old cache is stored in new cache format new_cache = PersistedTokenCache(persistence) #Initialize MSAL Extensions cache object # Now you can use it in an msal application like this: app_2 = msal.PublicClientApplication(client_id='client_id', authority='authority', token_cache=new_cache) ``` -------------------------------- ### Search for Tokens in Cache Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Use `search` to retrieve tokens or account information from the cache. You can filter by credential type or account ID. ```python from msal_extensions import PersistedTokenCache cache = PersistedTokenCache(persistence) # Get all access tokens tokens = cache.search("access_token") # Get specific account's tokens tokens = cache.search( "access_token", home_account_id="account@tenant" ) # Get account information accounts = cache.search("account") ``` -------------------------------- ### Initialize LibsecretPersistence with Custom Schema and Attributes Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/configuration.md Configure LibsecretPersistence with a custom schema name and attributes for precise secret filtering and identification. ```python persistence = LibsecretPersistence( signal_location="~/.msal_cache_signal", schema_name="com.example.myapp.tokens", attributes={ "app": "myapp", "version": "1", "user": "alice@example.com" } ) ``` -------------------------------- ### Create Token Cache with Fallback to Plaintext Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md This snippet attempts to use encrypted persistence and falls back to plaintext storage if encryption fails. This provides a balance between security and compatibility. ```python from msal_extensions import build_encrypted_persistence, FilePersistence, PersistedTokenCache try: persistence = build_encrypted_persistence("~/.myapp/token_cache.bin") except Exception: persistence = FilePersistence("~/.myapp/token_cache.bin") cache = PersistedTokenCache(persistence) ``` -------------------------------- ### Customizing Lock File Location for Token Cache Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/api-reference/token-cache.md Shows how to initialize `PersistedTokenCache` with a custom location for its lock file, which is used to manage concurrent access. ```python from msal_extensions import build_encrypted_persistence, PersistedTokenCache persistence = build_encrypted_persistence("token_cache.bin") # Lock file will be at "token_cache.bin.lockfile" by default cache = PersistedTokenCache(persistence) # Or specify a custom lock location cache = PersistedTokenCache(persistence, lock_location="/tmp/my_lock") ``` -------------------------------- ### Recovery Strategy: Timeout and Continue (Risky) Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/errors.md This strategy proceeds without acquiring the lock if it cannot be obtained within a timeout. This is risky and may lead to data corruption. ```python import os import time from msal_extensions import CrossPlatLock, LockError # Strategy 3: Timeout and continue (risky) try: with CrossPlatLock("/tmp/cache.lock"): perform_operation() except LockError: print("Cannot acquire lock; continuing without lock (potential data corruption)") perform_operation() # Risky! ``` -------------------------------- ### Return Types Convention Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/types.md Outlines the return types and potential exceptions for `load()`, `save()`, `serialize()`, and `search()` methods. ```APIDOC ## Return Types **`load()` methods:** - **Return**: `str` — The persisted content as string - **Raise**: `PersistenceNotFound` if not initialized **`save()` methods:** - **Return**: `None` - **Raise**: Encryption error on failure **`serialize()`:** - **Return**: `str` — JSON representation of token cache - **Never raises** **`search()`:** - **Return**: `list` — List of matching credential dictionaries - **Empty list if no matches** ``` -------------------------------- ### Check Token Cache File Permissions Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/quick-reference.md Checks the file permissions of a token cache file and prints them in octal format. It notes that the expected permissions should be 0o600 (read/write for owner only) for security. ```python import os import stat cache_path = "token_cache.bin" if os.path.exists(cache_path): mode = stat.S_IMODE(os.stat(cache_path).st_mode) print(f"Permissions: {oct(mode)}") # Should be 0o600 (read/write for owner only) ``` -------------------------------- ### Handle macOS Keychain Errors Source: https://github.com/azuread/microsoft-authentication-extensions-for-python/blob/main/_autodocs/errors.md Demonstrates how to handle various KeychainError exceptions in macOS, including item not found on first use, user denied access, and generic errors. Requires importing Keychain and KeychainError from msal_extensions.osx. ```python from msal_extensions.osx import Keychain, KeychainError # Case 1: Item not found on first use try: with Keychain() as kc: password = kc.get_generic_password("msal", "account@tenant") except KeychainError as e: if e.exit_status == KeychainError.ITEM_NOT_FOUND: # Normal; initialize Keychain with Keychain() as kc: kc.set_generic_password("msal", "account@tenant", token_value) else: raise # Case 2: User denied access try: with Keychain() as kc: password = kc.get_generic_password("msal", "account@tenant") except KeychainError as e: if e.exit_status == KeychainError.ACCESS_DENIED: print("User denied Keychain access. Try again and grant permission.") else: raise # Case 3: Generic error with reference try: with Keychain() as kc: password = kc.get_generic_password("msal", "account@tenant") except KeychainError as e: print(f"Keychain error: {e.message}") # Message includes link to Apple MacErrors.h for unknown codes ```