2

In my B2C Tenant I have 4 custom user attributes defined. Using the CLI app defined here:https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet#create-consumer-user-accounts When I add my custom properties to the .json for user creation, such as

{
  "accountEnabled": true,
  "signInNames": [
    {
      "type": "emailAddress",
      "value": "mail@example.com"
    }
  ],
  "creationType": "LocalAccount",
  "displayName": "Joe Consumer",
  "passwordProfile": {
    "password": "P@ssword!",
    "forceChangePasswordNextLogin": false
  },
  "passwordPolicies": "DisablePasswordExpiration",
  "canViewSoccer": true
}

I get the error:

Error Calling the Graph API:
{
  "odata.error": {
    "code": "Request_BadRequest",
    "message": {
      "lang": "en",
      "value": "One or more property values specified are invalid."
    },
    "date": "2019-01-09T16:07:16",
    "requestId": "a1e30ffb-c675-4def-9741-d2a6aceb96c7",
    "values": null
  }
}

What do I need to do to be able to use my custom properties in user creation

juunas
  • 54,244
  • 13
  • 113
  • 149
Jane Quinn
  • 63
  • 1
  • 4

3 Answers3

2

A custom attribute must be formatted as:

"extension_{app_id}_{property_name}": "{property_value}"

Example:

"extension_917ef9adff534c858b0a683b6e6ec0f3_CanViewSoccer": true

where {app_id} must be set to the application ID of the b2c-extensions-app application that is registered in your Azure AD B2C tenant.

Chris Padgett
  • 14,186
  • 1
  • 15
  • 28
  • When I perform gets on user, the extension properties aren't present either, and when attempting to use them I receive the error `"The following extension properties are not available: extension_{app_id}_canViewSoccer`. Is there something else I'm missing? – Jane Quinn Jan 10 '19 at 12:06
  • Hi @JaneQuinn How did you create the custom attribute? [Via the Azure portal](https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-reference-custom-attr)? – Chris Padgett Jan 10 '19 at 20:50
  • That's right I created it via the Azure portal. If I prompt the application extension_properties from the application storing them, I do get the extension properties, they're just not being applied to any of my users. `GET https://graph.windows.net/{tenant}.onmicrosoft.com/applications/{application_id}/extensionProperties?api-version=1.6` `"value": [ { "odata.type": "Microsoft.DirectoryServices.ExtensionProperty", "objectType": "ExtensionProperty", "appDisplayName": "", "name": "extension_e716a5725e584d44a366ae39913b50f8_canViewBaseball",` – Jane Quinn Jan 11 '19 at 09:13
  • @jane-quinn Have you used the attribute in a user flow? The docs Chris linked say " A custom attribute is only created the first time it is used in any user flow, and not when you add it to the list of User attributes." – Jason Jan 12 '19 at 16:39
  • @JaneQuinn By creating a custom attribute, it isn't added to any **user** objects, so if you `GET` a **user** object then the custom attribute won't be returned. However, you can `PATCH` a **user** object with a custom attribute, as described in the above answer. – Chris Padgett Jan 13 '19 at 21:11
  • @ChrisPadgett If I attempt to patch a user that doesn't yet have the extension properties, I still get `the following extension properties are not available` error. `PATCH https://graph.windows.net/{tenant}/users/{user_object_Id}?api-version=1.6 Authorization: Bearer {Bearer token}... Content-Type: application/json { "extension_e716a572-5e58-4d44-a366-ae39913b50f8_canViewSoccer": true }` – Jane Quinn Jan 14 '19 at 09:26
  • @Jason I have used the attribute in a user flow and have users created with that policy that have the custom attributes. – Jane Quinn Jan 14 '19 at 10:04
  • Is the custom attribute called `canViewBaseball` or `canViewSoccer`? – Chris Padgett Jan 14 '19 at 10:49
  • @ChrisPadgett There are multiple extension properties, canViewBaseball & canViewSoccer are some of them, there are 5 in total for various sports. – Jane Quinn Jan 14 '19 at 15:40
1

The application id of the from b2c-extensions-app must be used without the hyphens in the extension property name. I.e.

{ "extension_e716a572-5e58-4d44-a366-ae39913b50f8_canViewSoccer": true }

should be

{ "extension_e716a5725e584d44a366ae39913b50f8_canViewSoccer": true }
Markus
  • 341
  • 1
  • 5
1

See this example, UserService.CreateUserWithCustomAttribute(): https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/4-WebApp-your-API/4-2-B2C

public static async Task CreateUserWithCustomAttribute(GraphServiceClient graphClient, string b2cExtensionAppClientId, string tenantId)
{
    if (string.IsNullOrWhiteSpace(b2cExtensionAppClientId))
    {
        throw new ArgumentException("B2C Extension App ClientId (ApplicationId) is missing in the appsettings.json. Get it from the App Registrations blade in the Azure portal. The app registration has the name 'b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.'.", nameof(b2cExtensionAppClientId));
    }

    // Declare the names of the custom attributes
    const string customAttributeName1 = "FavouriteSeason";
    const string customAttributeName2 = "LovesPets";

    // Get the complete name of the custom attribute (Azure AD extension)
    Helpers.B2cCustomAttributeHelper helper = new Helpers.B2cCustomAttributeHelper(b2cExtensionAppClientId);
    string favouriteSeasonAttributeName = helper.GetCompleteAttributeName(customAttributeName1);
    string lovesPetsAttributeName = helper.GetCompleteAttributeName(customAttributeName2);

    Console.WriteLine($"Create a user with the custom attributes '{customAttributeName1}' (string) and '{customAttributeName2}' (boolean)");

    // Fill custom attributes
    IDictionary<string, object> extensionInstance = new Dictionary<string, object>();
    extensionInstance.Add(favouriteSeasonAttributeName, "summer");
    extensionInstance.Add(lovesPetsAttributeName, true);

    try
    {
        // Create user
        var result = await graphClient.Users
        .Request()
        .AddAsync(new User
        {
            GivenName = "Casey",
            Surname = "Jensen",
            DisplayName = "Casey Jensen",
            Identities = new List<ObjectIdentity>
            {
                new ObjectIdentity()
                {
                    SignInType = "emailAddress",
                    Issuer = tenantId,
                    IssuerAssignedId = "casey.jensen@example.com"
                }
            },
            PasswordProfile = new PasswordProfile()
            {
                Password = Helpers.PasswordHelper.GenerateNewPassword(4, 8, 4)
            },
            PasswordPolicies = "DisablePasswordExpiration",
            AdditionalData = extensionInstance
        });

        string userId = result.Id;

        Console.WriteLine($"Created the new user. Now get the created user with object ID '{userId}'...");

        // Get created user by object ID
        result = await graphClient.Users[userId]
            .Request()
            .Select($"id,givenName,surName,displayName,identities,{favouriteSeasonAttributeName},{lovesPetsAttributeName}")
            .GetAsync();

        if (result != null)
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.WriteLine($"DisplayName: {result.DisplayName}");
            Console.WriteLine($"{customAttributeName1}: {result.AdditionalData[favouriteSeasonAttributeName].ToString()}");
            Console.WriteLine($"{customAttributeName2}: {result.AdditionalData[lovesPetsAttributeName].ToString()}");
            Console.WriteLine();
            Console.ResetColor();
            Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
        }
    }
    catch (ServiceException ex) 
    {
        if (ex.StatusCode == System.Net.HttpStatusCode.BadRequest)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"Have you created the custom attributes '{customAttributeName1}' (string) and '{customAttributeName2}' (boolean) in your tenant?");
            Console.WriteLine();
            Console.WriteLine(ex.Message);
            Console.ResetColor();
        }                
    }
    catch (Exception ex)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(ex.Message);
        Console.ResetColor();
    }
}

If you want you can avoid using the helpers at the top. The string key in the extensionInstance dictionary will be "extension_{app id}_{property name}" where the {app id} is the id of the default app created for you with the name b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.

Luke Garrigan
  • 4,571
  • 1
  • 21
  • 29
Aaron Hoffman
  • 6,604
  • 8
  • 56
  • 61