Set Up SSO with Microsoft Entra ID (Azure AD)
OpenID Connect (OIDC) adds an identity layer to OAuth 2.0, allowing applications to verify an end user's identity and obtain basic profile information from an identity provider (IdP). In a single sign-on (SSO) deployment, users authenticate through the IdP and can access connected applications without signing in separately to each one.
Microsoft Entra ID, formerly Azure Active Directory, is Microsoft's cloud-based identity and access management service. It can serve as the centralized IdP for workforce users and provides capabilities such as SSO, multi-factor authentication, and access policies. In this integration, Apache APISIX delegates browser authentication to Microsoft Entra ID before proxying requests to an upstream service.
The guide shows how to configure APISIX and Microsoft Entra ID for the OIDC Authorization Code flow with Proof Key for Code Exchange (PKCE). When a request does not have a valid APISIX session, APISIX redirects the browser to Microsoft Entra ID. After successful authentication, APISIX exchanges the authorization code for tokens, creates a browser session, and resumes the original request.
Prerequisite(s)
- Install Docker.
- Install cURL and OpenSSL.
- Follow the Getting Started tutorial to start APISIX with Docker.
- Have access to a Microsoft Entra tenant and permission to register applications. The Application Developer role or a higher-privileged role is sufficient.
- If you plan to use ADC, install and configure ADC before continuing.
Configure Microsoft Entra ID
Register an OIDC web application in Microsoft Entra ID, then save its tenant and client credentials for the APISIX route.
Register an Application
Sign in to the Microsoft Entra admin center. Select Entra ID → App registrations → New registration, and configure the application:
- Enter
APISIX Authorization Codeas the application name. - Under Supported account types, select Single tenant only for this example.
- Under Redirect URI, select Web and enter
http://localhost:9080/anything/user/callback. - Select Register.

The redirect URI identifies the APISIX endpoint where Microsoft Entra ID returns the browser after authentication. In production, use an HTTPS endpoint that users can reach and register that exact URI in Microsoft Entra ID.
Create a Client Secret
On the registered application's Overview page, record the Application (client) ID and Directory (tenant) ID. Then select Certificates & secrets → Client secrets → New client secret. Enter a description, select an expiration period that follows the organization's credential-rotation policy, and select Add.
Copy the client secret Value immediately. Microsoft Entra ID displays it only once. Do not use the Secret ID as the client secret.
This example uses a client secret for local testing. For production applications, Microsoft recommends using a certificate credential instead.

Save the tenant ID, client ID, client secret, and discovery URL to environment variables, replacing the example values:
export ENTRA_TENANT_ID=replace-with-your-tenant-id
export ENTRA_CLIENT_ID=replace-with-your-client-id
export ENTRA_CLIENT_SECRET=replace-with-your-client-secret
export ENTRA_DISCOVERY="https://login.microsoftonline.com/${ENTRA_TENANT_ID}/v2.0/.well-known/openid-configuration"
Configure APISIX
Configure a route that authenticates browser requests before forwarding them to httpbin.org, a public HTTP request and response service. The /anything/user/* endpoint returns request details for verification.
Generate a unique secret that APISIX will use to encrypt and authenticate the browser session cookie:
export APISIX_SESSION_SECRET="$(openssl rand -hex 32)"
Choose either the Admin API or ADC to configure the route.
- Admin API
- ADC
Create a route that authenticates browser requests with Microsoft Entra ID:
curl "http://127.0.0.1:9180/apisix/admin/routes/entra-browser" -X PUT \
--data-binary @- <<EOF
{
"uri": "/anything/user/*",
"plugins": {
"openid-connect": {
"client_id": "$ENTRA_CLIENT_ID",
"client_secret": "$ENTRA_CLIENT_SECRET",
"discovery": "$ENTRA_DISCOVERY",
"scope": "openid profile",
"redirect_uri": "http://localhost:9080/anything/user/callback",
"bearer_only": false,
"use_pkce": true,
"session": {
"secret": "$APISIX_SESSION_SECRET"
},
"set_access_token_header": false,
"set_id_token_header": false,
"set_userinfo_header": false
},
"proxy-rewrite": {
"headers": {
"remove": ["Cookie"]
}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}
EOF
❶ discovery: URI of the tenant-specific Microsoft Entra ID OIDC discovery document.
❷ redirect_uri: URI where Microsoft Entra ID returns the browser after authentication. It must match the redirect URI registered for the application.
❸ bearer_only: Set to false so that APISIX starts the browser authentication flow when a request does not have a valid session.
❹ use_pkce: Set to true to send an S256 PKCE challenge during authorization.
❺ set_access_token_header, set_id_token_header, and set_userinfo_header: Set to false to prevent APISIX from adding the access token, ID token, and user information to upstream request headers.
❻ proxy-rewrite.headers.remove: Includes Cookie to remove the entire header before proxying the request, including the APISIX session cookie. Review this setting if the upstream application requires cookies.
Create an adc.yaml file with the same route configuration:
services:
- name: entra-sso
routes:
- name: entra-browser
uris:
- /anything/user/*
plugins:
openid-connect:
client_id: "${ENTRA_CLIENT_ID}"
client_secret: "${ENTRA_CLIENT_SECRET}"
discovery: "${ENTRA_DISCOVERY}"
scope: openid profile
redirect_uri: http://localhost:9080/anything/user/callback
bearer_only: false
use_pkce: true
session:
secret: "${APISIX_SESSION_SECRET}"
set_access_token_header: false
set_id_token_header: false
set_userinfo_header: false
proxy-rewrite:
headers:
remove:
- Cookie
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
❶ discovery: URI of the tenant-specific Microsoft Entra ID OIDC discovery document.
❷ redirect_uri: URI where Microsoft Entra ID returns the browser after authentication. It must match the redirect URI registered for the application.
❸ bearer_only: Set to false so that APISIX starts the browser authentication flow when a request does not have a valid session.
❹ use_pkce: Set to true to send an S256 PKCE challenge during authorization.
❺ set_access_token_header, set_id_token_header, and set_userinfo_header: Set to false to prevent APISIX from adding the access token, ID token, and user information to upstream request headers.
❻ proxy-rewrite.headers.remove: Includes Cookie to remove the entire header before proxying the request, including the APISIX session cookie. Review this setting if the upstream application requires cookies.
ADC reconciles services as desired state. The label selector limits this example to its own labeled resources. Preview the scoped changes and confirm that they contain no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=entra-sso
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=entra-sso
The file keeps the client secret as an environment-variable reference. Do not add credentials to the file or commit them to source control.
Verify Authentication
Navigate to http://localhost:9080/anything/user/get in a browser. APISIX redirects you to Microsoft Entra ID. If the browser does not have an active Microsoft Entra session, Microsoft prompts you to sign in.

The sign-in page can use organization-specific logos, colors, and other visual elements. See Configure company branding for requirements and configuration options.
Complete the Microsoft Entra sign-in if prompted. After successful authentication, Microsoft Entra ID returns the browser to APISIX, and APISIX forwards the request to httpbin.org. You should see a response with fields similar to these:
{
"args": {},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "en-CA,en-US;q=0.9,en;q=0.8",
"Host": "localhost",
"Priority": "u=0, i",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "cross-site",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15",
"X-Amzn-Trace-Id": "Root=1-...",
"X-Forwarded-Host": "localhost:9080"
},
"json": null,
"method": "GET",
"origin": "192.168.155.1, xxx.xxx.xxx.xxx",
"url": "http://localhost:9080/anything/user/get"
}
Header values and the reported origin address vary by browser and network environment.
The upstream request headers should not include the APISIX session cookie, access token, ID token, or user-information header. Reload the page to verify that APISIX reuses the browser session without redirecting you to Microsoft Entra ID.
Next Steps
You have now configured APISIX to authenticate browser requests with Microsoft Entra ID. See the openid-connect plugin reference for more configuration options.