Microsoft Graph Authentication¶
Authentication flows for the Graph client — pick the one that matches who your app runs as and where it runs.
Which flow should I use?¶
flowchart TD
A[Who will sign in?] --> B[An application / daemon]
A --> C[A user]
B --> D{Where will it run?}
D -->|Production| E[Client certificate
more secure]
D -->|Development / simple| F[Client secret
simplest setup]
C --> G{User present to interact?}
G -->|Yes| H[Interactive auth
supports MFA, SSO]
G -->|No| I{Has a browser?}
I -->|Yes, visit URL| N[Device code flow
headless CLI, SSH,
remote server]
I -->|No browser at all| O["ROPC (password grant)
no MFA, legacy"]
E --> J[with_client_cert.py]
F --> K[with_client_secret.py]
H --> L[interactive.py]
N --> P[with_device_flow.py]
O --> M[with_user_creds.py]
style A fill:#1a73e8,color:#fff
style B fill:#e8f0fe
style C fill:#e8f0fe
style E fill:#fff3cd
style F fill:#fff3cd
style H fill:#d4edda
style I fill:#f8d7da
App-only (application permissions)¶
Client secret¶
App-only access for daemons, cron jobs, and CI/CD — simplest setup, no user involved.
client = GraphClient(tenant="contoso.onmicrosoft.com").with_client_secret(
client_id="<client_id>", client_secret="<client_secret>"
)
site = client.sites.root.get().execute_query()
print(site.web_url)
Client certificate¶
App-only access for production daemons — X.509 certificate instead of a shared secret.
app = msal.ConfidentialClientApplication(
client_id,
authority="https://login.microsoftonline.com/contoso.onmicrosoft.com",
client_credential={"thumbprint": cert_thumbprint, "private_key": open("cert.pem").read()},
)
token = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
client = GraphClient(token)
for drive in client.drives.get().top(10).execute_query():
print(drive.web_url)
User sign-in (delegated permissions)¶
Interactive¶
User sign-in with a browser prompt — supports MFA, SSO, and consent.
client = GraphClient(tenant="contoso.onmicrosoft.com").with_token_interactive(client_id="<client_id>")
me = client.me.get().execute_query()
print(f"Welcome, {me.given_name}!")
Device code flow¶
Headless CLI, SSH, and remote servers — the user visits a URL on another device.
client = GraphClient(tenant="contoso.onmicrosoft.com").with_device_flow(client_id="<client_id>")
me = client.me.get().execute_query()
print(f"Authenticated as: {me.user_principal_name}")
Username & password (ROPC)¶
User context without interactivity — no MFA, legacy flow (Resource Owner Password Credentials).
client = GraphClient(tenant="contoso.onmicrosoft.com").with_username_and_password(
client_id="<client_id>", username="<user>", password="<password>"
)
me = client.me.get().execute_query()
print(me)
Custom token callback¶
Bring your own token acquisition — secrets vault, managed identity, or a custom identity provider.
def acquire_token() -> dict:
app = msal.ConfidentialClientApplication(
client_id, client_credential=client_secret,
authority="https://login.microsoftonline.com/contoso.onmicrosoft.com",
)
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
if not result or "access_token" not in result:
raise RuntimeError(f"Token acquisition failed: {result}")
return result
client = GraphClient(tenant="contoso.onmicrosoft.com", token_callback=acquire_token)
org = client.organization.get().execute_query()
Special environments¶
Microsoft Entra External ID (CIAM)¶
Customer identity / External ID tenants — connect via the ciamlogin.com authority.
authority = "https://contoso.ciamlogin.com"
client = GraphClient(tenant="contoso.onmicrosoft.com", authority=authority).with_client_secret(
client_id="<client_id>", client_secret="<client_secret>"
)
org = client.organization.get().execute_query()
National clouds¶
Sovereign clouds (GCC High, DoD, China) via AzureEnvironment — applies to any flow above.
from office365.azure_env import AzureEnvironment
client = GraphClient(
tenant="contoso.onmicrosoft.com", environment=AzureEnvironment.USGovernmentHigh
).with_client_secret(client_id="<client_id>", client_secret="<client_secret>")
org = client.organization.get().execute_query()
National cloud environments¶
| Environment | AzureEnvironment |
|---|---|
| Global | Global |
| US Government GCC | USGovernment |
| US Government GCC High | USGovernmentHigh |
| US Government DoD | USGovernmentDoD |
| China | China |
| Germany (legacy) | Germany |
Best practice: verify permissions upfront¶
Guard against missing permissions or licenses before making a call:
client = (
GraphClient(tenant="contoso.onmicrosoft.com")
.with_client_secret(client_id="<client_id>", client_secret="<client_secret>")
.require_application_permission("DeviceManagementConfiguration.Read.All")
.require_delegated_permission("User.Read", "User.ReadWrite.All")
.require_license("DEVELOPERPACK_E5")
)