Invite Only Model for Entra External ID to Control / Limit User Sign Ups

RW
Rawson WadeAzure & Entra Software Engineer

Microsoft Entra External ID is a powerful new tool for identity and access management targest specifically for customers. Microsoft Entra External ID simplifies secure access for external users, ensuring seamless collaboration while maintaining robust security. Whether you're integrating partners, suppliers, or customers, Microsoft Entra External ID offers comprehensive features for managing identities, reducing administrative overhead, enhancing user experience and streamlining User Sign Up Flow. However, one issue with this system is that it makes the Sign-Up process very easy for our customers even if we don't know who they are.

Licensing & Security Implications

Not every scenario calls for a self-service signup process. Many organizations prefer to manage a whitelist of approved email addresses for accessing their applications. Whether it's for licensing costs, security, or managing application betas, this approach offers greater control. However, implementing Closed Community Entra External IDs with Microsoft isn't straightforward out of the box; it often requires custom code. But fear not, I've written most of the code for you.

Customer Sign In Sign Up (SUSI) Flow

Before we start with creating our invite only community we first need to understand how the typical Sign Up Sign In ( SUSI) flow works in Microsoft Entra External Id.

  1. The user enters the flow an is prompted for email or Social sign in
  2. User enters an email
  3. User is prompted that a One Time Password (OTP) has been sent
  4. User enters the OTP into Form
  5. OnAttributeCollectionStart is called with the user's email and fields in the signup Form
  6. User is sent to a custom Sign Up form
  7. User enters details. E.g. Display name, phone number,
  8. User submits form
  9. OnAttributeCollectionSubmit is called with the user's inputs for each form item
  10. User is created in Entra ID

They key takeaway from this flow is that we have two opportunities to interact with the user's sign up. First, OnAttributeCollectionStart which allows to inject values into the form. Second, onAttributeCollectionSubmit which lets us modify the inputs, block the user sign up flow, return validation errors or simply continue. If you want documentation on how to set up the basic sign up flow then check out Microsoft's docs. They have a pretty good write up.

Invite-Only Entra External Id Sign Up Flow

Taking that flow described above we can do some sneaky blocks to control who can and can't sign up to our Entra External ID.

Invitation signup flow diagram

The main idea to grasp is that once the user has verified their email via the OTP we can then simply check an invitation store to see if it does or doesn't contain that email address. Awesome Blog Done 🥳.

Invitation Model Function App Code

Well how about I give you working code to do 90% of the job. What you see below simply checks to see if the email address of the user is in our "invitation store". If they are then we let them continue the sign-up process. If they are not we block them from sign up. If you need some assitance then send us a message.

onAttributeCollectionStart.cs
#r "Newtonsoft.Json"
 
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Text;
 
public static async Task<object> Run(HttpRequest req, ILogger log) {
    log.LogInformation("C# HTTP trigger function processed a request.");
 
    // This could be a DB connection or Blob Storage file
    List<string> Whitelist = new List<string>() { "elite@code.entraneering.com" };
 
    string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
    dynamic request = JsonConvert.DeserializeObject(requestBody);
    string userEmail = request?.data?.userSignUpInfo?.identities[0]?.issuerAssignedId;
    log.LogInformation(userEmail);
    bool isUserInvited = !string.IsNullOrWhiteSpace(userEmail) && Whitelist.Contains(userEmail);
 
    if (!isUserInvited) {
        // Return Block
        return new BlockResponseObject {
            data = new BlockData {
                type = "microsoft.graph.onAttributeCollectionStartResponseData",
                actions = new List<BlockedActions> {
                    new BlockedActions {
                        type = "microsoft.graph.attributeCollectionStart.showBlockPage",
                        message = "The email address you provided does not match our records. Looks like you need to contact Entraneer!",
                    }, // Oh Boy look at that nesting!
                },
            },
        };
    }
 
    // Return Continue
    return new ContinueResponseObject {
        data = new ContinueData {
            type = "microsoft.graph.onAttributeCollectionStartResponseData",
            actions = new List<ContinueWithDefaultBehavior> {
                new ContinueWithDefaultBehavior {
                    type = "microsoft.graph.attributeCollectionStart.continueWithDefaultBehavior",
                },
            },
        },
    };
}
 

Pop this into your OnAttributeCollectionStart which you created via the Microsoft Docs and you are good to go other than some types below. One thing to note is that if your function app takes more than 2 seconds to return a response, it will cause a dead-end to the user's signup process with no easy recovery.

types.cs
 
#region Continue Types
 
public class ContinueResponseObject {
    public ContinueData data { get; set; }
}
 
[JsonObject]
public class ContinueData {
    [JsonProperty("@odata.type")]
    public string type { get; set; }
 
    public List<ContinueWithDefaultBehavior> actions { get; set; }
}
 
[JsonObject]
public class ContinueWithDefaultBehavior {
    [JsonProperty("@odata.type")]
    public string type { get; set; }
}
 
#endregion
 
#region Block Types
 
public class BlockResponseObject {
    public BlockData data { get; set; }
}
 
[JsonObject]
public class BlockData {
    [JsonProperty("@odata.type")]
    public string type { get; set; }
 
    public List<BlockedActions> actions { get; set; }
}
 
[JsonObject]
public class BlockedActions {
    [JsonProperty("@odata.type")]
    public string type { get; set; }
 
    public string message { get; set; }
}
[JsonObject]
public class Identities {
    public string signInType { get; set; }
    public string issuer { get; set; }
    public string issuerAssignedId { get; set; }
}
 
#endregion

Invitation Store

In this demo we use a simple list as the invitation store. However, don't let that limit your imagination. In fact you should likely use a dictionary if you use this method. Anyway, This store could be a file on sharepoint / blob storage or an SQL database. For one of our implementation it was an API from Apporetum which returned if a user was apart of an application is a different tenant. Either way, this is where you must manage who should and shouldn't be able to collaborate in your customer tenant.

One More Thing

As you implement the above function app and associated flow you noticed that each signup flow can be associated with a particular application. With the right customisations you can have different invitations / signup flows for each of your applications inside your custom tenant. In addition, each signup flow can capture different properties in the signup flow and add it as custom attributes on the user objects.

Want To See A Production Demo

Want to see first hand how to implement a production grade invitation only Entra External ID? Contact Us to express interest or ask for a free consultation session to understand what we can provide you.

RW
Rawson WadeAzure & Entra Software Engineer

© Posts are provided 'as is' under the AGPL 3.0 license unless otherwise stated

Want to be kept in the loop

Sign up to hear about the latest from the team at Entraneer. We talk anything Entra, IAM and Azure Enterprise Apps.

entra external id invite only microsoft entra external Id closed community invite only ciam entra external invitation model entra external Id control signup SUSI entra b2c integrate with high schools entra external id vs b2c azure b2c ad entra microsoft com

Expansive Knowledge, Best in class Security, best Value in the Cyber Security and Microsoft Entra Partner Class, Highly Skilled. Preference number one. Microsoft Entra Excellence. We are your consultants for entra engineering and development. Microsoft entra engineering. Identity and Access Management Experts in Microsoft Azure, Microsoft Entra, Microsoft Entra ID and Microsoft Entra External ID. experts for microsoft entra Our team offers expansive knowledge and best-in-class security, ensuring the best value in the cyber security industry. As a top Microsoft Entra Partner, we provide highly skilled services, making us your number one preference. We excel in Microsoft Entra excellence and serve as your dedicated consultants for Entra engineering and development. Our expertise in Microsoft Entra engineering encompasses all aspects of identity and access management. We are identity and access management experts in Microsoft Azure, Microsoft Entra, Microsoft Entra ID, and Microsoft Entra External ID. Trust us as your experts for Microsoft Entra. Need help with azuread / Microsoft Entra. Talk to the trusted experts from Australia

We use cookies

We use cookies to ensure you get the best experience on our website. By clicking Accept, you agree to our use of cookies.
Learn more.