Search code examples
c#azure-active-directorylinqpad

Creating a user for Azure AD B2C app, how to acquire token correctly? Failing with "parsing_wstrust_response_failed"


[SOLVED, see the edits]

I am working in Linqpad 6, running a script that I made based on the following articles:

Here is my script:

void Main()
{
    Debug.WriteLine("yo");
    UserCreator creator = new();
    creator.CreateUser();
}

public class UserCreator
{
    public async void CreateUser()
    {
            var scopes = new[] { "User.ReadWriteAll" };
            // Multi-tenant apps can use "common",
            // single-tenant apps must use the tenant ID from the Azure portal
            var tenantId = "<MY_TENANT_ID>";

            // Value from app registration
            var clientId = "<MY_APPLICATION_ID>";

            var pca = PublicClientApplicationBuilder
                .Create(clientId)
                .WithTenantId(tenantId)
                .Build();

            // DelegateAuthenticationProvider is a simple auth provider implementation
            // that allows you to define an async function to retrieve a token
            // Alternatively, you can create a class that implements IAuthenticationProvider
            // for more complex scenarios
            var authProvider = new DelegateAuthenticationProvider(async (request) =>
            {
            // Use Microsoft.Identity.Client to retrieve token
            var result = await pca.AcquireTokenByIntegratedWindowsAuth(scopes).ExecuteAsync();

                request.Headers.Authorization =
                    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
            });

            GraphServiceClient graphClient = new GraphServiceClient(authProvider);
            var user = new User
            {
                AccountEnabled = true,
                DisplayName = "John",
                MailNickname = "John",
                UserPrincipalName = "[email protected]",
                PasswordProfile = new PasswordProfile
                {
                    ForceChangePasswordNextSignIn = true,
                    Password = "xWwvJ]6NMw+bWH-d"
                }
            };

            await graphClient.Users
                .Request()
                .AddAsync(user);
    }
}

I am trying to add a new user to an Azure AD B2C app, but the request is failing with an InnerException of: "The system cannot contact a domain controller to service the authentication request."

I am suspecting I need more info for the script, such as the name of the registered App, but I cannot find anything about it in the documentation. I find it likely that the request is not returning the correct auth token.

Below is a screenshot of the error: An image of the InnerException from Linpad

Updated code This is my final, working result. Originally, I tried to create a user through my own account, but MFA got in the way. The actual way to do it, is through an app registration.

void Main()
{
    UserCreator creator = new();
    creator.CreateUser();
}

public class UserCreator
{
    public async void CreateUser()
    {
        var clientId = "<CLIENT_ID>";
        var scopes = new[] { "https://graph.microsoft.com/.default" };

        var tenantId = "<TENANT_ID>";

        var clientSecret = "<CLIENT_SECRET>";
        
        // using Azure.Identity;
        var options = new TokenCredentialOptions
        {
            AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
        };

        // https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
        var clientSecretCredential = new ClientSecretCredential(
            tenantId, clientId, clientSecret, options);

        var graphClient = new GraphServiceClient(clientSecretCredential, scopes);

        var user = new{
            Email = "[email protected]",
            DisplayName = "TestUser", 
            Username = "Someusername",
        };

        var invitation = new Invitation
        {
            InvitedUserEmailAddress = user.Email,
            InvitedUser = new User
            {
                AccountEnabled = true,
                DisplayName = "TestUser",
                CreationType = "LocalAccount",
                PasswordPolicies = "DisableStrongPassword",
                PasswordProfile = new PasswordProfile
                    {
                        ForceChangePasswordNextSignIn = true,
                        Password = "Test123456",
                    }
                    
            },
            InvitedUserType = "member",
            SendInvitationMessage = true,
            InviteRedirectUrl = "someurl.com"
        };

        await graphClient.Invitations
            .Request()
            .AddAsync(invitation);
            Console.Write("completed");
    }
}

Solution

  • Try to set Azure AD authority by .WithAuthority instead of WithTenantId.

    There is a typo in your scopes. Required permission is User.ReadWrite.All not User.ReadWriteAll.

    var scopes = new[] { "User.ReadWrite.All" };
    ...
    
    var pca = PublicClientApplicationBuilder
                .Create(clientId)
                .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
                .WithDefaultRedirectUri()
                .Build();