CtrlK
BlogDocsLog inGet started
Tessl Logo

webiny-cognito-federation

Configuring Cognito Federation for Webiny projects — federated sign-in via external identity providers (Google, Facebook, Apple, Amazon, OIDC/Entra ID) while keeping Cognito as the user pool. Use this skill when the developer asks about Cognito federation, SSO with Cognito, adding Google/Microsoft/OIDC login to Cognito, federated identity providers, CognitoSignInConfig, CognitoIdpConfig, external users, signInWithRedirect, OAuth redirect URLs, hiding the password form, allowCredentialsLogin, or customizing the federated login screen.

68

Quality

84%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Cognito Federation

TL;DR

Webiny supports federated sign-in through Cognito — users authenticate via external identity providers (Google, Entra ID, etc.) while Cognito remains the user pool. Configure it by adding a federation prop to <Cognito /> in webiny.config.tsx. This handles both infrastructure (Cognito User Pool Domain, IdP resources, OAuth client) and the admin login screen (provider buttons, OAuth for Amplify). Federated users are auto-detected and synced into Webiny. For advanced use cases, provide apiConfig (custom identity mapping) and/or adminConfig (custom login screen behavior).

Pattern / Core Concept

Cognito Federation has three layers:

  1. Infrastructure — The federation prop on <Cognito />, under the hood, creates the Cognito User Pool Domain, Identity Provider resources, and configures OAuth on the User Pool Client.

  2. Admin Login Screen — The federation config is passed as an Admin.BuildParam to the admin app. A CognitoSignInConfig abstraction provides the login screen with provider buttons, OAuth settings for Amplify, and credentials visibility. By default, this is auto-generated from the federation prop. For advanced customization (IP whitelists, async logic), provide an adminConfig extension.

  3. API Identity — Federated tokens are auto-detected via the identities JWT claim and marked external: true. The ExternalIdpUserSyncHandler auto-creates/updates users on login. For custom role/team mapping, provide an apiConfig extension implementing CognitoIdpConfig.

How Federated Login Works

  1. User clicks a provider button on the login screen
  2. signInWithRedirect() redirects to Cognito Hosted UI
  3. Cognito redirects to the external IdP (Google, Entra ID, etc.)
  4. After authentication, Cognito creates an idToken with an identities claim
  5. The admin app picks up the session via fetchAuthSession()
  6. The API detects identities in the token, sets external: true
  7. ExternalIdpUserSyncHandler creates/updates the Webiny user with roles/teams

Reference Tables

<Cognito /> Props

PropTypeDescription
federationobject | () => Promise<object>Federation config (see below) — sync or async
mfabooleanEnable TOTP MFA for all users (default: false)
apiConfigstringPath to API identity mapping extension
adminConfigstringPath to Admin login customization extension

federation Object

FieldTypeRequiredDefaultDescription
domainstringYesCognito User Pool domain prefix
callbackUrlsstring[]YesOAuth callback/redirect URLs
logoutUrlsstring[]NocallbackUrlsOAuth logout redirect URLs
responseType"code" | "token"No"code"OAuth response type
allowCredentialsLoginbooleanNotrueShow email/password form
identityProvidersarrayYesList of federated IdPs

identityProviders[] Items

FieldTypeRequiredDescription
type"google" | "facebook" | "amazon" | "apple" | "oidc"YesProvider type
namestringNoCustom provider name (required for OIDC)
labelstringYesButton text on the login screen
providerDetailsobjectYesAWS Cognito provider details (client_id, client_secret, etc.)
attributeMappingobjectNoCustom attribute mapping (overrides defaults)

CognitoSignInConfig.Interface (Admin Customization)

MethodSignatureDescription
getConfig()() => Promise<Config>Returns federation config for the login screen

CognitoSignInConfig.Config (Return Type)

FieldTypeRequiredDescription
oauth{ scopes, redirectSignIn, redirectSignOut, responseType }YesAmplify OAuth config
allowCredentialsLoginbooleanYesShow email/password form
providersFederatedProvider[]YesProvider buttons
titlestringNoLogin screen title (default: "Sign in")
descriptionstringNoLogin screen description

FederatedProvider (Union Type)

type FederatedProvider =
  | { name: string; label: string } // Auto-rendered button
  | { name: string; component: React.FC<{ signIn: () => void }> }; // Custom button

CognitoIdpConfig.Interface (API Identity Mapping)

MethodSignatureRequiredDescription
getIdentity(token: JwtPayload) => CognitoIdentity | Promise<CognitoIdentity>YesMaps JWT claims to Webiny identity
verifyTokenClaims(token: JwtPayload) => void | Promise<void>NoCustom claim verification

Identity Return Type

Default identity fields (id, displayName, profile) are auto-populated from standard Cognito claims (custom:id, given_name, family_name, email). The custom getIdentity only needs to return fields it wants to override — typically roles and teams.

FieldTypeDescription
idstringUser ID (default: custom:id or sub)
displayNamestringDisplay name (default: from name claims)
rolesstring[]Webiny roles by slug
teamsstring[]Webiny teams by slug
profile{ email, firstName, lastName }User profile (defaults from claims)

Full Examples

Example 1: Simple Federation (No Extension Files)

// webiny.config.tsx
import { Cognito } from "@webiny/cognito";

<Cognito
  federation={{
    domain: "my-app",
    callbackUrls: ["http://localhost:3001", "https://admin.example.com"],
    responseType: "code",
    identityProviders: [
      {
        type: "google",
        label: "Sign in with Google",
        providerDetails: {
          authorize_scopes: "email profile openid",
          client_id: String(process.env.GOOGLE_CLIENT_ID),
          client_secret: String(process.env.GOOGLE_CLIENT_SECRET)
        }
      }
    ]
  }}
/>;

This alone creates the Cognito IdP, configures OAuth, shows a "Sign in with Google" button, and auto-syncs federated users.

Example 2: OIDC Provider (Entra ID / Custom)

<Cognito
  federation={{
    domain: "my-app",
    callbackUrls: ["http://localhost:3001"],
    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)
        }
      }
    ]
  }}
/>

Example 3: Async Federation Config

When provider credentials need to be fetched asynchronously (e.g., from a secrets manager, vault, or remote API), pass federation as an async function instead of a plain object. The config rendering pipeline will wait for the promise to resolve before continuing.

// extensions/idp/entraid/Extension.tsx
import React from "react";
import { Cognito } from "@webiny/cognito";

async function getCredentials() {
  return {
    client_id: process.env.ENTRA_CLIENT_ID,
    client_secret: process.env.ENTRA_CLIENT_SECRET,
    oidc_issuer: process.env.ENTRA_OIDC_ISSUER
  };
}

export const CognitoFederation = () => {
  return (
    <Cognito
      mfa={true}
      apiConfig={"@/extensions/idp/entraid/EntraIdApiConfig.ts"}
      federation={async () => {
        const credentials = await getCredentials();

        return {
          domain: "myproj-webiny-with-entraid",
          callbackUrls: ["https://webiny-6.4.x.localhost"],
          responseType: "code",
          allowCredentialsLogin: true,
          identityProviders: [
            {
              name: "EntraID",
              type: "oidc",
              label: "Sign in with Microsoft",
              providerDetails: {
                attributes_request_method: "POST",
                authorize_scopes: "email profile openid",
                ...credentials
              },
              attributeMapping: {
                "custom:id": "sub",
                username: "sub",
                email: "email",
                given_name: "given_name",
                family_name: "family_name",
                preferred_username: "email"
              }
            }
          ]
        };
      }}
    />
  );
};

Under the hood, the <Cognito> component uses <Await fn={...}> from @webiny/react-properties to resolve the async function. The AsyncProperties wrapper in the config rendering worker gates onChange until all <Await> promises settle, so the CLI won't exit prematurely.

Example 4: Multiple Providers

<Cognito
  federation={{
    domain: "my-app",
    callbackUrls: ["http://localhost:3001"],
    responseType: "code",
    identityProviders: [
      {
        type: "google",
        label: "Sign in with Google",
        providerDetails: {
          authorize_scopes: "email profile openid",
          client_id: String(process.env.GOOGLE_CLIENT_ID),
          client_secret: String(process.env.GOOGLE_CLIENT_SECRET)
        }
      },
      {
        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)
        }
      }
    ]
  }}
/>

Example 5: Custom Identity Mapping (apiConfig)

Map Cognito groups to Webiny roles/teams:

// webiny.config.tsx
<Cognito federation={{/* ... */}} apiConfig={"/extensions/cognito/api.ts"} />
// extensions/cognito/api.ts
import { CognitoIdpConfig } from "@webiny/cognito/api";

class MyConfig implements CognitoIdpConfig.Interface {
  getIdentity(token: CognitoIdpConfig.JwtPayload) {
    const cognitoGroups: string[] = (token["cognito:groups"] as string[]) || [];

    return {
      roles: cognitoGroups.includes("admins") ? ["full-access"] : ["content-editor"],
      teams: cognitoGroups.filter(g => g.startsWith("team-"))
    };
  }
}

export default CognitoIdpConfig.createImplementation({
  implementation: MyConfig,
  dependencies: []
});

Example 6: Custom Admin Login Screen (adminConfig)

IP-based credentials whitelist

// webiny.config.tsx
<Cognito federation={{/* ... */}} adminConfig={"/extensions/cognito/admin.tsx"} />
// extensions/cognito/admin.tsx
import { CognitoSignInConfig } from "@webiny/cognito/admin";

const ALLOWED_IPS = ["1.2.3.4", "5.6.7.8"];

async function fetchUserIP(): Promise<string> {
  const response = await fetch("https://api64.ipify.org?format=json");
  const data = await response.json();
  return data.ip;
}

class MyFederationConfig implements CognitoSignInConfig.Interface {
  async getConfig() {
    let allowCredentials = false;

    if (process.env.REACT_APP_STAGE !== "prod") {
      const ip = await fetchUserIP();
      allowCredentials = ALLOWED_IPS.includes(ip);
    }

    return {
      oauth: {
        scopes: ["profile", "email", "openid"],
        redirectSignIn: [window.location.origin],
        redirectSignOut: [window.location.origin],
        responseType: "code" as const
      },
      allowCredentialsLogin: allowCredentials,
      providers: [{ name: "EntraID", label: "Sign in with Microsoft" }],
      title: "Welcome"
    };
  }
}

export default CognitoSignInConfig.createImplementation({
  implementation: MyFederationConfig,
  dependencies: []
});

Custom button component

// extensions/cognito/admin.tsx
import { CognitoSignInConfig } from "@webiny/cognito/admin";
import { GoogleLoginButton } from "react-social-login-buttons";

class MyFederationConfig implements CognitoSignInConfig.Interface {
  async getConfig() {
    return {
      oauth: {
        scopes: ["profile", "email", "openid"],
        redirectSignIn: [window.location.origin],
        redirectSignOut: [window.location.origin],
        responseType: "code" as const
      },
      allowCredentialsLogin: true,
      providers: [
        {
          name: "google",
          component: ({ signIn }) => <GoogleLoginButton onClick={signIn} />
        }
      ]
    };
  }
}

export default CognitoSignInConfig.createImplementation({
  implementation: MyFederationConfig,
  dependencies: []
});

Custom title and description

class MyFederationConfig implements CognitoSignInConfig.Interface {
  async getConfig() {
    return {
      oauth: {/* ... */},
      allowCredentialsLogin: false,
      providers: [{ name: "EntraID", label: "Sign In" }],
      title: "Company Portal",
      description: "Use your corporate credentials to sign in."
    };
  }
}

Example 7: Custom Attribute Mapping

Override the default OIDC attribute mapping when your IdP uses non-standard claim names.

The custom:id mapping is important — Webiny uses it as the primary user identifier. It's mapped to the IdP's sub claim by default. Always include it in custom mappings unless the IdP's sub value exceeds 36 characters on an existing Cognito pool (deployed prior to Webiny 6.4.4). New pools support up to 256 characters.

<Cognito
  federation={{
    domain: "my-app",
    callbackUrls: ["http://localhost:3001"],
    identityProviders: [
      {
        name: "MyIDP",
        type: "oidc",
        label: "Sign in with MyIDP",
        providerDetails: {
          authorize_scopes: "email profile openid",
          client_id: "...",
          client_secret: "...",
          oidc_issuer: "..."
        },
        attributeMapping: {
          "custom:id": "sub",
          username: "sub",
          email: "email",
          given_name: "first_name",
          family_name: "last_name"
        }
      }
    ]
  }}
/>

MFA (Multi-Factor Authentication)

Enable TOTP-based MFA for all admin users with mfa={true}:

<Cognito mfa={true} />

Or combine with federation:

<Cognito
  mfa={true}
  federation={{
    domain: "my-app",
    callbackUrls: ["http://localhost:3001"],
    identityProviders: [
      {
        name: "EntraID",
        type: "oidc",
        label: "Sign in with Microsoft",
        providerDetails: {/* ... */}
      }
    ]
  }}
/>

When MFA is enabled:

  • The Cognito User Pool requires TOTP for all users (mfaConfiguration: "ON")
  • On first login, users see a TOTP setup screen with a QR code to scan with their authenticator app (Google Authenticator, Authy, etc.)
  • On subsequent logins, users enter a 6-digit code from their authenticator app
  • MFA applies to password-based logins only — federated IdP logins are handled by the external provider

Quick Reference

Imports

// Extension component
import { Cognito } from "@webiny/cognito";

// API identity mapping
import { CognitoIdpConfig } from "@webiny/cognito/api";

// Admin login customization
import { CognitoSignInConfig } from "@webiny/cognito/admin";

Key Interfaces

InterfacePackagePurpose
CognitoIdpConfig.Interface@webiny/cognito/apiAPI-side JWT-to-identity mapping
CognitoIdpConfig.JwtPayload@webiny/cognito/apiJWT token payload type
CognitoSignInConfig.Interface@webiny/cognito/adminAdmin login screen customization
CognitoSignInConfig.FederatedProvider@webiny/cognito/adminProvider button type (label or component)

File Structure (Advanced)

extensions/cognito/
├── api.ts       # API config (identity mapping) — optional
└── admin.tsx    # Admin config (login customization) — optional

Deploy

yarn webiny deploy        # Deploy all (Core + API + Admin)

Core must be deployed first (creates IdP resources), then API + Admin.

Related Skills

  • webiny-configure-entraid — Specific guide for Microsoft Entra ID federation
  • webiny-configure-okta — Alternative: Okta replaces Cognito entirely
  • webiny-configure-auth0 — Alternative: Auth0 replaces Cognito entirely
  • webiny-dependency-injection — The DI pattern used by createImplementation()
Repository
webiny/webiny-js
Last updated
First committed

Is this your skill?

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.