Corporate Login with Cognito and Entra ID

Federate Microsoft Entra ID with Amazon Cognito to protect your AWS app.

Introduction

30 Second Summary

Every company needs a way to make sure only employees can access internal tools. The gold standard is letting people log in with the same corporate email they already use for Outlook and Teams.

In this project, you will add a corporate login system to your existing AWS app so that only users with valid company Outlook emails can access it.

What You'll Build

A complete corporate authentication layer that uses Microsoft Entra ID (the identity service behind Outlook) and Amazon Cognito to protect your API Gateway endpoints from unauthorized access.

By the end of this project, you'll have:

  • A Cognito User Pool configured as a SAML 2.0 federation broker between your app and Microsoft Entra ID.
  • An Enterprise Application registered in Entra ID with proper attribute mapping so Cognito receives user emails.
  • A Cognito Authorizer on your API Gateway that rejects any request without a valid JWT token.
  • Secret Mission: Add group-based access control by mapping Entra ID security groups to Cognito, then restrict API routes based on group membership.

Are there any prerequisites?

This project builds on top of an existing AWS serverless app. You need an AWS account with S3, Lambda, DynamoDB, CloudFront, and an API Gateway REST API already set up.

You also need admin access to a Microsoft Entra ID tenant (this comes with any Microsoft 365 business subscription).

Create a Cognito User Pool

Right now, your AWS app is open to anyone who has the URL. To restrict access to only employees with valid corporate Outlook emails, you need an authentication service that can verify users against your company's directory.

Amazon Cognito is a managed authentication service that acts as a broker between your app and identity providers like Microsoft Entra ID. In this step, you'll create a Cognito User Pool to serve as the foundation for your corporate login flow.

In this step, get ready to:
  • Create a Cognito User Pool with email sign-in and disabled self-registration.
  • Add a Cognito domain for your hosted login page.
  • Note down key values needed for the Microsoft Entra ID configuration.
Create the User Pool in the Cognito console

A User Pool is a user directory in Cognito that handles sign-up, sign-in, and token management. You'll configure yours to only allow users who authenticate through your corporate identity provider.

The creation wizard walks you through several configuration screens. Follow these settings on each screen:

  • Under Configure sign-in experience, select Email as a sign-in option.
  • Under Configure security requirements, keep the defaults.
  • Under Configure sign-up experience, uncheck Enable self-registration to disable it.
  • Under Configure message delivery, select Send email with Cognito.
  • Under Integrate your app, enter corporate-auth-pool as the User pool name.
  • Under Initial app client, set the App client name to my-app-client.
  • Select Generate a client secret.
  • Set the Callback URL to your CloudFront distribution URL followed by /callback (e.g., your CloudFront callback URL).
  • Review the settings and choose Create user pool.

Why disable self-registration?

Since only corporate Entra ID users should access your app, you don't want anyone to sign up with a random email address. Disabling self-registration ensures the only way into the User Pool is through your federated identity provider.

Why Cognito instead of Auth0 or Clerk?

Your app already runs entirely on AWS (S3, Lambda, DynamoDB, CloudFront, API Gateway). Cognito integrates natively with API Gateway through a built-in authorizer type. An external auth provider would require a custom Lambda authorizer and extra configuration.

Cognito is the purpose-built solution for apps already living in the AWS ecosystem.

You should see your new user pool listed on the User pools page with a status of Active.

Don't see the user pool?

Make sure you are in the correct AWS region. User pools are regional resources. Check the region selector in the top-right corner of the console.

If the creation failed, check that your callback URL starts with https://. Cognito requires HTTPS for callback URLs.

Why can't I create my Cognito User Pool?

Add a Cognito domain

Cognito needs a domain to host the login page that users see before being redirected to Microsoft's sign-in. This domain is where your SAML responses will be sent.

  • Click on your newly created corporate-auth-pool user pool to open it.
  • Go to the App integration tab.
  • Under Domain, choose Actions, then Create Cognito domain.
  • Enter a unique domain prefix (e.g., mycompany-auth).
  • Choose Create Cognito domain.

Domain prefix restrictions

You cannot use the keywords "aws", "amazon", or "cognito" in your domain prefix. Pick something related to your company or project name instead.

Your Cognito domain is now active. The full URL follows this format: https://{your-prefix}.auth.{region}.amazoncognito.com.

Note down key values for Entra ID setup

In the next step, you'll register your Cognito pool as an Enterprise Application in Microsoft Entra ID. Entra ID needs two specific values to establish the trust relationship.

  • From the User pool overview page, copy your User pool ID (format: region_xxxxxxxxx).
  • Write down your Entity ID using this format: urn:amazon:cognito:sp:{your-user-pool-id}.
  • Write down your Reply URL using this format: https://{your-domain-prefix}.auth.{region}.amazoncognito.com/saml2/idpresponse.

What are these values?

The Entity ID tells Entra ID which Cognito User Pool is requesting authentication. It is a unique identifier that Entra ID uses to route SAML responses back to the correct pool.

The Reply URL is the endpoint where Entra ID sends the SAML assertion after a user successfully logs in. Cognito listens on this URL to receive and validate the response.

Keep these values handy

Paste both values into a text file or note-taking app. You'll need them in the very next step when configuring the Entra ID Enterprise Application.

Your Cognito User Pool is ready and waiting for an identity provider. Next up, you'll register it as an Enterprise Application in Microsoft Entra ID so the two services can communicate through SAML.

Register Cognito as an Enterprise Application in Microsoft Entra ID

In the previous step, you created a Cognito User Pool and noted down the Entity ID and Reply URL. Now you need to tell Microsoft's identity system about your app so it knows where to send authenticated users.

To establish a trust relationship between Cognito and your corporate login system, you'll register Cognito as an Enterprise Application in Microsoft Entra ID. This tells Entra ID that your Cognito User Pool is a legitimate app that can receive user identity information after login.

In this step, get ready to:
  • Create a non-gallery Enterprise Application in Entra ID.
  • Configure SAML-based Single Sign-On with your Cognito Entity ID and Reply URL.
  • Copy the Federation Metadata URL to connect Entra ID back to Cognito.
Create the Enterprise Application

An Enterprise Application in Entra ID represents any external service that your organization's users can sign in to. By registering Cognito here, you're telling Microsoft that it's safe to send user credentials to your AWS app after authentication.

  • Open a new browser tab and go to the Azure Portal at https://portal.azure.com.
  • Sign in with your Microsoft account that has admin access to your Entra ID tenant.
  • In the top search bar, type Microsoft Entra ID and select it from the results.
  • In the left sidebar, click Enterprise applications.
  • Click New application at the top.
  • Click Create your own application.
  • Enter AWS Cognito SSO as the application name.
  • Select Integrate any other application you don't find in the gallery (Non-gallery).
  • Click Create.

Why a non-gallery application?

Entra ID's gallery contains pre-configured templates for popular apps like Salesforce or Slack. Since your Cognito User Pool is a custom setup unique to your AWS account, it doesn't have a gallery template. The non-gallery option lets you configure the SAML trust manually.

After creation, you should land on the application overview page for your new AWS Cognito SSO Enterprise Application.

Don't see Enterprise applications in the sidebar?

Make sure you navigated to Microsoft Entra ID first (via the search bar), not Azure Active Directory. Your account also needs admin-level permissions on the Entra ID tenant to create Enterprise Applications.

Help me troubleshoot Entra ID access issues

Set up SAML-based Single Sign-On

Now you'll configure the SAML 2.0 settings that tell Entra ID exactly where to send users after they log in. This is where you'll use the Entity ID and Reply URL you noted down in Step 1.

  • On the application overview page, find Set up single sign on under the "Getting Started" section.
  • Click Get started.
  • Select SAML as the sign-on method.

You should now see the SAML-based Sign-on configuration page with several numbered sections.

  • In the Basic SAML Configuration section, click the Edit icon (pencil).
  • In the Identifier (Entity ID) field, enter your Entity ID: urn:amazon:cognito:sp: followed by your User Pool ID.
  • In the Reply URL (Assertion Consumer Service URL) field, enter your Reply URL: https://{your-domain-prefix}.auth.{region}.amazoncognito.com/saml2/idpresponse.
  • Click Save at the top of the Basic SAML Configuration panel.

What are the Entity ID and Reply URL doing?

The Entity ID is a unique identifier that tells Entra ID which Cognito User Pool this application represents. The format urn:amazon:cognito:sp:{user-pool-id} is required by AWS.

The Reply URL is the endpoint where Entra ID sends the SAML response after a user successfully logs in. Cognito listens at this /saml2/idpresponse path and processes the identity assertion.

Use the values you wrote down in Step 1

If you followed Step 1 and wrote down your Entity ID and Reply URL, paste them directly into these fields. For example, if your User Pool ID is us-east-1_abc123def, your Entity ID would be urn:amazon:cognito:sp:us-east-1_abc123def.

Copy the Federation Metadata URL

Entra ID publishes a metadata document that contains all the information Cognito needs to trust it as an identity provider. You'll grab this URL now and use it in the next step to complete the connection.

  • On the same SAML configuration page, scroll down to section 3: SAML Signing Certificate.
  • Find the App Federation Metadata Url field.
  • Click the copy icon next to the URL to copy it to your clipboard.
  • Paste this URL somewhere safe (a text editor or sticky note). You will need it in the next step.

What does the Federation Metadata URL look like?

The URL follows a pattern like https://login.microsoftonline.com/{tenant-id}/federationmetadata/2007-06/federationmetadata.xml?appid={app-id}. This XML document contains Entra ID's signing certificates and endpoint URLs that Cognito uses to validate SAML responses.

Don't see the App Federation Metadata Url?

Make sure you saved the Basic SAML Configuration in the previous substep. The SAML Signing Certificate section only populates after you've configured and saved the Entity ID and Reply URL.

Help me find the Federation Metadata URL in Entra ID

You've now registered your Cognito User Pool as a trusted application in Microsoft Entra ID. Next up, you'll go back to Cognito and add Entra ID as a SAML identity provider using the metadata URL you just copied.

Configure Entra ID as a SAML Identity Provider in Cognito

In the previous step, you registered your app in Microsoft Entra ID and copied the Federation Metadata URL. Now it's time to bring that metadata back to Cognito so the two services can trust each other.

Right now, Cognito has no idea that Entra ID exists. You need to tell Cognito where to find Entra ID's SAML metadata, how to map user attributes like email, and which app client should use this new identity provider.

In this step, get ready to:
  • Add Entra ID as a SAML identity provider in your Cognito User Pool.
  • Map the SAML email attribute so Cognito receives the user's email address.
  • Enable the identity provider on your app client's Hosted UI.
Add the SAML identity provider

A SAML identity provider tells Cognito how to communicate with an external authentication service. By pointing Cognito at Entra ID's metadata URL, Cognito automatically learns the certificates, endpoints, and protocols it needs to validate login tokens from Entra ID.

  • Open the AWS Console and navigate to the Amazon Cognito console.
  • Select User Pools in the left navigation, then choose your corporate-auth-pool user pool.
  • Go to the Sign-in experience tab.
  • Under Federated identity provider sign-in, choose Add identity provider.
  • Select SAML.
  • For Provider name, enter EntraID.
  • Under Metadata document source, select Enter metadata document endpoint URL.
  • Paste the App Federation Metadata URL you copied from Entra ID in the previous step.
  • Choose Add identity provider.

This name is permanent

The provider name EntraID cannot be changed after you create it. If you make a typo, you'll need to delete the provider and recreate it.

You should see a success banner confirming the identity provider was added. The EntraID provider now appears under the Federated identity provider sign-in section.

Seeing a metadata error?

Double-check that you pasted the full Federation Metadata URL from Entra ID. It should start with https://login.microsoftonline.com/ and end with a query parameter containing your app ID.

If you copied it with extra whitespace or line breaks, clear the field and paste it again.

Help me troubleshoot my SAML metadata URL error in Cognito

What is a SAML Identity Provider in Cognito?

A SAML identity provider in Cognito acts as a bridge. When a user wants to log in, Cognito redirects them to the external provider (Entra ID in this case) to handle authentication.

Once the user successfully authenticates, Entra ID sends a SAML assertion back to Cognito containing the user's details. Cognito then creates a session and issues tokens your app can use.

Map attributes between Entra ID and Cognito

Entra ID and Cognito use different names for the same user information. Attribute mapping tells Cognito which SAML claim from Entra ID corresponds to which Cognito user pool attribute.

  • After creating the provider, choose EntraID from the identity providers list.
  • Under Attribute mapping, choose Edit.
  • Map the SAML attribute http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress to the Cognito user pool attribute email.
  • Choose Save changes.

Why map the email attribute?

When a user logs in through Entra ID, the SAML assertion includes their email in a claim with a long XML namespace URL. Cognito doesn't know what that claim means unless you explicitly map it to Cognito's email attribute.

Without this mapping, federated users would appear in Cognito without an email address. Your app wouldn't know who they are.

Don't see the attribute mapping section?

Make sure you clicked on the EntraID provider name to open its details page. The attribute mapping section appears on the provider's detail page, not on the main Sign-in experience tab.

Help me find the attribute mapping section for my SAML provider in Cognito

Enable the identity provider on your app client

Adding the identity provider to Cognito is not enough on its own. You also need to tell your app client to use it. This step connects the Entra ID provider to the Hosted UI that your users will see when they sign in.

  • Go to the App integration tab in your user pool.
  • Under App clients and analytics, choose your app client (my-app-client).
  • Under Hosted UI, choose Edit.
  • Under Identity providers, check the box for EntraID.
  • Confirm the OAuth 2.0 grant type is set to Authorization code grant.
  • For OpenID Connect scopes, select openid and email.
  • Confirm the Callback URL is still set to your CloudFront distribution URL (e.g., https://d1234abcd.cloudfront.net/callback).
  • Choose Save changes.

After saving, you should see the Hosted UI configuration updated with EntraID listed as an identity provider alongside the grant type and scopes you selected.

What are OAuth scopes?

Scopes tell Cognito what information your app is allowed to request about the user. The openid scope is required for any OpenID Connect flow. The email scope tells Cognito to include the user's email address in the token.

Don't see EntraID in the identity providers list?

Go back to the Sign-in experience tab and verify that the EntraID provider shows under Federated identity provider sign-in. If it doesn't appear there, the provider was not created successfully. Repeat the first substep.

Help me figure out why my EntraID provider is missing from the Hosted UI settings

The trust relationship is now complete. Cognito knows about Entra ID, knows how to read user emails from SAML assertions, and your app client is configured to offer Entra ID as a login option. Next up, you'll test the entire login flow end-to-end using Cognito's Hosted UI.

Test the Login Flow with Cognito Hosted UI

Your SAML trust relationship is fully configured between Microsoft Entra ID and Amazon Cognito. Now it is time to prove everything actually works end-to-end.

Cognito provides a Hosted UI that gives you a ready-made login page. You will use it to trigger the full SAML flow: clicking a sign-in button, authenticating with your corporate Outlook email through Microsoft, and landing back on your app with a valid authorization code.

In this step, get ready to:
  • Launch the Cognito Hosted UI and confirm the Entra ID sign-in option appears.
  • Sign in with your corporate Outlook email through the Microsoft login page.
  • Verify the federated user appeared in your Cognito User Pool.
Launch the Hosted UI

The Hosted UI is a pre-built login page that Cognito generates for your app client. It handles redirecting users to any identity providers you have configured.

  • In the AWS Cognito console, open your corporate-auth-pool user pool.
  • Go to the App integration tab.
  • Under App clients and analytics, choose your app client (my-app-client).
  • Find the View Hosted UI button and choose it.

A new browser tab opens showing the Cognito Hosted UI login page. You should see a button for your Entra ID provider.

What should I see?

The Hosted UI login page displays a sign-in button labeled with your identity provider name (EntraID). This button triggers the SAML redirect to Microsoft's login page.

If you also left email/password sign-in enabled, you may see a username/password form as well. The important thing is that your Entra ID provider button is visible.

Don't see your Entra ID provider button?

Go back to App integration > your app client > Hosted UI > Edit. Confirm that your SAML provider (EntraID) is checked under Identity providers.

Also verify that you have a Cognito domain set up. Without a domain, the Hosted UI cannot load.

Why isn't my Entra ID provider showing on the Cognito Hosted UI?

Sign in with your Outlook email

This is where the full SAML flow happens. Cognito redirects you to Microsoft, you authenticate, and Microsoft sends a SAML assertion back to Cognito with your identity.

  • On the Hosted UI page, choose the EntraID sign-in button.
  • You will be redirected to the Microsoft login page at https://login.microsoftonline.com.
  • Enter your corporate Outlook email address and choose Next.
  • Enter your password and choose Sign in.
  • If prompted with a "Stay signed in?" dialog, choose either option.

After successful authentication, Microsoft redirects you back to your callback URL. Check your browser's URL bar.

What should I see in my URL bar?

Your browser should show your CloudFront callback URL with an authorization code appended as a query parameter. It looks something like:

https://d1234abcd.cloudfront.net/callback?code=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

The ?code= parameter proves that Cognito received the SAML assertion from Entra ID and issued an authorization code. You will use this code later to exchange for access tokens.

Your page might show an error

The actual page content may show an error or a blank page because your frontend does not have a /callback route yet. That is expected. The important thing is the authorization code in the URL bar.

Seeing a SAML error or getting stuck at Microsoft's login page?

Verify that your Reply URL in Entra ID matches exactly: https://{your-domain-prefix}.auth.{region}.amazoncognito.com/saml2/idpresponse. Even a trailing slash will cause a mismatch error.

Also check that your Entity ID in Entra ID follows the exact format: urn:amazon:cognito:sp:{your-user-pool-id}. The User Pool ID is case-sensitive.

My SAML login flow is failing between Entra ID and Cognito. Help me troubleshoot.

Verify the user appeared in Cognito

The authorization code in your URL confirms the redirect worked. Now confirm that Cognito actually created a user record from the SAML assertion.

  • Go back to the AWS Cognito console.
  • Open your corporate-auth-pool user pool.
  • Choose the Users tab.

You should see a new user entry with your corporate email address. The Confirmed status should show External provider.

What does External provider mean?

This status means the user did not create an account directly in your Cognito User Pool. Instead, their identity came from a federated identity provider (in this case, Entra ID via SAML).

Cognito creates a linked user record automatically when a federated user signs in for the first time. The email attribute was populated from the SAML attribute mapping you configured in the previous step.

Don't see a new user in the Users tab?

Make sure you completed the sign-in flow all the way through. If you closed the browser tab before reaching the callback URL, Cognito may not have received the SAML assertion.

Try launching the Hosted UI again and completing the full sign-in. Also check that your attribute mapping is set correctly. The email attribute must be mapped or the user record may appear without an email.

Help me figure out why no user appeared in my Cognito User Pool after signing in through the Hosted UI.

Your SAML federation is confirmed working. Users can now authenticate with their corporate Outlook email and Cognito receives their identity. Next up, you will protect your API Gateway endpoints so only authenticated users can access them.

Add a Cognito Authorizer to API Gateway

In the previous step, you proved the full SAML login flow works. Users can authenticate with their corporate Outlook email and land back on your app with a valid authorization code.

There is one problem though. Your API Gateway endpoints are still wide open. Anyone with the URL can call them without logging in. In this step, you will lock those endpoints down by adding a Cognito Authorizer that checks every incoming request for a valid token from your user pool.

In this step, get ready to:
  • Create a Cognito Authorizer in your API Gateway REST API.
  • Apply the authorizer to your API methods so they require a valid token.
  • Redeploy your API and confirm unauthenticated requests are rejected.
Create the authorizer

A Cognito Authorizer sits in front of your API methods and inspects the Authorization header of every request. If the token is missing or invalid, the request is rejected before it ever reaches your Lambda function.

  • Open the API Gateway console at https://console.aws.amazon.com/apigateway.
  • Select your existing REST API from the list.
  • In the left navigation pane, choose Authorizers.
  • Choose Create authorizer.
  • For Authorizer name, enter CognitoAuth.
  • For Authorizer type, select Cognito.
  • For Cognito user pool, select the region where your pool lives, then select corporate-auth-pool.
  • For Token source, enter Authorization.
  • Choose Create authorizer.

What is the Token source?

The Token source tells API Gateway which HTTP header to look at when checking for credentials. Setting it to Authorization means your frontend will send the Cognito JWT token in the standard Authorization header with each API request.

You should now see CognitoAuth listed under your API's Authorizers with the type showing as Cognito.

Don't see your user pool in the dropdown?

Make sure you selected the correct region for the Cognito user pool. Your user pool only appears in the region where you created it.

Also confirm that your API Gateway REST API is in the same region as your user pool. Cross-region authorizers are not supported.

Help me troubleshoot my Cognito Authorizer setup in API Gateway

Apply the authorizer to your API methods

Creating the authorizer does not automatically protect anything. You need to explicitly assign it to each method you want to lock down.

  • In the left navigation pane, choose Resources.
  • Select a method you want to protect (e.g., GET on one of your resources).
  • Choose the Method request tab.
  • Choose Edit.
  • For Authorization, select your Cognito authorizer (CognitoAuth).
  • Choose Save.
  • Repeat for any other methods you want to protect.

Which methods to protect

Protect every method that reads or writes your application data. If you have multiple resources with GET, POST, PUT, or DELETE methods, apply the authorizer to each one individually.

After saving, you should see CognitoAuth displayed in the Authorization field of each method you updated.

Deploy the API

Changes to API Gateway are not live until you deploy. Your existing stage still serves the old, unprotected configuration until you push the update.

  • Choose Deploy API.
  • Select your existing stage (e.g., prod).
  • Choose Deploy.

Your API now requires a valid JWT token in the Authorization header for the protected methods. Any request without a token receives a 401 Unauthorized response.

How can I verify this is working?

Try calling one of your protected API endpoints directly in a browser tab (just paste the invoke URL). You should see a 401 Unauthorized error because the browser does not send an Authorization header.

This confirms the authorizer is active. In the next step, you will update your frontend to include the token so requests succeed again.

Still getting a 200 response without a token?

Make sure you chose Deploy API after saving the authorizer assignment. Undeployed changes do not take effect.

Double check that the specific method you are calling has CognitoAuth set in its Authorization field. If it still shows None, the authorizer was not applied to that method.

Help me debug why my API Gateway still returns 200 without an Authorization token

Your API is now locked down. Next up, you will update your frontend to redirect users through the SSO login flow and include the token in every API call.

Update Your Frontend to Handle Login

Your API Gateway now rejects any request that does not include a valid Cognito token. Unauthenticated users receive a 401 Unauthorized response.

In this step, you will update your S3-hosted frontend to redirect users to the Cognito Hosted UI for login, handle the callback redirect, and attach the resulting token to every API call.

In this step, get ready to:
  • Add a Sign In button that redirects users to the Cognito login page.
  • Extract the authorization code from the callback URL and exchange it for tokens.
  • Include the token in API calls and verify that protected endpoints accept authenticated requests.
Add a login redirect to your frontend

The Cognito Hosted UI provides a ready-made login page that handles the SAML 2.0 federation with Microsoft Entra ID for you. You just need to redirect users to a specific URL that includes your app client ID and callback URL.

The login URL follows this format:

https://[[COGNITO_DOMAIN="your-domain-prefix"]].auth.[[AWS_REGION="your-region"]].amazoncognito.com/login?client_id=[[APP_CLIENT_ID="your-app-client-id"]]&response_type=code&scope=openid+email&redirect_uri=[[CALLBACK_URL="https://d1234abcd.cloudfront.net/callback"]]

What do these parameters mean?

  • client_id tells Cognito which app client is making the request.
  • response_type=code requests an authorization code (not tokens directly). This is the secure Authorization Code flow.
  • scope=openid+email requests the user's identity and email address.
  • redirect_uri is where Cognito sends the user after login. This must match the callback URL you configured earlier.
  • Find your App client ID by opening the Cognito console, selecting your corporate-auth-pool user pool, going to the App integration tab, and locating your app client under App clients and analytics.
  • Copy the Client ID value. You will use it in your frontend code.
  • Open your frontend's main HTML file in a code editor.
  • Add a Sign In button or link that redirects users to the Cognito Hosted UI login URL. Add this code to your HTML where you want the button to appear:
<!-- Sign In button that redirects to Cognito Hosted UI -->
<button id="sign-in-btn">Sign In</button>

<script>
  // Replace these values with your actual Cognito configuration
  const cognitoDomain = '[[COGNITO_DOMAIN="your-domain-prefix"]].auth.[[AWS_REGION="your-region"]].amazoncognito.com';
  const clientId = '[[APP_CLIENT_ID="your-app-client-id"]]';
  const callbackUrl = '[[CALLBACK_URL="https://d1234abcd.cloudfront.net/callback"]]';

  // Build the login URL with required parameters
  const loginUrl = `https://${cognitoDomain}/login?client_id=${clientId}&response_type=code&scope=openid+email&redirect_uri=${encodeURIComponent(callbackUrl)}`;

  // Redirect user to Cognito Hosted UI when they click Sign In
  document.getElementById('sign-in-btn').addEventListener('click', () => {
    window.location.href = loginUrl;
  });
</script>

What does this code do?

The script builds the full Cognito login URL from your configuration values. When the user clicks the Sign In button, the browser redirects them to the Cognito Hosted UI.

From there, the user sees the Entra ID sign-in option and logs in with their corporate Outlook credentials. After successful login, Cognito redirects them back to your callback URL with an authorization code.

  • Save your HTML file and upload it to your S3 bucket (replacing the existing version).
  • Open your CloudFront URL in a browser. You should see the Sign In button on the page.

Don't see the Sign In button?

  • Make sure you uploaded the updated file to S3. CloudFront serves the S3 version, not your local file.
  • CloudFront may cache the old version. Create an invalidation for /* in your CloudFront distribution to force a refresh.
  • Check the browser console for JavaScript errors. A typo in the script tag can prevent the button from loading.

Help me troubleshoot why my Sign In button is not appearing on my CloudFront-hosted frontend.

Handle the callback and exchange the code for tokens

After a successful login, Cognito redirects the user back to your callback URL with an authorization code in the query string. This code is not a token itself. You need to exchange it for actual tokens by making a POST request to the Cognito token endpoint.

Why an extra exchange step?

The Authorization Code flow is more secure than returning tokens directly in the URL. The code is short-lived and can only be used once. By exchanging it server-side (or with a secret), tokens never appear in the browser's address bar where they could be logged or intercepted.

Your app client was configured with a client secret in Step 1. This means the token exchange request must include the secret. For a learning project, you can include it directly in the request body.

Client secrets in production

In a production SPA with no backend, you would create the app client without a secret (a "public" client). Since your app has Lambda functions, you could route the token exchange through a Lambda function to keep the secret server-side.

For this project, including the secret directly is fine for learning the full flow.

  • Find the Client secret value in the Cognito console under App integration > App clients and analytics > your app client > Show client secret.
  • Create a callback page (or add callback logic to your existing app) that handles the redirect. Add the following JavaScript to the page that loads at your callback URL:
// Configuration values - replace with your actual Cognito details
const cognitoDomain = '[[COGNITO_DOMAIN="your-domain-prefix"]].auth.[[AWS_REGION="your-region"]].amazoncognito.com';
const clientId = '[[APP_CLIENT_ID="your-app-client-id"]]';
const clientSecret = 'your-client-secret-here';
const callbackUrl = '[[CALLBACK_URL="https://d1234abcd.cloudfront.net/callback"]]';

// Extract the authorization code from the URL query string
const urlParams = new URLSearchParams(window.location.search);
const authCode = urlParams.get('code');

if (authCode) {
  // Exchange the authorization code for tokens
  exchangeCodeForTokens(authCode);
}

async function exchangeCodeForTokens(code) {
  // Build the token request body
  const body = new URLSearchParams({
    grant_type: 'authorization_code',
    client_id: clientId,
    client_secret: clientSecret,
    code: code,
    redirect_uri: callbackUrl
  });

  // POST to the Cognito token endpoint
  const response = await fetch(`https://${cognitoDomain}/oauth2/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: body.toString()
  });

  const tokens = await response.json();

  // Store the id_token for use in API calls
  sessionStorage.setItem('id_token', tokens.id_token);
  sessionStorage.setItem('access_token', tokens.access_token);

  // Redirect user to the main app page
  window.location.href = '/';
}

What does this code do?

  • URLSearchParams extracts the code parameter from the callback URL that Cognito appended after login.
  • The fetch call POSTs to the Cognito /oauth2/token endpoint, sending the code along with your client credentials.
  • Cognito responds with three tokens: id_token (user identity), access_token (permissions), and refresh_token (for getting new tokens later).
  • The id_token is stored in sessionStorage so you can retrieve it for API calls. This token is what your API Gateway authorizer validates.
  • Save your callback page and upload it to your S3 bucket.
  • Click your Sign In button from the main page. You should be redirected to the Microsoft login page.
  • Enter your corporate Outlook email and password.
  • After login, you should be redirected back to your callback URL, then to the main page.
  • Open your browser's Developer Tools (press F12 or Cmd+Option+I on macOS). Go to the Application tab, then Session Storage. You should see id_token and access_token stored there.

Not getting redirected back after login?

  • Confirm your callback URL in the code matches exactly what you configured in the Cognito app client settings. Even a trailing slash difference causes a mismatch error.
  • Check the browser console for CORS errors. If the token exchange fails, the console will show the error details.
  • Make sure the client_secret value in your code matches the one shown in the Cognito console.

Help me debug why my Cognito callback is failing after Microsoft login.

Use the token in API calls

Your API Gateway is configured to look for a JWT token in the Authorization header. Every API call from your frontend now needs to include the id_token that Cognito issued after login.

  • Update your existing API calls in your frontend code to include the Authorization header. Use this pattern for every fetch call to your API Gateway:
// Retrieve the token from sessionStorage
const idToken = sessionStorage.getItem('id_token');

// Include the token in the Authorization header for API calls
const response = await fetch('[[API_ENDPOINT="https://your-api-id.execute-api.region.amazonaws.com/prod/your-resource"]]', {
  method: 'GET',
  headers: {
    'Authorization': idToken
  }
});

const data = await response.json();
console.log(data);

What does this code do?

The code retrieves the id_token from sessionStorage and passes it in the Authorization header. Your API Gateway's Cognito authorizer validates this token against your user pool before allowing the request through to Lambda.

  • Save your updated frontend files and upload them to S3.
  • Create a CloudFront invalidation for /* to clear cached files.
  • Open your CloudFront URL, click Sign In, and complete the Microsoft login.
  • After login, trigger an action that calls your API (whatever your existing app does). Open the Network tab in Developer Tools. You should see a successful response (status 200) from your API endpoint.

Now confirm that unauthenticated requests are properly blocked.

  • Open a new browser tab in incognito/private mode.
  • Try calling your API endpoint directly without signing in (paste the API URL into the browser or use Developer Tools console).
  • You should receive a 401 Unauthorized response. This confirms your API Gateway rejects requests without a valid Cognito token.

Getting 401 even after signing in?

  • Check that sessionStorage.getItem('id_token') returns a value in the browser console. If it returns null, the token exchange step failed silently.
  • Make sure the header name is exactly Authorization (capital A). This must match the Token source you configured on the authorizer.
  • Tokens expire after one hour. If your token is older than that, sign in again to get a fresh one.

Help me figure out why my API Gateway returns 401 even though I'm signed in with Cognito.

Secret mission

Group-Based Access Control

Right now, every authenticated user has the same level of access to your API. In a real company, admins need write access while regular employees only need read access. Can you extend your setup to enforce group-based permissions using Entra ID security groups?

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses Amazon Cognito's free tier, which covers 50 SAML federated users per month indefinitely. There are no ongoing costs unless you exceed that threshold.

Resources you used:

  • Amazon Cognito User Pool (Essentials tier) with a custom Cognito domain.
  • Microsoft Entra ID Enterprise Application with SAML single sign-on configuration.
  • API Gateway Cognito Authorizer applied to your REST API methods.
  • SAML attribute mappings between Entra ID and Cognito.

What about costs?

Amazon Cognito's free tier covers 50 SAML federated monthly active users per account. This free tier does not expire after 12 months. It is available indefinitely to all AWS customers.

If you exceed 50 MAUs, each additional user costs $0.015 per month. The Cognito domain, Entra ID Enterprise Application, and API Gateway Authorizer carry no additional charges.

Keep everything running

No action needed. Choose this if you are still actively building on your app or testing the authentication flow.

  • Your Cognito User Pool and Entra ID Enterprise Application remain active at no cost for up to 50 users per month.
  • Your API Gateway endpoints stay protected by the Cognito Authorizer.
  • Users can continue signing in with their corporate Outlook emails.

Pause - I'll come back to this later

Remove authentication enforcement while keeping your setup intact. This lets your API work without tokens temporarily.

  • Open the API Gateway console and select your REST API.
  • For each protected method, go to the Method request tab, choose Edit, and set Authorization back to None.
  • Deploy your API to apply the changes.
  • Leave your Cognito User Pool and Entra ID Enterprise Application in place. They carry no cost while idle.
  • When you are ready to re-enable authentication, re-apply the Cognito Authorizer to your methods and redeploy.

Delete - I don't want to use this again

Remove all authentication resources completely. You will need to redo the full setup if you want corporate login again in the future.

  • In the API Gateway console, remove the Cognito Authorizer from all protected methods. Set Authorization to None on each method, then redeploy your API.
  • In the API Gateway Authorizers section, delete the Cognito Authorizer.
  • In the Amazon Cognito console, open your user pool and choose Delete. Confirm deletion when prompted.
  • In the Azure Portal, navigate to Microsoft Entra ID, then Enterprise applications. Select your application (e.g., "AWS Cognito SSO") and choose Delete.
  • Remove any login redirect code or token handling logic you added to your S3-hosted frontend.

That's a Wrap!

That's a Wrap!

Nice work! You've just built a complete enterprise SSO authentication layer for your existing AWS serverless app.

Only employees with valid corporate Outlook emails can now access your protected endpoints.

You've learned how to:

  • Configured a SAML 2.0 federation between Microsoft Entra ID and Amazon Cognito, letting corporate users sign in with their Outlook email.
  • Protected your API Gateway endpoints with a Cognito Authorizer that rejects requests without a valid JWT token.
  • Built a complete login flow from frontend redirect through the Cognito Hosted UI to token exchange and authenticated API calls.
  • Secret Mission: Added group-based access control by mapping Entra ID security groups to Cognito custom attributes for fine-grained permissions.

Ready to quiz yourself?