Microsoft Entra released Token Protection, a conditional-access policy that binds access tokens to the device they were issued on to harden against token theft. This is a welcome security improvement which brings some headaches. In particular, it breaks browser-based interactive login flows. The fix is to switch to broker-based authentication, which delegates login to the OS authentication broker (Web Account Manager on Windows, Company Portal on macOS, Microsoft Identity Broker on Linux). This post covers the error and the fix.
Trying to authenticate with Microsoft Graph using InteractiveBrowserCredential from the Azure Identity library:
from azure.identity import InteractiveBrowserCredential
credential = InteractiveBrowserCredential()
access_token = credential.get_token('https://graph.microsoft.com/.default').tokennow shows an error pop-up when Token Protection is enabled:
Sorry, a security policy is preventing access
An organization security policy requiring token protection is preventing this application from accessing the resource. You may be able to use a different application.
followed by an Authentication failed: access_denied exception.
The azure-identity-broker package delegates authentication to the OS broker, which produces device-bound tokens that satisfy Token Protection. Add it along with pywin32 (needed on Windows to retrieve a window handle) in requirements.txt (or equivalent):
azure-identity
azure-identity-broker
pywin32 ; sys_platform == "win32"Then replace InteractiveBrowserCredential with InteractiveBrowserBrokerCredential, passing it a window handle for the login popup:
import sys
from azure.identity.broker import InteractiveBrowserBrokerCredential
if sys.platform == "win32":
import win32gui
window_handle = win32gui.GetForegroundWindow()
else:
import msal
window_handle = msal.PublicClientApplication.CONSOLE_WINDOW_HANDLE
credential = InteractiveBrowserBrokerCredential(parent_window_handle=window_handle)
access_token = credential.get_token('https://graph.microsoft.com/.default').tokenOne open issue: broker-based authentication requires a windowing system, which is not available in Docker containers, including VSCode Dev Containers. If someone has a solution, potentially using VSCode’s built-in authentication proxy, I am interested…
The companion notebook with the full code sample is at github.com/mmaitre314/brokered-auth.