Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes
Sponsored Reserved space — layout preview until AdSense is connected

Python OAuth2 Azure AD Integration – Complete Guide

This tutorial walks you through registering an Azure AD app, using MSAL in a Flask project, and securely obtaining tokens to call Microsoft Graph.

Focus: python oauth2 azure ad

Sponsored

Sponsored Reserved space — layout preview until AdSense is connected

OAuth 2.0 and OpenID Connect with Microsoft Entra (Azure AD) – Python Integration Guide

Overview

OAuth 2.0 is the industry standard for delegated authorization. OpenID Connect (OIDC) builds on OAuth 2.0 to provide authentication – it returns an ID token that proves the user’s identity. Microsoft Entra (formerly Azure AD) implements both protocols and offers a simple way for Python applications to sign‑in users, obtain tokens, and call protected APIs such as Microsoft Graph.

This tutorial walks you through a complete, practical integration:

  1. Register an app in Azure AD.
  2. Install the required Python packages.
  3. Implement the OAuth 2.0 Authorization Code flow with MSAL.
  4. Retrieve and verify the OpenID Connect ID token.
  5. Call a protected API (Microsoft Graph) using the access token.

All code examples are ready to copy‑paste into a small Flask project.


Prerequisites

  • Python 3.8 or newer installed.
  • pip package manager.
  • An Azure account with permission to create an App Registration in Microsoft Entra.
  • Basic familiarity with HTTP requests and JSON.

Register an Application in Azure AD

Create a new app registration

  1. Sign in to the Azure portal → Azure Active DirectoryApp registrationsNew registration.
  2. Give the app a name (e.g., python‑oauth‑demo).
  3. Choose Supported account types – for a demo, select Accounts in this organizational directory only.
  4. After creation, note the Application (client) ID and Directory (tenant) ID.
  5. Under Certificates & secrets, click New client secret, add a description, set an expiration, and copy the generated secret value. Keep it safe – you’ll need it in code.

Configure Redirect URI

For a web app that runs locally, add a redirect URI such as:

Code snippet
http://localhost:5000/get_token

If you later deploy to a public URL, replace it with that address.

Set API permissions

  1. Go to API permissionsAdd a permissionMicrosoft Graph.
  2. Choose Delegated permissions and add at least User.Read (for basic profile) and openid profile email (required for OIDC).
  3. Click Grant admin consent (requires admin rights) or have a user consent at sign‑in time.

Install Required Python Packages

Code snippet
pip install msal requests pyjwt
  • msal – Microsoft Authentication Library for Python (handles token acquisition).
  • requests – simple HTTP client for calling Microsoft Graph.
  • pyjwt – verify the JWT signature of the ID token.

Implement OAuth 2.0 Authorization Code Flow

Step 1: Build the Authorization URL

The user is redirected to Azure AD’s endpoint. The URL must contain:

  • client_id – your app’s client ID.
  • response_type=code – indicates we want an authorization code.
  • redirect_uri – must match the one set in the portal.
  • scope – at minimum openid profile email.
  • state – optional but recommended to mitigate CSRF.
Code snippet
import urllib.parse

CLIENT_ID = "YOUR_CLIENT_ID"
TENANT_ID = "YOUR_TENANT_ID"
REDIRECT_URI = "http://localhost:5000/get_token"
SCOPES = ["openid", "profile", "email"]

def build_auth_url():
    params = {
        "client_id": CLIENT_ID,
        "response_type": "code",
        "redirect_uri": REDIRECT_URI,
        "scope": " ".join(SCOPES),
        "state": "random_state_string",   # generate securely in real code
        "response_mode": "query"
    }
    return f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/authorize?" + urllib.parse.urlencode(params)

Print or open this URL in a browser to start the sign‑in process.

Step 2: Handle the Callback

A minimal Flask app receives the code query parameter after the user consents.

Code snippet
from flask import Flask, request, redirect, session, jsonify
import msal

app = Flask(__name__)
app.secret_key = "replace_with_strong_secret"

# MSAL configuration
authority = f"https://login.microsoftonline.com/{TENANT_ID}"
app_config = {
    "client_id": CLIENT_ID,
    "client_secret": "YOUR_CLIENT_SECRET",   # keep this safe
    "authority": authority,
    "redirect_uri": REDIRECT_URI,
}
msal_app = msal.PublicClientApplication(app_config)

@app.route("/")
def index():
    # Generate a link and send the user to Azure AD
    auth_url = build_auth_url()
    return f'<a href="{auth_url}">Sign in with Microsoft</a>'

@app.route("/get_token")
def get_token():
    # Azure AD redirects here with ?code=...&state=...
    error = request.args.get("error")
    if error:
        return f"Error: {error}", 400

    code = request.args.get("code")
    if not code:
        return "Authorization code missing", 400

    # Exchange the code for tokens
    result = msal_app.acquire_token_by_authorization_code(
        code=code,
        scopes=SCOPES,
        redirect_uri=REDIRECT_URI,
    )

    if "access_token" in result:
        session["access_token"] = result["access_token"]
        session["id_token"] = result.get("id_token")
        return redirect("/profile")
    else:
        return f"Token acquisition error: {result.get('error_description')}", 400

Step 3: Use the Access Token to Call Microsoft Graph

With the access token stored in the session, we can call /me to fetch the signed‑in user’s profile.

Code snippet
import requests

@app.route("/profile")
def profile():
    access_token = session.get("access_token")
    if not access_token:
        return redirect("/")

    headers = {"Authorization": f"Bearer {access_token}"}
    graph_url = "https://graph.microsoft.com/v1.0/me"
    response = requests.get(graph_url, headers=headers)

    if response.status_code == 200:
        return jsonify(response.json())
    else:
        return f"Graph request failed: {response.text}", 500

Run the Flask app (flask run) and navigate to http://localhost:5000. Click Sign in with Microsoft, complete the consent screen, and you’ll be redirected back to /profile with a JSON payload containing the user’s name, email, and object ID.


OpenID Connect – Adding ID Token Verification

The ID token is a JWT that asserts the user’s identity. It contains claims such as sub (subject), iss (issuer), aud (audience), and exp (expiration). Verifying these claims ensures the token is genuine.

Install pyjwt

Code snippet
pip install pyjwt

Verify the ID token

Code snippet
import jwt
import json

def verify_id_token(id_token):
    # MSAL already validated the signature, but we double‑check the claims
    unverified_header = jwt.get_unverified_header(id_token)
    kid = unverified_header["kid"]
    # In a production app you would fetch the JWKS endpoint and cache the keys.
    # For this demo we use the built‑in Microsoft public keys endpoint.
    jwks_url = f"https://login.microsoftonline.com/{TENANT_ID}/discovery/v2.0/keys"
    jwks = requests.get(jwks_url).json()

    # Find the key that matches the kid
    key = None
    for key_dict in jwks["values"]:
        if key_dict["kid"] == unverified_header["kid"]:
            key = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(key_dict))
            break
    if not key:
        raise ValueError("Unable to find matching signing key")

    # Verify claims
    claims = jwt.decode(
        id_token,
        key=key,
        algorithms=[unverified_header["alg"]],
        audience=CLIENT_ID,
        issuer=f"https://login.microsoftonline.com/{TENANT_ID}/v2.0",
    )
    return claims

# Example usage after token acquisition
@app.route("/profile")
def profile():
    # ... (same as before) ...
    id_token = session.get("id_token")
    if id_token:
        try:
            claims = verify_id_token(id_token)
            return f"Signed in as {claims['name']} (Object ID: {claims['sub']})"
        except Exception as e:
            return f"ID token verification failed: {e}", 400
    return "No ID token in session"

The verify_id_token function:

  1. Retrieves Microsoft’s public JWKS (JSON Web Key Set) to obtain the signing key.
  2. Validates the token’s aud (must match the client ID) and iss (must be the expected issuer).
  3. Returns the decoded claims, which you can safely display or store.

Protecting a Web API with Azure AD (Optional)

If you want your own Flask API to accept only Azure AD‑authenticated calls, you can add the Authorization: Bearer <token> header check:

Code snippet
from functools import wraps

def require_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get("Authorization")
        if not token or not token.startswith("Bearer "):
            return jsonify({"error": "Missing or malformed token"}), 401
        try:
            # Use msal to validate the token (or pyjwt as above)
            claims = verify_id_token(token.split()[1])
            request.user = claims   # make user info available downstream
        except Exception as e:
            return jsonify({"error": str(e)}), 401
        return f(*args, **kwargs)
    return decorated

@app.route("/secure-data")
@require_auth
def secure_data():
    return jsonify({"message": f"Hello {request.user['name']}, this is protected data."})

Now any client that obtains a token from Azure AD can call /secure-data and receive a 401 if the token is missing or invalid.


Common Errors and Debugging

  • Invalid redirect_uri – Ensure the URI in the portal exactly matches the one used in the code (including scheme and port).
  • Authorization code not found – The code expires after a few minutes; retry quickly or increase the timeout in your app.
  • Insufficient scopes – If you request openid but omit profile or email, the ID token may be missing claims.
  • Token acquisition errors – Check result.get('error_description') for details (e.g., consent required, client secret expired).
  • JWT verification failures – Verify that the aud claim equals your client ID and that the iss matches the Azure AD tenant URL.

Summary

  • Register an app in Microsoft Entra, configure redirect URIs, and grant API permissions.
  • Use MSAL to implement the OAuth 2.0 Authorization Code flow in Python (Flask example).
  • Retrieve both access token and ID token; verify the ID token with pyjwt and Microsoft’s JWKS endpoint to ensure OIDC compliance.
  • Call protected resources (e.g., Microsoft Graph) with the access token.
  • Handle common pitfalls and secure your API with token validation.

With these steps, junior developers can quickly build secure, real‑world Python applications that leverage Azure AD for authentication and authorization.

Sponsored

Sponsored Reserved space — layout preview until AdSense is connected

Sponsored

Sponsored Reserved space — layout preview until AdSense is connected

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.