███████╗███████╗ ██████╗ ██╗ ██████╗
██╔════╝██╔════╝██╔═══██╗ ██╔╝ ██╔══██╗
███████╗███████╗██║ ██║ ██╔╝ ██║ ██║
╚════██║╚════██║██║ ██║ ██╔╝ ██║ ██║
███████║███████║╚██████╔╝██╔╝ ██████╔╝
╚══════╝╚══════╝ ╚═════╝ ╚═╝ ╚═════╝
Microsoft Entra ID — OAuth 2.0 SSO Integration
Enterprise-grade Single Sign-On using Microsoft Entra ID, OAuth 2.0 Authorization Code Flow, and Microsoft Graph API
Overview • Architecture • Setup • Screenshots • Concepts
This project implements a production-pattern OAuth 2.0 / OpenID Connect (OIDC) Single Sign-On flow using Microsoft Entra ID (formerly Azure Active Directory) as the Identity Provider.
A Python Flask web application authenticates users through Entra ID, exchanges authorization codes for access tokens using MSAL (Microsoft Authentication Library), and retrieves live user profile data from the Microsoft Graph API — the same architecture used by enterprise SaaS applications like Microsoft 365, Teams, and SharePoint.
- 🔐 OAuth 2.0 Authorization Code Flow — industry-standard, secure server-side auth
- 🛡️ CSRF Protection — state parameter validation on every callback
- 👤 Microsoft Graph API — live user profile retrieval post-authentication
- 🏢 Single Tenant — scoped to one Entra ID directory (enterprise pattern)
- 🔑 MSAL Integration — Microsoft's official auth library for Python
┌─────────────┐ ┌──────────────────┐ ┌─────────────────────┐
│ │ 1.Login │ │ 2.Auth │ │
│ User │────────▶│ Flask App │────────▶│ Microsoft Entra ID │
│ (Browser) │ │ (localhost:5000)│ │ (login.microsoft │
│ │ │ │◀────────│ online.com) │
└─────────────┘ │ │ 3.Code └─────────────────────┘
│ │
│ │ 4.Exchange code → Token
│ │────────────────────────▶ Entra ID
│ │◀──────────────────────── Access Token
│ │
│ │ 5. GET /me (with token)
│ │────────────────────────▶ ┌─────────────┐
│ │◀──────────────────────── │ Microsoft │
│ │ User Profile JSON │ Graph API │
└──────────────────┘ └─────────────┘
| Layer | Technology |
|---|---|
| Identity Provider | Microsoft Entra ID |
| Auth Protocol | OAuth 2.0 + OpenID Connect (OIDC) |
| Auth Library | MSAL for Python v1.36 |
| Backend | Python 3.9+ / Flask 3.1 |
| User Data | Microsoft Graph API v1.0 |
| Config | python-dotenv |
- Python 3.9 or higher
- A Microsoft account (free)
- Access to Microsoft Entra ID Portal
git clone https://github.com/YOUR_USERNAME/entra-sso-oauth2.git
cd entra-sso-oauth2- Go to entra.microsoft.com → App registrations → New registration
- Name:
My SSO App| Account type: Single tenant | Redirect URI:http://localhost:5000/callback - Copy your Application (client) ID and Directory (tenant) ID from Overview
- Go to Certificates & secrets → New client secret → copy the value
- Go to API permissions → Add:
openid,profile,email,User.Read→ Grant admin consent
cp .env.example .envEdit .env with your values:
CLIENT_ID=your-application-client-id
CLIENT_SECRET=your-client-secret-value
TENANT_ID=your-directory-tenant-id
REDIRECT_URI=http://localhost:5000/callback
FLASK_SECRET_KEY=your-random-secret-keypython -m venv venv
venv\Scripts\activate # Windows
# source venv/bin/activate # macOS/Linux
pip install -r requirements.txt
python app.pyhttp://localhost:5000
Click Sign in with Microsoft → authenticate → see your profile. ✅
The most secure OAuth flow for server-side apps. Unlike the deprecated Implicit Flow, the authorization code is exchanged server-side — the access token never touches the browser.
- ID Token (JWT) — proves who the user is (authentication)
- Access Token — grants permission to call APIs like Graph (authorization)
Every auth request generates a random state UUID stored in the session. The callback validates it matches — preventing cross-site request forgery attacks.
Only User.Read is requested — following the principle of least privilege. The app gets exactly what it needs, nothing more.
A unified REST API endpoint (https://graph.microsoft.com/v1.0/me) that returns the authenticated user's directory profile using the access token as a Bearer credential.
entra-sso-oauth2/
├── app.py # Main Flask application
├── requirements.txt # Python dependencies
├── .env.example # Environment variable template
├── .gitignore # Git ignore rules
├── README.md # This file
└── screenshots/ # Step-by-step project screenshots
├── 01_app_registration.png
├── 02_api_permissions.png
├── 03_env_file.png
├── 04_python_setup.png
├── 06_vscode_app.png
├── 07_flask_running.png
└── 08_sso_working.png
- Never commit
.env— it's in.gitignorefor a reason - Regenerate your
CLIENT_SECRETif it's ever exposed - Use a strong random
FLASK_SECRET_KEYin production - In production, serve over HTTPS only and use a WSGI server (Gunicorn, uWSGI)
This is Project 3 of 5 in my Microsoft Entra ID IAM learning path:
| # | Project | Level | Status |
|---|---|---|---|
| 1 | User & Group Management Automation | Beginner | ✅ |
| 2 | Conditional Access Policy Lab | Beginner–Intermediate | ✅ |
| 3 | App Registration & OAuth 2.0 SSO | Intermediate | ✅ Complete |
| 4 | Privileged Identity Management Dashboard | Advanced | 🔄 In Progress |
| 5 | Zero Trust Identity Governance Pipeline | Expert | ⏳ Upcoming |
MIT License — see LICENSE for details.
Built by Muhammad Ahmad | Microsoft Entra ID IAM Portfolio Project






