OAuth 2.0 Flows
This document provides an illustration of OAuth 2.0 authentication flows supported by Microsoft Entra ID.
Note: All the following implementation steps are for illustration purposes. It's always recommended to use a library to handle the authentication flow.
Authorization Code Flow
Flow Steps
1. User → App: Navigate to app's web UI
2. App → User: Redirect to Microsoft login
3. User → Entra ID: Authenticate & consent
4. Entra ID → App: Authorization code (via redirect URI)
5. App → Entra ID: Exchange code for tokens (with client secret)
6. Entra ID → App: Access token + refresh token + ID token
7. App → API: Call API with access tokenImplementation Steps
#### 1. Build Authorization URL
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={application_id}
&response_type=code
&redirect_uri={redirect_uri}
&response_mode=query
&scope={scopes}
&state={random_state}Parameters:
tenant: Your tenant ID orcommonfor multi-tenantclient_id: Application (client) ID from app registrationredirect_uri: Must match exactly what's registeredscope: Space-separated permissions (e.g.,openid profile User.Read)state: Random value to prevent CSRF attacks
#### 2. User Authenticates
User is redirected to Microsoft login page, authenticates, and grants consent.
#### 3. Receive Authorization Code
App receives callback at redirect URI:
https://your-app.com/callback?
code={authorization_code}
&state={state_value}Validation:
- Verify
statematches what you sent - Extract
codeparameter
#### 4. Exchange Code for Tokens
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&code={authorization_code}
&redirect_uri={redirect_uri}
&grant_type=authorization_code
&client_secret={client_secret}Response:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}#### 5. Use Access Token
GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer {access_token}Authorization Code Flow with PKCE
PKCE (Proof Key for Code Exchange) adds security for public clients that cannot securely store a client secret.
Flow Steps
1. App: Generate code verifier (random string)
2. App: Generate code challenge (SHA256 hash of verifier)
3. App → Entra ID: Authorization request with code challenge
4. User → Entra ID: Authenticate & consent
5. Entra ID → App: Authorization code
6. App → Entra ID: Exchange code + code verifier for token
7. Entra ID: Validates verifier matches challenge
8. Entra ID → App: Access token + ID tokenImplementation Steps
#### 1. Generate PKCE Values
Code Verifier: 43-128 character random string
// JavaScript example
const codeVerifier = generateRandomString(128);Code Challenge: Base64URL-encoded SHA256 hash of verifier
const codeChallenge = base64URLEncode(sha256(codeVerifier));#### 2. Build Authorization URL
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id={application_id}
&response_type=code
&redirect_uri={redirect_uri}
&scope={scopes}
&state={state}
&code_challenge={code_challenge}
&code_challenge_method=S256#### 3. Exchange Code for Tokens (No Secret)
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&code={authorization_code}
&redirect_uri={redirect_uri}
&grant_type=authorization_code
&code_verifier={code_verifier}Client Credentials Flow
Flow Steps
1. App → Entra ID: Request token with client ID + secret
2. Entra ID: Validate credentials
3. Entra ID → App: Access token (application permissions)
4. App → API: Call API with tokenImplementation Steps
#### 1. Configure Application Permissions
In app registration:
- Go to "API permissions"
- Add Application permissions (not delegated)
- Grant admin consent (required for app permissions)
Example permissions:
User.Read.All(application) - Read all usersDirectory.Read.All(application) - Read directory
#### 2. Request Access Token
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope=https://graph.microsoft.com/.default
&client_secret={client_secret}
&grant_type=client_credentialsParameters:
scope: Use{resource}/.defaultformat- For Microsoft Graph:
https://graph.microsoft.com/.default - For your API:
api://{api_app_id}/.default
Response:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3599
}#### 3. Use Access Token
GET https://graph.microsoft.com/v1.0/users
Authorization: Bearer {access_token}Device Code Flow
Use for: Devices without browsers (IoT, CLIs), headless environments
Flow Steps
1. App → Entra ID: Request device code
2. Entra ID → App: Device code + user code + verification URL
3. App → User: Display code and URL
4. User: Opens URL on another device, enters code
5. User → Entra ID: Authenticates & consents
6. App → Entra ID: Poll for token
7. Entra ID → App: Access token (after user completes auth)Implementation Steps
#### 1. Request Device Code
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/devicecode
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}Response:
{
"user_code": "GTHK-QPMN",
"device_code": "GMMhmHCXhWEzkobqIHGG_EnNYYsAkukHspeYUk9E8",
"verification_uri": "https://microsoft.com/devicelogin",
"expires_in": 900,
"interval": 5,
"message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code GTHK-QPMN to authenticate."
}#### 2. Display Instructions to User
To sign in, open https://microsoft.com/devicelogin
and enter code: GTHK-QPMN#### 3. Poll for Token
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&grant_type=urn:ietf:params:oauth:grant-type:device_code
&device_code={device_code}Poll every 5 seconds (use interval from response)
Pending Response (user hasn't completed auth yet):
{
"error": "authorization_pending",
"error_description": "AADSTS70016: Pending end-user authorization..."
}Success Response:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}Refresh Token Flow
Use for: Refreshing expired access tokens without re-authentication
When to Refresh
- Access tokens typically expire in 1 hour
- Refresh tokens are long-lived (14-90 days)
- Refresh before access token expires for seamless UX
Implementation
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded
client_id={application_id}
&scope={scopes}
&refresh_token={refresh_token}
&grant_type=refresh_token
&client_secret={client_secret}Note: client_secret only required for confidential clients
Response:
{
"access_token": "eyJ0eXAi...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "M.R3_BAY...",
"id_token": "eyJ0eXAi..."
}Important: New refresh token is returned; use it for next refresh
Token Types
Access Token
- Used to call APIs
- Contains claims (user ID, permissions, etc.)
- Short-lived (typically 1 hour)
- Format: JWT (JSON Web Token)
Sample claims:
{
"aud": "https://graph.microsoft.com",
"iss": "https://sts.windows.net/{tenant}/",
"sub": "{user_object_id}",
"scp": "User.Read Mail.Read",
"exp": 1680000000
}Refresh Token
- Used to get new access tokens
- Long-lived (days to months)
- Opaque string (not JWT)
- Single-use (new one issued with each refresh)
ID Token
- Contains user identity information
- Used by the app to authenticate user
- Format: JWT
Sample claims:
{
"sub": "{user_object_id}",
"name": "Jane Doe",
"preferred_username": "[email protected]",
"email": "[email protected]",
"oid": "{object_id}"
}Scopes and Permissions
Scope Format
Microsoft Graph:
https://graph.microsoft.com/User.Read
https://graph.microsoft.com/Mail.SendCustom API:
api://{api_application_id}/access_as_userSecurity Considerations
| Practice | Why |
|---|---|
| Use state parameter | Prevents CSRF attacks |
| Use PKCE for public clients | Prevents authorization code interception |
| Validate tokens | Verify signature, issuer, audience, expiration |
| Use HTTPS only | Protect tokens in transit |
| Store tokens securely | Use secure storage, never in localStorage for sensitive apps |
| Implement token refresh | Seamless UX without repeated logins |
| Handle token expiration | Gracefully refresh or re-authenticate |
| Minimal scope principle | Request only necessary permissions |