How to create user by using microsoft Graph in multiple tenant through c#

Pappu Singh 21 Reputation points
2023-01-04T13:36:21.847+00:00

I have created a tenant and registered the app with multitenant, I am able to create users, and groups and assign the groups to user by using Microsoft Graph API into the same tenant.

Appsseting.json look like--

{
"tenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx",
"applicationId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxx",
"applicationSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

}

while using "tenantId": "common" then i am getting the below error

Microsoft.Graph.ServiceException: 'Code: Authorization_IdentityNotFound
Message: The identity of the calling application could not be established.
Inner error:
AdditionalData:
date: 2023-01-04T08:00:49
request-id: 04afdea2-1009-4e8a-93b3-b6be1b7a0e41
client-request-id: 04afdea2-1009-4e8a-93b3-b6be1b7a0e41
ClientRequestId: 04afdea2-1009-4e8a-93b3-b6be1b7a0e41

I am using this below code

// The client credentials flow requires that you request the
// /.default scope, and preconfigure your permissions on the
// app registration in Azure. An administrator must grant consent
// to those permissions beforehand.
var scopes = new[] { "https://graph.microsoft.com/.default" };

// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "common";

// Values from app registration
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_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 User
{
AccountEnabled = true,
DisplayName = "Adele Vance",
MailNickname = "AdeleV",
UserPrincipalName = "AdeleV@Company portal .onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "xWwvJ]6NMw+bWH-d"
}
};

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

Microsoft Security | Microsoft Graph
0 comments No comments
{count} votes

Accepted answer
  1. CarlZhao-MSFT 46,376 Reputation points
    2023-01-05T09:06:40.853+00:00

    Hi @Pappu Singh

    Do not use the common endpoint in the client credentials flow, or your token will not contain the target tenant's information, because the client credentials flow is an unattended authentication flow.

    If you want to use the client credentials flow to create users for the target tenant, you first need to add your multi-tenant application as an enterprise application to the target tenant. You can run the admin consent URL in the browser, then log in with the target tenant's admin and accept.

    https://login.microsoftonline.com/{the tenant-id of the target tenant}/adminconsent?client_id={client-id}

    Next, you need to set the value of the tenantId variable to the tenant id or domain name of the target tenant:

    var tenantId = "{the tenant id or domain name of the target tenant}";

    Refer to the complete sample code:

    using Azure.Identity;   
    using Microsoft.Graph;  
    
    
    var scopes = new[] { "https://graph.microsoft.com/.default" };  
      
    var tenantId = "{the tenant id or domain name of the target tenant}";  
      
    // Values from app registration  
    var clientId = "{client 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 User  
    {  
          AccountEnabled = true,  
          DisplayName = "xxxx",  
          MailNickname = "xxxx",  
          UserPrincipalName = "xxxxx@xxxxx.xxxxxxxxx",  
          PasswordProfile = new PasswordProfile  
          {  
                ForceChangePasswordNextSignIn = true,  
                Password = "xxxxxxxxx"  
          }  
    };  
      
    await graphClient.Users  
          .Request()  
          .AddAsync(user);  
    

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

2 additional answers

Sort by: Most helpful
  1. Vicky Kumar (Mindtree Consulting PVT LTD) 1,161 Reputation points Microsoft Employee
    2023-01-05T06:20:09.493+00:00

    Everything looks fine , make sure you are adding correct scope , one of these application permissions required to create a user - User.Read.All, User.ReadWrite.All, Directory.Read.All, Directory.ReadWrite.All

    Before calling the create user, you can check the access token and check the required scopes , ref doc - https://learn.microsoft.com/en-us/graph/auth-v2-service#4-get-an-access-token

    0 comments No comments

  2. Pappu Singh 21 Reputation points
    2023-01-05T07:22:17.11+00:00

    I am adding this scope
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    code is working fine when i put the actual tenantId="xxxxxxxxxxxxxxxxxxxxx" but the problem is tenantId="common"

    throwing the below error

    Microsoft.Graph.ServiceException: 'Code: Authorization_IdentityNotFound
    Message: The identity of the calling application could not be established.

    below code is for check the User
    please look at below code snippet

            var authenticationProvider = CreateAuthorizationProvider(config);  
            GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);  
    

    bool user = await FindByUserPrincipalName(inputuser.UserPrincipalName, graphClient);

    public static async Task<bool> FindByUserPrincipalName(string alias, GraphServiceClient graphClient)
    {
    List<QueryOption> queryOptions = new List<QueryOption>
    {
    new QueryOption("$filter", $@"userPrincipalName eq '{alias}'")
    };

            var userResult = await graphClient.Users.Request(queryOptions).GetAsync();  
            if (userResult.Count != 1) return false; else return true;  
              
        }  
    

    private static IAuthenticationProvider CreateAuthorizationProvider(IConfiguration config)
    {
    var clientId = config["applicationId"];
    var clientSecret = config["applicationSecret"];
    var redirectUri = config["redirectUrl"];
    var tenantId = config["tenantId"];
    var authority= $"https://login.microsoftonline.com/{tenantId}/oauth2/token";
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    var cca = ConfidentialClientApplicationBuilder.Create(clientId)
    .WithAuthority(authority)
    .WithRedirectUri(redirectUri)
    .WithClientSecret(clientSecret)
    .Build();
    return new MsalAuthenticationProvider(cca, scopes.ToArray());
    }

    public class MsalAuthenticationProvider : IAuthenticationProvider
    {
    private IConfidentialClientApplication _clientApplication;
    private string[] _scopes;

        public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)  
        {  
            _clientApplication = clientApplication;  
            _scopes = scopes;  
        }  
        public async Task AuthenticateRequestAsync(HttpRequestMessage request)  
        {  
            var token = await GetTokenAsync(); // CreateUserAD.HttpAppAuthenticationAsync();  
            request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token.ToString());  
        }  
    
        public async Task<string> GetTokenAsync()  
        {  
            AuthenticationResult authResult = null;  
            authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();  
            return authResult.AccessToken;  
        }  
    }  
    

    }

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.