Exception Azure AD B2C Setting Custom Attribute With GraphServiceClient - microsoft-graph-api

Following directions from here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/user-flow-custom-attributes?pivots=b2c-user-flow
I am able to create and get users fine:
// This works!
var graphServiceClient = new GraphServiceClient(
$"https://graph.microsoft.com/beta",
AuthenticationProvider);
var user = await graphClient.Users[userId].Request().GetAsync();
if (user.AdditionalData == null)
user.AdditionalData = new Dictionary<string, object>();
user.AdditionalData[$"extension_xxxxxxx_Apps] = "TestValue";
// this does not work!
result = await graphClient.Users[user.Id].Request().UpdateAsync(msGraphUser);
For xxxxxxx I tried both the Client ID and Object Id from the b2c-extensions-app in my tenant.
Exception:
Microsoft.Graph.ServiceException: 'Code: Request_BadRequest
Message: The following extension properties are not available: extension_xxxxxxx_Apps.
what am I missing? How can I set a custom attribute from GraphServiceClient?
Thank you

Try creating a "new" user rather getting the existing one. When you call UpdateAsync, B2C will only set the properties that you provide (it won't overwrite the other props with null). This may or may not help, but the thing is, we're doing the same thing you do above except with a "new" User, and it works for us.
User b2cUser = await this.GetOurUser(userId);
var additionalData = new Dictionary<string, object>();
additionalData["extension_id-of-extensions-app-here_Foo"] = "Ice Cream Cone";
var updatedB2cUser = new User
{
AdditionalData = additionalData
};
await this.GraphServiceClient.Users[b2cUser.Id].Request().UpdateAsync(updatedB2cUser);
In practice, we include additional props such as Identities, because we use B2C as an identity provider...so the above is some pared-down code from our B2C wrapper showing just the "custom property" part.
Update
Actually, you may just need to remove the hyphens from your extension-app ID and double-check which one you're using.

Related

How to mock Microsoft Graph APIs GraphServiceClient using moq

My project is using GraphServiceClient to get the user Group Names using the below code.This is also using Microsoft.Identity.Web package so GraphServiceClient is injected through constructor.
var group = await graphClient.Me.TransitiveMemberOf
.Request()
.GetAsync();
The group variable is then used to get the DisplayName of the group.
I want to unit test the above code using NUnit and Moq.
var mockAuthProvider = new Mock<IAuthenticationProvider>();
var mockHttpProvider = new Mock<IHttpProvider>();
var mockGraphClient = new Mock<GraphServiceClient>(mockAuthProvider.Object, mockHttpProvider.Object);
mockGraphClient.Setup(c => c.Me.TransitiveMemberOf.Request().GetAsync(CancellationToken.None)).ReturnsAsync(???);
The ReturnAsync will return IUserTransitiveMemberOfCollectionWithReferencesPage, but how can I give a default value for it so I can test the rest of the method which actually gets the displayName
Thanks in advance.
Create a new instance of UserTransitiveMemberOfCollectionWithReferencesPage and add a new Group item to the current page.
UserTransitiveMemberOfCollectionWithReferencesPage page = new
UserTransitiveMemberOfCollectionWithReferencesPage
{
AdditionalData = new Dictionary<string, object>(),
};
page.Add(new Group { DisplayName = "MyName" });
Return page in ReturnsAsync method
mockGraphClient.Setup(c => c.Me.TransitiveMemberOf.Request().GetAsync(CancellationToken.None))
.ReturnsAsync(() => page);

Error When creating a GraphServiceClient in Example App from MS

I'm trying to get the MS example application to work for the Graph Beta Webhooks API and it's currently crashing because I've had to to modify some of the example code to remove some obsolete code and I'm not sure what to replace it with.
This is the function:
public static GraphServiceClient GetAuthenticatedClient(string userId, string redirect)
{
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (request) =>
{
var tokenCache = new SampleTokenCache(userId);
// Obsolete code
//var cca = new ConfidentialClientApplication(Startup.ClientId, redirect, new ClientCredential(Startup.ClientSecret), tokenCache.GetMsalCacheInstance(), null);
// New code
var cca2 = ConfidentialClientApplicationBuilder.Create(Startup.ClientId).WithClientSecret(Startup.ClientSecret).WithRedirectUri(redirect).Build();
// Question - how do I pass the tokenCache in here as the userTokenCache ?
// ERROR - With the new code this returns zero accounts presuambly because I haven't passed in a userTokenCache
var accounts = await cca2.GetAccountsAsync();
// Obsolete code
//var authResult = await cca.AcquireTokenSilentAsync(Startup.Scopes, accounts.First());
// New code
var authResult2 = await cca2.AcquireTokenSilent(Startup.Scopes, accounts.First()).ExecuteAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult2.AccessToken);
}));
return graphClient;
}
If I use the ConfidentialClientApplicationBuilder then the GetAccountsAsync() returns an empty collection and I think it's because I haven't passed the tokenCache into the builder. Does anyone know how to fix this code or has anyone got the example App working ?
This is the link to the example App:
https://learn.microsoft.com/en-us/samples/microsoftgraph/aspnet-webhooks-rest-sample/microsoft-graph-aspnet-webhooks/
Thanks
Ed James
You need to login as user so that an account is added to the token cache which is available calling the tokenCache.GetMsalCacheInstance() method.

The type initializer for 'Homepage.Controllers.AppFlowMetadata' threw an exception

This is weird.
This is related to the article "Using OAuth 2.0 with Web applications (ASP.NET MVC)": https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#web-applications-aspnet-mvc
Disregard the entire code, no need, the question is this - there is a method:
private static readonly IAuthorizationCodeFlow flow =
new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = WebConfigurationManager.AppSettings["ClientId"],
ClientSecret = WebConfigurationManager.AppSettings["ClientSecret"]
},
Scopes = new[] { CalendarService.Scope.Calendar, GmailService.Scope.GmailReadonly },
DataStore = new FileDataStore(folder)
//DataStore = new FileDataStore("Google.Auth.Store")
});
If I use DataStore = new FileDataStore(folder) everything works,
however, according to the documentation, I can just use
DataStore = new FileDataStore("Google.Auth.Store")
So, according to this article (http://www.daimto.com/google-net-filedatastore-demystified/) we should have a new folder called Google.Auth.Store created here with all the JSON token files in it:
%AppData%\Roaming\Google.Auth.Store\Google.Apis.Auth.OAuth2.Responses.TokenResponse-user
Never happens!
Instead I get a blank page with a single error message:
Index is not loading, try again later The type initializer for
'Homepage.Controllers.AppFlowMetadata' threw an exception.
Why is this happening? according to the documentation this is exactly how it should be.

How to mock and test asp.net identity's UserStore.CreateAsync method

I have a rather simple problem . I am simply trying to test asp.net Identity's UserStore method and have this in my test. Essentially the goal was simply to create a mock user store( in memory), insert a dummy user and verify that the insert succeeded.
[Test]
public void Can_Create_User()
{
var identityResult = Task.FromResult(new IdentityResult());
var user = new ApplicationUser() { UserName = "Andy", Email = "andy#andy.com" };
var store = new Mock<UserStore<ApplicationUser>>();
store.Setup(x => x.CreateAsync(user))
.Returns(identityResult);
Assert.IsTrue(identityResult.Result.Succeeded);
}
But the test keeps failing with 'Expected true' error.
Thank you
I'm answering my own question as for some reason the question wasn't getting any views and I did manage to fix it .
I don't know if this is the right approach, but the way I fixed it was firstly changing successfulResult to Task<IdentityResult> AND assigning IdentityResult.Success to it
[Test]
public void Can_Create_User()
{
Task<IdentityResult> successfulResult = Task.FromResult<IdentityResult>(IdentityResult.Success);
var user = new ApplicationUser() { UserName = "Andy", Email = "andy#andy.com" };
var store = new Mock<UserStore<ApplicationUser>>();
store.Setup(x => x.CreateAsync(user)).Returns(successfulResult);
Task<IdentityResult> tt = (Task<IdentityResult>) store.Object.CreateAsync(user);
Assert.IsTrue(tt.Result.Succeeded);
}
I didn't see your code which you trying to test but as I understand problem is how to mock method which returns IdentityResult?
By default you cannot create new instance of IdentityResult and assign value to Successed property because it doesn't accespt SET, it accept only GET.
Anyway, IdentityResult has protected constructor which accept bool which indicate whether is Successed true or false. It means you can create your own IdentityResultMock class which inherite from IdentityResult and than you can control creation assign Successed.
You can see my example which I answered here.
Please refer to my

Why does the role claim have incorrect type?

I downloaded the WebApp-RoleClaims-DotNet sample application and somehow cannot get it to work.
The about page has the following rather straightforward piece of code:
ClaimsIdentity claimsId = ClaimsPrincipal.Current.Identity as ClaimsIdentity;
var appRoles = new List<String>();
foreach (Claim claim in ClaimsPrincipal.Current.FindAll(claimsId.RoleClaimType))
appRoles.Add(claim.Value);
ViewData["appRoles"] = appRoles;
return View();
Now, the expression ClaimsPrincipal.Current.FindAll(claimsId.RoleClaimType) returns an empty list, even though I have proper roles assigned.
This is I guess, because -- according to the debugger -- the role claim have the type roles instead of the proper namespace http://schemas.microsoft.com/ws/2008/06/identity/claims/role.
What am I missing?
did you ever get this working?
I found this worked:
ClaimsIdentity id = ClaimsPrincipal.Current.Identity as ClaimsIdentity;
var appRoles = new List<string>();
foreach (Claim claim in ClaimsPrincipal.Current.Claims)
{
appRoles.Add(claim.Value);
}
I hope it helps someone with a similar problem.

Resources