One of the big selling points of Azure Active Directory B2C is its support for a variety of social identity providers like Twitter and Google. You might be moving an existing identity store to Azure AD B2C, and that could include social accounts. If you are migrating local accounts (you store the password hashes), there is already documentation for that: Migrate users to Azure AD B2C.

But what about social accounts? Well, that's the purpose of this article.

Prerequisites

In order to add social accounts in advance to Azure AD B2C, you must know the unique id that the social identity provider will return for that user. This depends on the identity provider, but for example Google uses a numeric id. OAuth/OpenID Connect providers typically return this value in the sub claim. If you only have the email address, it might not be enough. The reason we need this id is because Azure AD B2C will also receive it and try to locate the user using it.

At minimum, you should have the following for each user:

  • Identity provider assigned user id
  • Display name
  • Email address

You can also specify other attributes like given name and surname if those are available.

You also need your Azure AD B2C tenant domain name and tenant id. You can find the domain name in the Overview page of your B2C tenant's settings. It should be something like myb2ctenant.onmicrosoft.com. The tenant id you can find in the tenant switcher or on one of the app registrations' Overview pages.

Migrating a Google account

To migrate an existing social account (in this case a Google account), we need to call Microsoft Graph API to create a user in the Azure AD B2C tenant. The special part is that you need to specify the social account in the user's identities collection.

For the purposes of migrating users, I made this .NET console application:

class Program
{
    static async Task Main(string[] args)
    {
        // Load from file, database etc.
        var users = new List<MigratedUser>
        {
            new MigratedUser
            {
                GoogleId = "1234567890122345",
                Email = "user@gmail.com",
                DisplayName = "Test User",
                GivenName = "Test",
                Surname = "User"
            }
        };

        var b2cDomainName = "yourb2ctenant.onmicrosoft.com";
        var b2cTenantId = "your-b2c-tenant-id";
        var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions
        {
            TenantId = b2cTenantId
        });
        var client = new GraphServiceClient(credential);

        foreach (var user in users)
        {
            await MigrateUserAsync(client, user, b2cDomainName);
        }
    }

    private static async Task MigrateUserAsync(
        GraphServiceClient client,
        MigratedUser user,
        string b2cDomainName)
    {
        var userPrincipalName = $"cpim_{Guid.NewGuid()}@{b2cDomainName}";
        await client.Users.Request().AddAsync(new User
        {
            AccountEnabled = true,
            DisplayName = user.DisplayName,
            GivenName = user.GivenName,
            Surname = user.Surname,
            Identities = new List<ObjectIdentity>
            {
                new ObjectIdentity
                {
                    SignInType = "userPrincipalName",
                    Issuer = b2cDomainName,
                    IssuerAssignedId = userPrincipalName
                },
                new ObjectIdentity
                {
                    SignInType = "federated",
                    Issuer = "google.com",
                    IssuerAssignedId = user.GoogleId
                }
            },
            UserPrincipalName = userPrincipalName,
            OtherMails = new List<string>
            {
                user.Email
            }
        });
    }
}

internal class MigratedUser
{
    public string GoogleId { get; set; }
    public string Email { get; set; }
    public string DisplayName { get; set; }
    public string GivenName { get; set; }
    public string Surname { get; set; }
}

This application only needs two libraries:

You can see we added two identities for the user:

new ObjectIdentity
{
    SignInType = "userPrincipalName",
    Issuer = b2cDomainName,
    IssuerAssignedId = userPrincipalName
},
new ObjectIdentity
{
    SignInType = "federated",
    Issuer = "google.com",
    IssuerAssignedId = user.GoogleId
}

The first identity is added by Azure AD B2C to all local/social users, so I've added it here for completeness as well. Note that we generate the userPrincipalName variable value. This is the same as what Azure AD B2C does as well.

The second identity is the user's social account. SignInType must be "federated", and the issuer should match the social identity provider. If you are not sure what value to put here, sign up to the Azure AD B2C tenant from that provider, and see what is set in the Issuer field on the user's details page.

After running this application for my Google user, I was able to log in using that account and was not prompted for any additional profile info.

Summary

Migrating social accounts to Azure AD B2C is not overly difficult, but it isn't very easy to figure out all the small things you have to set. Hopefully this sample helps you :)