microsoftgraph / microsoftgraph/msgraph-sdk-dotnet

`User` creation with `AdditionalData` set fails with `ODataError` "The following extension properties are not available"

Open
#2,680 18 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Needs: Attention :wave: type:bug
Dominant language
C#
Stars
789
Forks
264
Avg merge
15h 17m
Merged PRs (30d)
3

Description

Describe the bug

I'm trying to create users in an Azure B2C tenant via Graph which fails when using the code inside a gRPC web-service, but succeeds in a console POC-style app - using the exact same code snippet?!

Fun fact: setting the 2nd extension property without casting it from uint to int results in the API only complaining about that property, not the other 2.

Expected behavior

Code succeeds, no matter which way its run.

How to reproduce

Code snippet of the actual Graph code:

var user = await graphServiceClient.Users
                                   .PostAsync(new()
                                   {
                                     AccountEnabled = true,
                                     AdditionalData = new Dictionary<string, object>
                                     {
                                       { "extension_{appId}_CustomerIds", "1,2,3" },
                                       { "extension_{appId}_TenantId", (int)1u },
                                       { "extension_{appId}_TenantIds", "4,5,6" }
                                     },
                                     CompanyName = "Test Co.",
                                     DisplayName = "Test Pilot",
                                     GivenName   = "Test",
                                     Identities  =
                                     [
                                       new()
                                       {
                                         Issuer           = "{ourB2Csubdomain}.onmicrosoft.com",
                                         IssuerAssignedId = "quacks@example.net",
                                         SignInType       = "emailAddress",
                                       }
                                     ],
                                     Mail             = "quacks@example.net",
                                     PasswordPolicies = "DisablePasswordExpiration",
                                     PasswordProfile  = new()
                                     {
                                       ForceChangePasswordNextSignIn = false,
                                       Password                      = "Test12345!"
                                     },
                                     PreferredDataLocation = "EUR",
                                     PreferredLanguage     = "de-DE",
                                     Surname               = "Pilot",
                                     UsageLocation         = "DE",
                                   })
                                   .ConfigureAwait(false);

Code snippet for the actual setup in Program.cs of the web-service:

// … code left out for brevity…
var configuration   = builder.Configuration;
var isNonProduction = !builder.Environment.IsProduction();
// … code left out for brevity…
.AddAuthentication()
.AddMicrosoftIdentityWebApi(options =>
  {
    LogCompleteSecurityArtifact = ShowPII = isNonProduction;
    builder.Configuration.Bind(Constants.AzureAdB2C, options);
    options.TokenValidationParameters = new()
    {
      // … code left out for brevity…
    };
  },
  options => builder.Configuration.Bind(Constants.AzureAdB2C, options),
  subscribeToJwtBearerMiddlewareDiagnosticsEvents: isNonProduction)
.EnableTokenAcquisitionToCallDownstreamApi(options =>
{
  // … code left out for brevity…
  options.EnablePiiLogging = isNonProduction;
  options.LogLevel         = isNonProduction ? LogLevel.Always : LogLevel.Info;
})
.AddMicrosoftGraphAppOnly(_ =>
  new(
    new Azure.Identity.ClientSecretCredential(
      configuration[$"{Constants.AzureAdB2C}:{nameof(MicrosoftIdentityOptions.TenantId)}"],
      clientId,
      clientSecret,
      new()
      {
        Diagnostics =
        {
          // … code left out for brevity…
        },
        IsUnsafeSupportLoggingEnabled = isNonProduction
      })))
.AddInMemoryTokenCaches(options =>
  options.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(isNonProduction ? 14 : 90))
// … code left out for brevity…
SDK Version

5.58

Latest version known to work for scenario above?

AFAICT: none

Known Workarounds

None, because neither using a POC-style console app nor adding those extension properties via PATCH HTTP request is suitable - I expect the Graph API to handle user creation successfully even when extension properties are part of the request's payload.

Debug output
Click to expand log
Microsoft.Graph.Models.ODataErrors.ODataError:
  The following extension properties are not available:
     extension_{appId}_CustomerIds,extension_{appId}_TenantId,extension_{appId}_TenantIds.
   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.ThrowIfFailedResponse(
     HttpResponseMessage response,
     Dictionary`2 errorMapping,
     Activity activityForAttributes,
     CancellationToken cancellationToken)
   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.SendAsync[ModelType](
     RequestInformation requestInfo,
     ParsableFactory`1 factory,
     Dictionary`2 errorMapping,
     CancellationToken cancellationToken)
   at Microsoft.Kiota.Http.HttpClientLibrary.HttpClientRequestAdapter.SendAsync[ModelType](
     RequestInformation requestInfo,
     ParsableFactory`1 factory,
     Dictionary`2 errorMapping,
     CancellationToken cancellationToken)
   at Microsoft.Graph.Users.UsersRequestBuilder.PostAsync(
     User body,
     Action`1 requestConfiguration,
     CancellationToken cancellationToken)
   at My.Services.Migration.GraphService.CreateUser(CreateUserRequest request, ServerCallContext context)
   in D:\Code\Services\My.Services.Migration\GraphService.cs:line 24
Configuration
  • OS: Windows 11, latest patches & Ubuntu Ubuntu 22.04.4 LTS, latest patches
  • architecture: x64 (Windows) & x86_64 (Ubuntu)
Other information

I find the Graph SDK docs regarding Azure B2C user properties rather confusing, because the summary of AdditionalData states:

Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well.

while the one for Extensions states (emphasis by me):

The collection of open extensions defined for the user. Read-only. Supports $expand. Nullable.

Because that suggests 2 things: a) use AdditionalData for reading and writing, but Extensions only for reading - why 2 properties for basically the same amount of information?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at GraphService.cs line 24 and review the Graph and authentication setup in Program.cs. Reproduce the user-creation request with SDK 5.58, then inspect the serialized request and resulting extension-property error while comparing it with the console and PATCH cases. Done means the cause of the differing behavior is identified and a supported create-user path or SDK correction is established.

Written by the indexing model from the issue text.

Assessment

Tech stack
azure, csharp
Domain
api, authentication
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.