none
How to add a new user to Azure Active Directory using C# if the logging in user is not added already? RRS feed

  • Question

  • Hi,

    I am trying to implement single sign on using Azure Active Directory and I have implemented it and working fine if user is already added in the directory. Now I want to check whether a user is already added and if not need to add through c# code(if the user id is of a particular domain) and allow log in. I have done some R&D and came to know that we can do that using Graph API. But don't know where or how to start. Any help will be appreciated.

    Thanks in advance.

    Jai

    Thursday, January 11, 2018 10:14 AM

All replies

  • Hello,

    First off we can't know if there is a problem or what the problem is without code. 

    NOTE: All the code shown below will not work outside of the solution it was created in but instead is kind of a map to follow.

    You should be using the Graph API as indicated here which gives the base REST call to determine if a user exists. The old way of doing this with classes is obsolete and should not be used.

    I don't have a code sample as we have our methods broken up into logical parts. What I can say is you should write unit test to add/check what you are after.

    Here is two of our unit test that are the same topic we use to test for adding, existence and removal. The values for groupObjectId and userObjectId come from Azure online admin pages.

    /// <summary>
    /// Add an existing user to an existing group
    /// </summary>
    [TestTraits(Trait.AzureGraphApi)]
    [TestMethod]
    public void AddUserToGroup()
    {
        var operations = new AzureGraphRestClient(new HttpClient());
        var groupObjectId = "57f7eccb-222a-4969-9te4-cfb743ba4368";
        var userObjectId = "6094c94a-cdfb-47c5-90b4-a302672de4be";
    
        if (operations.IsGroupMember($"groups/{groupObjectId}/$links/members?", userObjectId))
        {
            var result = operations.RemoveUserToGroup($"groups/{groupObjectId}/$links/members/{userObjectId}?", "").Result;
        }
    
        string jsonBody = "{\"url\": \"/directoryObjects/" + userObjectId + "\"}";
    
        string results = operations.AddUserToGroup($"groups/{groupObjectId}/$links/members?", jsonBody).Result;
        Assert.AreEqual("", results, "Expected No Content (if BadRequest user may be in the group already)");
    }
    
    /// <summary>
    /// Remove a user currently in a group, from the group
    /// </summary>
    [TestTraits(Trait.AzureGraphApi)]
    [TestMethod]
    public void RemoveUserFromGroup()
    {
        var operations = new AzureGraphRestClient(new HttpClient());
        var groupObjectId = "57f7eccb-222a-4969-9te4-cfb743ba4368";
        var userObjectId = "6094c94a-cdfb-47c5-90b4-a302672de4be";
    
        if (!operations.IsGroupMember($"groups/{groupObjectId}/$links/members?", userObjectId))
        {
            var result = operations.AddUserToGroup($"groups/{groupObjectId}/$links/members/{userObjectId}?", "").Result;
        }
    
        string results = operations.RemoveUserToGroup($"groups/{groupObjectId}/$links/members/{userObjectId}?", "").Result;
        Assert.AreEqual("", results, "Expected No Content (if BadRequest user may be in the group already)");
    }

    Everything you need to get started (and I say started because it may take time to get things together) is on the following page. This page is a good indexer for the last page.

    Here is some code that we use to work with Azure

    using AcedAPI.Extensions;
    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Threading.Tasks;
    
    namespace AcedApi.MicrosoftGraph
    {
        public class AzureGraphRepository : IGraphRepository
        {
            private ICachingGraphClient Client { get; set; }
    
            public AzureGraphRepository(ICachingGraphClient graphClient)
            {
                Client = graphClient;
            }
    
            /// <summary>
            /// Gets the AD groups that the user is a part of.
            /// </summary>
            public async Task<IEnumerable<GraphValues>> GetUserGroups(string userGraphId)
            {
                var groups = new List<GraphValues>();
    
                if (string.IsNullOrEmpty(userGraphId)) return groups;
    
                string jsonMemberOf = await Client.GetQuery($"users/{userGraphId}/$links/memberOf?");
                GraphResponse groupResponse = JsonConvert.DeserializeObject<GraphResponse>(jsonMemberOf);
    
                foreach (var graphValue in groupResponse.Value)
                {
                    string jsonGroup = await Client.Get(graphValue);
                    var group = JsonConvert.DeserializeObject<GraphValues>(jsonGroup);
                    groups.Add(group);
                }
    
                return groups;
            }
    
            public async Task RemoveUserFromGroups(string userGraphId,
                                                   IEnumerable<string> groups)
            {
                if (string.IsNullOrEmpty(userGraphId)) return;
    
                IEnumerable<GraphValues> allGroups = await GetAllGroups();
                foreach (var group in allGroups.Where(g => groups.Contains(g.DisplayName)))
                {
                    await Client.Delete($"groups/{group.ObjectId}/$links/members/{userGraphId}?");
                    Client.DeleteUrlCacheEntry(Client.MakeUrl(MakeGroupQueryString(userGraphId)));
                }
            }
    
            public async Task AddUserToGroups(string userGraphId,
                                              IEnumerable<string> groups)
            {
                if (string.IsNullOrEmpty(userGraphId)) return;
    
                IEnumerable<GraphValues> allGroups = await GetAllGroups();
                foreach (var group in allGroups.Where(g => groups.Contains(g.DisplayName)))
                {
                    string jsonLinkData = "{\"url\": \"/directoryObjects/" + userGraphId + "\"}";
                    try
                    {
                        await Client.Post($"groups/{group.ObjectId}/$links/members?", jsonLinkData);
                    }
                    catch (WebException e)
                    {
                        // If we are attempting to re-add a duplicate, there is no
                        //  way around it, we must gracefully continue.
                        if (null == e.FindByMessage("One or more added object references already exist for the following modified properties: 'members'"))
                        {
                            throw;
                        }
                    }
                    Client.DeleteUrlCacheEntry(Client.MakeUrl(MakeGroupQueryString(userGraphId)));
                }
            }
    
            /// <summary>
            /// Get all groups that exist in the graph
            /// </summary>
            public async Task<IEnumerable<GraphValues>> GetAllGroups()
            {
                IEnumerable<GraphValues> acedGroups;
    
                string groupsJSON = await Client.GetQuery("groups?");
                GraphResponse groups = JsonConvert.DeserializeObject<GraphResponse>(groupsJSON);
                acedGroups = groups.Value;
    
                return acedGroups;
            }
    
            /// <summary>
            /// Gets the email address from the graph for the given user object id
            /// </summary>
            /// <param name="graphUserObjectId"></param>
            /// <returns></returns>
            public async Task<string> GetUserEmailAddress(string userGraphId)
            {
                string emailAddress = null;
    
                if (string.IsNullOrEmpty(userGraphId)) return emailAddress;
    
                string responseString = await Client.GetQuery(String.Format(
                    "users/{0}/signInNames?",
                    userGraphId));
    
                var response = JsonConvert.DeserializeObject<GraphResponse>(responseString);
                emailAddress = response.Value
                                .FirstOrDefault(v => v.Type == "emailAddress")?
                                .Value;
    
                return emailAddress;
            }
    
            /// <summary>
            /// Make a REST query string to be used to query for the user's groups
            /// </summary>
            private string MakeGroupQueryString(string userGraphId)
            {
                return $"users/{userGraphId}/$links/memberOf?";
            }
        }
    }
    using AcedAPI;
    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    using Newtonsoft.Json;
    using System.Linq;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace AcedApi.MicrosoftGraph
    {
        public class AzureGraphRestClient : IGraphClient
        {
            private string Tenant { get; set; }
            private string ApiGraphVersion { get; set; }
            private string AzureGraphResourceUrl { get; set; }
    
            private AuthenticationContext authContext;
            private ClientCredential credential;
    
            private const string objectReferenceAlreadyExists = "One or more added object references already exist for the following modified properties: 'members'.";
    
            private HttpClient HttpClient { get; set; }
    
            public AzureGraphRestClient(HttpClient httpClient)
            {
                HttpClient = httpClient;
    
                Tenant = AzureHelper.Configs.TenantName;
                ApiGraphVersion = AzureHelper.Configs.AzureGraphApiVersion;
                AzureGraphResourceUrl = AzureHelper.Configs.AzureGraphResourceUrl;
    
                var clientId = AzureHelper.Configs.GraphClientId;
                var clientSecret = AzureHelper.Configs.GraphClientSecret;
                var authString = AzureHelper.Configs.AuthString;
    
                authContext = new AuthenticationContext(authString);
                credential = new ClientCredential(clientId, clientSecret);
            }
    
            /// <summary>
            /// Calls restful GET with proper url.
            /// </summary>
            /// <param name="restQuery"></param>
            /// <returns></returns>
            public async Task<string> GetQuery(string restQuery)
            {
                return await Get(MakeUrl(restQuery));
            }
    
            /// <summary>
            /// Calls restful GET with proper url.
            /// </summary>
            /// <param name="restQuery"></param>
            /// <returns></returns>
            public async Task<string> Get(GraphValues pGraphValue)
            {
                return await Get(MakeUrl(pGraphValue));
            }
    
            /// <summary>
            /// Performs a REST operation against the Graph API
            /// </summary>
            /// <returns>
            /// With a valid REST query returns a string containing json
            /// which can be deserialize e.g. where result is the return value from this method
            /// and T is a concrete class.
            ///
            /// Newtonsoft.Json.JsonConvert.DeserializeObject&lt;T&gt;(result);
            ///
            /// </returns>
            public async Task<string> Get(string url)
            {
                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
    
                string accessToken = await AzureHelper.GetGraphApiAccessToken();
    
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                HttpResponseMessage response = await HttpClient.SendAsync(request);
    
                if (!response.IsSuccessStatusCode)
                {
                    string exceptionDescription = await response.Content.ReadAsStringAsync();
                    object formatted = JsonConvert.DeserializeObject(exceptionDescription);
                    throw new WebException("Error Calling the Graph API:\n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
                }
    
                return await response.Content.ReadAsStringAsync();
            }
    
            /// <summary>
            /// Creates a graph request that includes authorization
            /// </summary>
            private async Task<HttpRequestMessage> CreateRequest(HttpMethod method, string uri)
            {
                string accessToken = await AzureHelper.GetGraphApiAccessToken();
                HttpRequestMessage request = new HttpRequestMessage(method, uri);
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                return request;
            }
    
            /// <summary>
            /// Performs a REST POST or operation against the Graph API
            /// </summary>
            /// <param name="pRestStringQuery">Full query to execute</param>
            /// <param name="pJsonResourceCollection">json body for query</param>
            /// <returns></returns>
            public async Task<string> Post(string pRestStringQuery, string pJsonResourceCollection)
            {
                var request = await CreateRequest(
                        new HttpMethod("POST"),
                        $"{AzureGraphResourceUrl}/{Tenant}/{pRestStringQuery}{ApiGraphVersion}");
    
                request.Content = new StringContent(pJsonResourceCollection,
                                                    Encoding.UTF8,
                                                    "application/json");
    
                HttpResponseMessage response = await HttpClient.SendAsync(request);
    
                if (!response.IsSuccessStatusCode)
                {
                    string graphResponseJson = await response.Content.ReadAsStringAsync();
                    object graphResponse = JsonConvert.DeserializeObject(graphResponseJson);
                    throw new WebException("Error Calling the Graph API:\n" + JsonConvert.SerializeObject(graphResponse, Formatting.Indented));
                }
    
                return await response.Content.ReadAsStringAsync();
            }
    
            /// <summary>
            /// Performs a REST DELETE or operation against the Graph API
            /// </summary>
            /// <param name="pRestStringQuery">Full query to execute</param>
            /// <param name="pJsonResourceCollection">json body for query</param>
            /// <returns></returns>
            public async Task<string> Delete(string pRestStringQuery, string pJsonResourceCollection = "")
            {
                var request = await CreateRequest(
                        new HttpMethod("DELETE"),
                        $"{AzureGraphResourceUrl}/{Tenant}/{pRestStringQuery}{ApiGraphVersion}");
    
                request.Content = new StringContent(pJsonResourceCollection,
                                                    Encoding.UTF8,
                                                    "application/json");
    
                HttpResponseMessage response = await HttpClient.SendAsync(request);
    
                if (!response.IsSuccessStatusCode)
                {
                    string graphResponseJson = await response.Content.ReadAsStringAsync();
                    object graphResponse = JsonConvert.DeserializeObject(graphResponseJson);
                    throw new WebException("Error Calling the Graph API:\n" + JsonConvert.SerializeObject(graphResponse, Formatting.Indented));
                }
    
                return await response.Content.ReadAsStringAsync();
            }
    
            /// <summary>
            /// Determine if an existing user is in an existing group
            /// </summary>
            /// <param name="pGroupObjectId">Group ObjectId</param>
            /// <param name="pUserObjectId">User ObjectId</param>
            /// <returns></returns>
            public bool IsGroupMember(string pGroupObjectId, string pUserObjectId)
            {
                var result = false;
                var groupGraphResponse = JsonConvert.DeserializeObject<GroupGraphResponse>(GetQuery(pGroupObjectId).Result);
                if (groupGraphResponse.Members.Count() > 0)
                {
                    result = groupGraphResponse.Members.Any(member => member.Url.Contains(pUserObjectId));
                }
    
                return result;
            }
    
            /// <summary>
            /// Makes a full url string for querying the graph
            /// </summary>
            public string MakeUrl(string restQuery)
            {
                return $"{AzureGraphResourceUrl}/{Tenant}/{restQuery}{ApiGraphVersion}";
            }
    
            /// <summary>
            /// Makes a full url string for querying the graph
            /// </summary>
            public string MakeUrl(GraphValues pGraphValue)
            {
                return $"{pGraphValue.Url}?{ApiGraphVersion}";
            }
        }
    }
    using AcedApi.MicrosoftGraph;
    using System.Threading.Tasks;
    
    namespace AcedAPI.Classes.MicrosoftGraph
    {
        public static class GraphClientExtensions
        {
            /// <summary>
            /// Add an existing user into an existing group
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="pRestStringQuery"></param>
            /// <param name="pJsonResourceCollection"></param>
            /// <returns></returns>
            public static async Task<string> AddUserToGroup(this AzureGraphRestClient sender, string pRestStringQuery, string pJsonResourceCollection)
            {
                return await sender.Post(pRestStringQuery, pJsonResourceCollection);
            }
    
            /// <summary>
            /// Remove an existing user in a group from the group
            /// </summary>
            /// <param name="sender"></param>
            /// <param name="pRestStringQuery"></param>
            /// <param name="pJsonResourceCollection"></param>
            /// <returns></returns>
            public static async Task<string> RemoveUserToGroup(this AzureGraphRestClient sender, string pRestStringQuery, string pJsonResourceCollection)
            {
                return await sender.Delete(pRestStringQuery, pJsonResourceCollection);
            }
        }
    }




    Please remember to mark the replies as answers if they help and unmark them if they provide no help, this will help others who are looking for solutions to the same or similar problem. Contact via my Twitter (Karen Payne) or Facebook (Karen Payne) via my MSDN profile but will not answer coding question on either.
    VB Forums - moderator
    profile for Karen Payne on Stack Exchange, a network of free, community-driven Q&A sites


    Thursday, January 11, 2018 11:06 AM
    Moderator