Skip to content

Migrate from legacy SAML auth to Azure AD certificate auth.

Microsoft retired SAML/WS-Federation for SharePoint Online in May 2026 (MC1184649). The old with_user_credentials() method no longer works for SharePoint Online.

This example covers the migration path to Azure AD app-only with a certificate, which is the recommended replacement.

Prerequisites: - An app registered in Azure AD (or update an existing one) - openssl (for certificate generation) - Admin consent for SharePoint API permissions

See https://learn.microsoft.com/en-us/sharepoint/dev/security/saml-auth-retirement See https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread

View source

# ===========================================================================
# Step 1 -- Generate a self-signed certificate (run in terminal)
# ===========================================================================
#
#   # Create private key and self-signed certificate (365-day validity)
#   openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
#     -keyout private_key.pem -out cert.pem \
#     -subj "/CN=SharePointApp"
#
#   # Extract thumbprint (needed below)
#   openssl x509 -in cert.pem -fingerprint -noout \
#     | sed 's/.*=//' | sed 's/://g'
#
#   # Alternatively, convert an existing PFX from Azure:
#   openssl pkcs12 -in myapp.pfx -nocerts -nodes \
#     | openssl pkcs8 -topk8 -nocrypt -out private_key.pem
#   openssl pkcs12 -in myapp.pfx -nokeys -out cert.pem

# ===========================================================================
# Step 2 -- Register / update an app in Azure AD
# ===========================================================================
#
#   Option A -- Create a new app registration:
#     1. Go to https://entra.microsoft.com/ -> App registrations -> New registration
#     2. Name: e.g. "SharePoint Python Client"
#     3. Supported account types: "Accounts in this organizational directory only"
#     4. Do not set a redirect URI (this is app-only)
#     5. Click Register and note the Application (client) ID and Directory (tenant) ID
#
#   Option B -- Update an existing app:
#     1. Same app works -- just add a certificate credential under "Certificates & secrets"
#     2. No need to create a new app registration

# ===========================================================================
# Step 3 -- Grant SharePoint API permissions
# ===========================================================================
#
#   IMPORTANT: Grant SharePoint permissions, not Microsoft Graph permissions.
#   ClientContext uses the SharePoint REST API, which requires SharePoint
#   resource permissions.
#
#     1. In your app registration -> "API permissions"
#     2. Click "Add a permission" -> "APIs my organization uses"
#     3. Search for and select "SharePoint"
#     4. Select "Application permissions"
#     5. Choose at minimum: Sites.Read.All or Sites.FullControl.All
#     6. Click "Add permissions"
#     7. Click "Grant admin consent for [tenant]" and confirm
#
#   Verify that "SharePoint" (not "Microsoft Graph") appears among configured
#   permissions.

# ===========================================================================
# Step 4 -- Upload the certificate to the app registration
# ===========================================================================
#
#     1. In your app registration -> "Certificates & secrets"
#     2. Click "Upload certificate"
#     3. Select the cert.pem file from Step 1
#     4. Click "Add"
#     5. Copy the Thumbprint value (hex string, no colons)

# ===========================================================================
# Step 5 -- Connect and verify
# ===========================================================================

import argparse

from office365.sharepoint.client_context import ClientContext
from tests.settings import client_id, site_url, tenant


def main():
    parser = argparse.ArgumentParser(description="Migrate from legacy SAML auth to Azure AD certificate auth")
    parser.add_argument(
        "--thumbprint",
        default="AABBCCDDEEFF00112233445566778899AABBCCDD",
        help="certificate thumbprint",
    )
    parser.add_argument("--cert-path", default="./private_key.pem", help="path to the private key PEM file")
    args = parser.parse_args()

    ctx = ClientContext(site_url).with_client_certificate(
        tenant=tenant,
        client_id=client_id,
        thumbprint=args.thumbprint,
        cert_path=args.cert_path,
    )

    web = ctx.web.get().execute_query()
    print("Connected to: {0}".format(web.url))
    print("Site title: {0}".format(web.title))


if __name__ == "__main__":
    main()

# ===========================================================================
# What changed from the old SAML approach
# ===========================================================================
#
#   OLD (retired -- do not use):
#     ctx = ClientContext(url).with_user_credentials(username, password)
#
#   NEW (recommended):
#     ctx = ClientContext(url).with_client_certificate(
#         tenant, client_id, thumbprint, cert_path
#     )
#
#   Benefits of certificate auth over SAML:
#     - No username/password to rotate
#     - No MFA or conditional access issues (app-only)
#     - Aligned with Microsoft's long-term auth strategy
#     - Works with Sovereign clouds (GCC High, 21Vianet, etc.)

# ===========================================================================
# Alternative: MSAL ROPC (requires delegated user context)
# ===========================================================================
#
#   If your code needs user-specific (delegated) access rather than app-only:
#
#     from office365.sharepoint.client_context import ClientContext
#     from tests.settings import client_id, site_url, tenant, username
#
#     ctx = ClientContext(site_url).with_username_and_password(
#         tenant=tenant,
#         client_id=client_id,
#         username=username,
#         password="***",
#     )
#
#   Limitations of ROPC flow:
#     - Does not support MFA
#     - May be blocked by conditional access policies
#     - Tenant must allow public client flows in the app manifest
#     - Microsoft recommends certificate or interactive flows instead
#
#   See https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc

← Back to Modern