Configure Microsoft Entra ID Authentication

SkillCloud & infra

Guides your agent to set up Microsoft sign-in for Webiny projects using Entra ID.

Available today. Use it from your connected AI after setup.

Add ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.

Then ask your AI: use the Configure Microsoft Entra ID Authentication skill

About this skill

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

What this skill tells your AI

The instructions your AI receives, as published by webiny/webiny-js in skills/user-skills/configure-entraid/SKILL.md and read by ahel’s review.

TL;DR

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.

Prerequisites

Before configuring Webiny, you need to register an application in the Microsoft Entra ID portal:

  1. Go to Microsoft Entra admin center > App registrations > New registration
  2. Set a name (e.g., "Webiny Admin")
  3. Set Supported account types (typically "Accounts in this organizational directory only")
  4. Set Redirect URI: Web — use your Cognito domain callback URL (you'll get this after the first deploy: https://{domain}.auth.{region}.amazoncognito.com/oauth2/idpresponse)
  5. After registration, note:
    • Application (client) ID — this is your client_id
    • Directory (tenant) ID — part of your issuer URL
  6. Go to Certificates & secrets > New client secret — note the Value (this is your client_secret)
  7. The Issuer URL is: https://login.microsoftonline.com/{tenant-id}/v2.0

Reference Tables

Required Entra ID Values

ValueWhere to find itUsed as
Application (client) IDApp registration > Overviewclient_id in providerDetails
Client secret valueApp registration > Certificates & secretsclient_secret in providerDetails
Directory (tenant) IDApp registration > OverviewPart of oidc_issuer URL

Environment Variables

VariableDescription
ENTRA_CLIENT_IDEntra ID Application (client) ID
ENTRA_CLIENT_SECRETEntra ID client secret value
ENTRA_ISSUERhttps://login.microsoftonline.com/{tenant-id}/v2.0

Attribute Mapping

When Cognito receives tokens from Entra ID, it maps the OIDC claims to Cognito user attributes. The default OIDC mapping is:

Cognito AttributeOIDC ClaimDescription
usernamesubUnique user identifier
custom:idsubWebiny internal user ID
emailemailUser's email address
given_namegiven_nameFirst name
family_namefamily_nameLast name
preferred_usernameemailUsed as the Cognito username alias

You can override this mapping with the attributeMapping property on the identity provider config. This is useful when:

  • Your Entra ID uses non-standard claim names
  • You want to skip custom:id mapping (e.g., when the sub value exceeds the attribute's max length on existing pools)
  • You need to map additional custom attributes
{
    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.

Full Examples

Example 1: Basic Entra ID Federation

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

Example 2: Entra ID Only (No Password Login)

<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.

Example 3: Entra ID with Custom Role Mapping

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: []
});

Example 4: Production Setup with Multiple Callback URLs

<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.

Example 5: Custom Attribute Mapping

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"
        }
      }
    ]
  }}
/>

Example 6: Entra ID with MFA

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.

Quick Reference

Imports

import { Cognito } from "@webiny/cognito";
import { CognitoIdpConfig } from "@webiny/cognito/api"; // for API config
import { CognitoSignInConfig } from "@webiny/cognito/admin"; // for Admin config

File Structure

extensions/entraid/
├── Extension.tsx       # Extension component with <Cognito federation={...} />
├── api.ts              # API config (role mapping) — optional
└── admin.tsx           # Admin config (login customization) — optional

Deploy Order

  1. yarn webiny deploy core --env=dev — creates Cognito IdP resources
  2. Update Entra ID redirect URI with the cognitoUserPoolDomain output
  3. yarn webiny deploy api --env=dev — deploys API with identity mapping
  4. yarn webiny deploy admin --env=dev — deploys admin with login screen

Related Skills

  • webiny-cognito-federation — Full reference for all Cognito Federation options
  • webiny-configure-okta — Alternative: Okta replaces Cognito entirely
  • webiny-configure-auth0 — Alternative: Auth0 replaces Cognito entirely

Signals

GitHub stars
8k
Forks
679
Last commit
Sep 2026
Advanced
Item type
skill
Key
webiny-configure-entraid
Source
github.com/webiny/webiny-js