Webiny supports Microsoft Entra ID (formerly Azure AD) as a federated identity provider through Cognito Federation. Unlike Okta or Auth0 which replace Cognito entirely, Entra ID works alongside Cognito — users authenticate via Microsoft, but Cognito remains the user pool. Configure it by adding federation to the <Cognito /> extension in webiny.config.tsx with your Entra ID application's client ID, client secret, and issuer URL.
Before configuring Webiny, you need to register an application in the Microsoft Entra ID portal:
https://{domain}.auth.{region}.amazoncognito.com/oauth2/idpresponse)client_id
client_secret)https://login.microsoftonline.com/{tenant-id}/v2.0
| Value | Where to find it | Used as |
|---|---|---|
| Application (client) ID | App registration > Overview | client_id in providerDetails |
| Client secret value | App registration > Certificates & secrets | client_secret in providerDetails |
| Directory (tenant) ID | App registration > Overview | Part of oidc_issuer URL |
| Variable | Description |
|---|---|
ENTRA_CLIENT_ID |
Entra ID Application (client) ID |
ENTRA_CLIENT_SECRET |
Entra ID client secret value |
ENTRA_ISSUER |
https://login.microsoftonline.com/{tenant-id}/v2.0 |
When Cognito receives tokens from Entra ID, it maps the OIDC claims to Cognito user attributes. The default OIDC mapping is:
| Cognito Attribute | OIDC Claim | Description |
|---|---|---|
username |
sub |
Unique user identifier |
custom:id |
sub |
Webiny internal user ID |
email |
email |
User's email address |
given_name |
given_name |
First name |
family_name |
family_name |
Last name |
preferred_username |
email |
Used as the Cognito username alias |
You can override this mapping with the attributeMapping property on the identity provider config. This is useful when:
custom:id mapping (e.g., when the sub value exceeds the attribute's max length on existing pools){
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: { /* ... */ },
attributeMapping: {
username: "sub",
email: "email",
given_name: "given_name",
family_name: "family_name",
preferred_username: "email"
// custom:id intentionally omitted
}
}
When attributeMapping is provided, it replaces the defaults entirely — include all mappings you need.
Step 1: Set environment variables
Add to your .env file:
# NOTE: these are made up example values
ENTRA_CLIENT_ID=f62ee823-2811-8314-a040-62848442c0d5
ENTRA_CLIENT_SECRET=~Gp7Q~97MAzTAUyeTLzTVzX31DRTY28chehU6c_a
ENTRA_ISSUER=https://login.microsoftonline.com/1cd0d912-0ac4-48a0-91b6-cd849ce9498f/v2.0
Step 2: Create the extension
Create extensions/entraid/Extension.tsx:
import React from "react";
import { Cognito } from "@webiny/cognito";
export const CognitoFederation = () => {
return (
<Cognito
federation={{
domain: "my-app-entraid",
callbackUrls: ["http://localhost:3001"],
responseType: "code",
identityProviders: [
{
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: {
attributes_request_method: "POST",
authorize_scopes: "email profile openid",
client_id: String(process.env.ENTRA_CLIENT_ID),
client_secret: String(process.env.ENTRA_CLIENT_SECRET),
oidc_issuer: String(process.env.ENTRA_ISSUER)
}
}
]
}}
/>
);
};
Step 3: Register in webiny.config.tsx
import { CognitoFederation } from "@/extensions/entraid/Extension.js";
export const Extensions = () => {
return (
<>
{/* Replace <Cognito /> with the federation extension */}
<CognitoFederation />
{/* ... other extensions ... */}
</>
);
};
Step 4: Deploy
# Deploy core first (creates Cognito IdP resources)
yarn webiny deploy core --env=dev
# Get the Cognito domain for Entra ID redirect URI config
yarn webiny output core --env=dev
# Look for cognitoUserPoolDomain — use it to update the redirect URI in Entra ID
# Deploy API + Admin
yarn webiny deploy api --env=dev
yarn webiny deploy admin --env=dev
Step 5: Update Entra ID redirect URI
After the first deploy, update the redirect URI in your Entra ID app registration to:
https://{cognitoUserPoolDomain}/oauth2/idpresponse
<Cognito
federation={{
domain: "my-app-entraid",
callbackUrls: ["http://localhost:3001", "https://admin.example.com"],
responseType: "code",
allowCredentialsLogin: false,
identityProviders: [
{
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: {
attributes_request_method: "POST",
authorize_scopes: "email profile openid",
client_id: String(process.env.ENTRA_CLIENT_ID),
client_secret: String(process.env.ENTRA_CLIENT_SECRET),
oidc_issuer: String(process.env.ENTRA_ISSUER)
}
}
]
}}
/>
This hides the email/password form and shows only the "Sign in with Microsoft" button with a description that the user will be redirected.
Map Entra ID groups (via Cognito groups or token claims) to Webiny roles:
// webiny.config.tsx
<CognitoFederation />
// where CognitoFederation includes:
// apiConfig={"@/extensions/entraid/api.ts"}
// extensions/entraid/api.ts
import { CognitoIdpConfig } from "@webiny/cognito/api";
class EntraIdConfig implements CognitoIdpConfig.Interface {
getIdentity(token: CognitoIdpConfig.JwtPayload) {
const groups: string[] = (token["cognito:groups"] as string[]) || [];
return {
roles: groups.includes("WebinyAdmins") ? ["full-access"] : ["content-editor"],
teams: groups.filter(g => g.startsWith("team-"))
};
}
}
export default CognitoIdpConfig.createImplementation({
implementation: EntraIdConfig,
dependencies: []
});
<Cognito
federation={{
domain: "mycompany-webiny",
callbackUrls: ["http://localhost:3001", "https://admin.mycompany.com"],
logoutUrls: ["http://localhost:3001", "https://admin.mycompany.com"],
responseType: "code",
allowCredentialsLogin: false,
identityProviders: [
{
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: {
attributes_request_method: "POST",
authorize_scopes: "email profile openid",
client_id: String(process.env.ENTRA_CLIENT_ID),
client_secret: String(process.env.ENTRA_CLIENT_SECRET),
oidc_issuer: String(process.env.ENTRA_ISSUER)
}
}
]
}}
/>
Remember to add all callback URLs to your Entra ID app registration's redirect URIs.
Override the default claim mapping — useful for existing Cognito pools where custom:id has a max length of 36 characters (Entra ID sub values can be longer):
<Cognito
federation={{
domain: "mycompany-webiny",
callbackUrls: ["http://localhost:3001"],
identityProviders: [
{
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: {
attributes_request_method: "POST",
authorize_scopes: "email profile openid",
client_id: String(process.env.ENTRA_CLIENT_ID),
client_secret: String(process.env.ENTRA_CLIENT_SECRET),
oidc_issuer: String(process.env.ENTRA_ISSUER)
},
attributeMapping: {
username: "sub",
email: "email",
given_name: "given_name",
family_name: "family_name",
preferred_username: "email"
}
}
]
}}
/>
Add TOTP-based MFA on top of Entra ID federation:
<Cognito
mfa={true}
federation={{
domain: "mycompany-webiny",
callbackUrls: ["http://localhost:3001"],
allowCredentialsLogin: false,
identityProviders: [
{
name: "EntraID",
type: "oidc",
label: "Sign in with Microsoft",
providerDetails: {
attributes_request_method: "POST",
authorize_scopes: "email profile openid",
client_id: String(process.env.ENTRA_CLIENT_ID),
client_secret: String(process.env.ENTRA_CLIENT_SECRET),
oidc_issuer: String(process.env.ENTRA_ISSUER)
},
attributeMapping: {
"custom:id": "sub",
username: "sub",
email: "email",
given_name: "given_name",
family_name: "family_name",
preferred_username: "email"
}
}
]
}}
/>
MFA applies to password-based logins. Federated sign-ins via Entra ID are handled by Microsoft's own authentication — configure MFA on the Entra ID side if needed for those users.
import { Cognito } from "@webiny/cognito";
import { CognitoIdpConfig } from "@webiny/cognito/api"; // for API config
import { CognitoSignInConfig } from "@webiny/cognito/admin"; // for Admin config
extensions/entraid/
├── Extension.tsx # Extension component with <Cognito federation={...} />
├── api.ts # API config (role mapping) — optional
└── admin.tsx # Admin config (login customization) — optional
yarn webiny deploy core --env=dev — creates Cognito IdP resourcescognitoUserPoolDomain outputyarn webiny deploy api --env=dev — deploys API with identity mappingyarn webiny deploy admin --env=dev — deploys admin with login screen