Configuring Microsoft Entra ID (formerly Azure AD) as a federated identity provider for Webiny projects using Cognito Federation. Use this skill when the developer asks about Entra ID, Azure AD, Microsoft SSO, OIDC with Microsoft, Microsoft login for Webiny, or configuring login.microsoftonline.com as an identity provider. Also relevant for tenant IDs, Entra application registration, or connecting Microsoft 365 accounts to Webiny.
84%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
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_idclient_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.0Step 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=devStep 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 configextensions/entraid/
├── Extension.tsx # Extension component with <Cognito federation={...} />
├── api.ts # API config (role mapping) — optional
└── admin.tsx # Admin config (login customization) — optionalyarn 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 screenwebiny/webiny-js
3892f01
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.