Microsoft Graph API - user extension data in C# - c#

I have an Azure AD B2C directory with some users in it. I have been using the Microsoft Graph API and through both https://developer.microsoft.com/en-us/graph/graph-explorer and PostMan I have been able to create and modify users along with including an extension attribute (eg: extension_[guid in our tenant]_ContactMethod) - details passed in json in the body of the request in PostMan.
That is fine, however I need to be albe to do the same in C#. I can successfully create the user without specifying any extensions:
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var user = new User
{
AccountEnabled = true,
DisplayName = "John Smith",
MailNickname = "JohnSimth",
UserPrincipalName = "JohnSmith#[tenant].onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "[Randomly Generated Password]"
}
//[Extension attributes to add here]
};
await graphClient.Users.Request().AddAsync(user);
However I need to be able to specify some extension attributes when creating / modifying the user in C#. How do I do this?

You cannot directly add extensions at the time of creation according to this limitations document.
After creating the user, you need to create a separate request that hits the extensions endpoint as shown in the below code and this adds these extension details to the user.
var extension = new OpenTypeExtension
{
ExtensionName = "{ExtensionName}",
AdditionalData = new Dictionary<string, object>
{
{"FirstEmail", "abc#yahoo.com"},
{"SecondEmail" , "xyz#yahoo.com"}
}
};
await graphClient.Users["{UserId}"].Extensions
.Request()
.AddAsync(extension);
So just after user creation, get that userid and use it to hit extensions endpoint as shown in the above code.

Related

Get user data from a TokenCredential object (.NET)

I'm building a .NET core tool (Console app) that needs to access some Azure Keyvault secrets by using a SecretClient. This client needs a TokenCredential for which I use DefaultAzureCredential().
The client is successfully authenticated and retrieves the secrets, but can I know which method from the flow was used (i.e. Environment, Cache, CLI, interactive)? I want to display the username that was used for logged in, since you might have an account in SharedCache but you might want to use another account.
var credentials = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(configuration["Authentication:KeyVaultUri"]), credentials);
// Just using the client to retrieve values
var settings = JsonSerializer.Deserialize<AppSettingsKeys>((await secretClient.GetSecretAsync(configuration["Authentication:SecretName"])).Value.Value);
I checked the credential object but didn't see anything useful to get the username. I want to Console.WriteLine something like Successfully logged in with pepe#test.com using SharedTokenCacheCredential
I was able to get the upn by first getting the jwt with the GetToken method, and then parsing it with a JwtSecurityTokenHandler.
Not the approach I was looking for but it works, I was wondering if there is cleaner way.
var credential = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(configuration["Authentication:KeyVaultUri"]), credential);
var settings = JsonSerializer.Deserialize<AppSettingsKeys>((await secretClient.GetSecretAsync(configuration["Authentication:SecretName"])).Value.Value);
var token = await credential.GetTokenAsync(
new Azure.Core.TokenRequestContext(
new[] { "https://vault.azure.net/.default" }));
var handler = new JwtSecurityTokenHandler();
var jsonToken = handler.ReadToken(token.Token) as JwtSecurityToken;
var upn = jsonToken.Claims.First(c => c.Type=="upn").Value;

Create a user passing in a user object (Microsoft Graph)

I'm trying to create a user using a microsoft graph call using this link: https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=csharp
However, is there a way to create a user by passing in a User object or a json representation of a user object in an azure function as a parameter As shown below?
[HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "user/settings")] HttpRequest req, ILogger logger, User user)
Also, is there a way to read the whole req.Body (w/ additional properties) and create a user, instead of only applying the required parameters (shown below)? Each and every user will have different properties
{
AccountEnabled = true,
DisplayName = "displayName-value",
MailNickname = "mailNickname-value",
UserPrincipalName = "upn-value#tenant-value.onmicrosoft.com",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "password-value"
}
};
await graphClient.Users
.Request()
.AddAsync(user);
For your first question, I don't think we can pass a User object to function directly to create the user. But we can pass all of the properties into the function to create it. As your second question mentioned, it is not easy for us to create the user if every user has different properties. So we can pass all of the properties as json into function and then read the json from the req and concat these properties as json string. After that, do not use graph sdk to create user. Do a post request with graph api by ourself like below code:
string str = "{\"displayName":\"xxxx\",\"mailNickname\":\"xxxx\"}";
var content = new StringContent(str, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "your token");
var response = client.PostAsync("https://graph.microsoft.com/v1.0/users", content).Result;

Create a user passing in a json of user object (Microsoft Graph)

I'm trying to create a user by calling microsoft graph and passing in a json string (one that looks like a user object) into the req.body of a httprequest.
The steps I am currently following are:
Call microsoft graph to get the user's properties
Serialize content into a json string (jsonString) and pass it in as the req.Body of a HTTPRequest when using a graph call to create a user in azure active directory
When I pass the content into the req.Body, since there are required fields that must be filled out (accountEnabled, displayName, onPremisesImmutableId, password, etc.), it will not be created cause it does not specify the required field (ex: In the content of jsonString, password is set to null. Password would need to have a value).
The source link of the graph call is found here: https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=http
Is there a way to pass in my jsonString and also specify some of the user properties within the req.Body? If not, is there a workaround?
var createUser = await httpClient.PostAsync(link, "Token", jsonString);
It's important that you cannot retrieve the password for any user in Azure AD in any way.
So, you need to set the value of the password in your code.
The other properties(displayName, MailNickname, UserPrincipalName, etc.) could be set directly, seeļ¼š
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
User user = await graphClient.Users["{user-id}"].Request().GetAsync();
var newUser = new User
{
AccountEnabled = true,
DisplayName = user.DisplayName,
MailNickname = user.MailNickname,
UserPrincipalName = user.UserPrincipalName,
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = "password-value"
}
};
await graphClient.Users.Request().AddAsync(newUser);
I refer to the code with C# in the link.

How to create a API scope using Azure AD Graph API

I'm trying to use the Azure AD Graph API to create an API Scope for an Azure AD B2C application. This is the operation performed using the "Expose an API" blade in the portal.
I've tried adding the scope directly to the application like so:
var current = await graphClient.Applications[appId].Request().GetAsync();
var currentList = current.Api.Oauth2PermissionScopes ?? new List<PermissionScope>();
var newScope = new PermissionScope
{
AdminConsentDescription = scopeDescription,
AdminConsentDisplayName = scopeDescription,
IsEnabled = true,
Type = "Admin",
Value = scopeName
};
var updated = new Application {
Api = new ApiApplication {
Oauth2PermissionScopes = currentList.Append(newScope).ToList()
}
};
await graphClient.Applications[appId].Request().UpdateAsync(updated);
However, when I do that, I get an exception:
Microsoft.Graph.ServiceException
Code: ValueRequired
Message: Property api.oauth2PermissionScopes.id value is required but is empty or missing.
Does this mean that I need to create the scope separately then add it to the application? Looking over the Graph API docs, it isn't obvious how to do that and I haven't found any articles that discuss it, either.
How do you use Graph API to create API scopes?
if you want to use the Microsoft Graph API to create an API Scope for an Azure AD B2C application, we need to define PermissionScope object. The object should provide id(it is GUID).
For example
Register Application
Grant API permissions
Under Manage, select API permissions.
Under Configured permissions, select Add a permission.
Select the Microsoft APIs tab, then select Microsoft Graph.
Select Application permissions.
Select the checkbox of the permission Application.ReadWrite.All to grant to your application.
Select Add permissions. As directed, wait a few minutes before proceeding to the next step.
Select Grant admin consent for (your tenant name).
Create a client secret
code
static async Task Main(string[] args)
{
string clientId = "0159ec7d-f99f-***";
string clientSecret = "G_fM3QKa***essTRX23t1_o";
string tenantDomain = "{your tenat name}.onmicrosoft.com";
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantDomain)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
var id = "fa89ac50-d5fd-47cb-9f3f-833f413a2ed4";
var app =await graphClient.Applications[id].Request().GetAsync();
var updated = new Application();
if (app.IdentifierUris.ToList().Count == 0) {
updated.IdentifierUris = new string[] { $"https://{tenantDomain}/{app.AppId}" };
}
var appscope = app.Api.Oauth2PermissionScopes.ToList();
var newScope = new PermissionScope
{
Id = Guid.NewGuid(),
AdminConsentDescription = "Allow the application to have read-only access to all Employee data",
AdminConsentDisplayName = "Read-only access to Employee records",
IsEnabled = true,
Type = "Admin",
Value = "Employees.Read.All"
};
appscope.Add(newScope);
updated.Api = new ApiApplication { Oauth2PermissionScopes =appscope };
await graphClient.Applications[id].Request().UpdateAsync(updated);
}
For more details, please refer to here.

Search for user in Azure Active directory group using graph api

I would like to have some kind of people picker functionality with auto complete features in my asp.net mvc 5 app to search for a user in a specific Azure AD group. It's a demo "todo app" that allows to assign a todo to a user that is member of a the group.
I tried with both the Graph API directly and the Azure Graph Client library but I don't seem to find a way to achieve what I want. The graph api allows to get the members of a group but adding filter "startswith" fails as when adding the filter the api returns only directory object which don't include for example DisplayName property... the client library doesn't help much either except for the batch functionality which offers a way but with a lot of overhead... I then would have to get a filtered resultset of user regardless of group membership (using User List stuff in the api), all members of the group and then fish out using Linq the correct result set.... would work fine for dev/testing but in production with a couple of hundred users this would be insane...
Any ideas or suggestions would be much appreciated. Thanks!
EDIT
Below my code that is called from client side Javascript to search for user;
AccessGroupId is the Azure AD group used to authorize users. Only
members of this group can access the web app which I handle in custom
OWin Middleware
The method is intented to be used to find a user in that group
Code works fine as below only there is no filtering applied which is the intentaion with the input parameter pre (which comes from a textbox in the ui). I get all the members of the access group.
public async Task<JsonResult> FindUser(string pre)
{
string AccessGroupId = ConfigurationManager.AppSettings["AccessGroupId"];
AuthenticationContext authCtx = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, "{0}/{1}", SecurityConfiguration.LoginUrl, SecurityConfiguration.Tenant));
ClientCredential credential = new ClientCredential(SecurityConfiguration.ClientId, SecurityConfiguration.AppKey);
AuthenticationResult assertionCredential = await authCtx.AcquireTokenAsync(SecurityConfiguration.GraphUrl, credential);
var accessToken = assertionCredential.AccessToken;
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08, AccessGroupId );
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, graphUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.SendAsync(request);
String responseString = await response.Content.ReadAsStringAsync();
JObject jsonReponse = JObject.Parse(responseString);
var l = from r in jsonReponse["value"].Children()
select new
{
UserObjectId = r["objectId"].ToString(),
UserPrincipalName = r["userPrincipalName"].ToString(),
DisplayName = r["displayName"].ToString()
};
//users = Newtonsoft.Json.JsonConvert.DeserializeObject<List<User>>(responseString);
return Json(l, JsonRequestBehavior.AllowGet);
}
When I add a filter to the same api call instead of returning the members (users, groups and/or contacts), it returns directory objects (that doesn't have displayName) which are not really usefull in the above code, unless I would query the api again (in batch) to retrieve the users displayname but that looks like a lot of overhead to me.
var graphUrl = string.Format("https://graph.windows.net/mytenant.onmicrosoft.com/groups/{0}/members?api-version=2013-11-08&$filter=startswith(displayName,'{1}')", AccessGroupId, pre);
I'd highlight two possible approaches:
Execute requests to Graph API using a custom JS library.
You'd need still need to care for accesstokens and have a look at ADAL.js
A sample app (not finalized as of this writing) available at:
AzureADSamples WebApp-GroupClaims-DotNet
Have a look at AadPickerLibrary.js
Try using ActiveDirectoryClient
It would look something like:
public async Task<JsonResult> FindUser(string pre) {
ActiveDirectoryClient client = AADHelper.GetActiveDirectoryClient();
IPagedCollection<IUser> pagedCollection = await client.Users.Where(u => u.UserPrincipalName.StartsWith(pre, StringComparison.CurrentCultureIgnoreCase)).ExecuteAsync();
if (pagedCollection != null)
{
do
{
List<IUser> usersList = pagedCollection.CurrentPage.ToList();
foreach (IUser user in usersList)
{
userList.Add((User)user);
}
pagedCollection = await pagedCollection.GetNextPageAsync();
} while (pagedCollection != null);
}
return Json(userList, JsonRequestBehavior.AllowGet);
}
More detailed sample is available at:
AzureADSamples WebApp-GraphAPI-DotNet

Categories

Resources