Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from LockTar/f/manage-custom-attributes
Browse files Browse the repository at this point in the history
F/manage custom attributes
  • Loading branch information
mmacy authored Mar 10, 2020
2 parents 4c96e99 + 48fc307 commit c4ad778
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 6 deletions.
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ The code in this sample backs the [Manage Azure AD B2C user accounts with Micros
## Setup

1. Clone the repo or download and extract the [ZIP archive](https://github.com/Azure-Samples/ms-identity-dotnetcore-b2c-account-management/archive/master.zip)
1. Modify `./src/appsettings.json` with values appropriate for your environment:
2. Modify `./src/appsettings.json` with values appropriate for your environment:
- Azure AD B2C **tenant ID**
- Registered application's **Application (client) ID**
- Registered application's **Client secret**
1. Build the application with `dotnet build`:
3. Build the application with `dotnet build`:

```console
azureuser@machine:~/ms-identity-dotnetcore-b2c-account-management$ cd src
Expand All @@ -61,6 +61,10 @@ The code in this sample backs the [Manage Azure AD B2C user accounts with Micros

Time Elapsed 00:00:02.62
```
4. Add 2 custom attributes to your B2C instance in order to run all the sample operations with custom attributes involved.
Attributes to add:
- FavouriteSeason (string)
- LovesPets (boolean)

## Running the sample

Expand All @@ -79,6 +83,8 @@ Command Description
[4] Delete user by object ID
[5] Update user password
[6] Create users (bulk import)
[7] Create user with custom attributes and show result
[8] Get all users (one page) with custom attributes
[help] Show available commands
[exit] Exit the program
-------------------------
Expand Down
22 changes: 22 additions & 0 deletions src/Helpers/B2cCustomAttributeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace b2c_ms_graph.Helpers
{
internal class B2cCustomAttributeHelper
{
internal readonly string _b2cExtensionAppClientId;

internal B2cCustomAttributeHelper(string b2cExtensionAppClientId)
{
_b2cExtensionAppClientId = b2cExtensionAppClientId.Replace("-", "");
}

internal string GetCompleteAttributeName(string attributeName)
{
if (string.IsNullOrWhiteSpace(attributeName))
{
throw new System.ArgumentException("Parameter cannot be null", nameof(attributeName));
}

return $"extension_{_b2cExtensionAppClientId}_{attributeName}";
}
}
}
37 changes: 37 additions & 0 deletions src/Helpers/PasswordHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;

namespace b2c_ms_graph.Helpers
{
public static class PasswordHelper
{
public static string GenerateNewPassword(int lowercase, int uppercase, int numerics)
{
string lowers = "abcdefghijklmnopqrstuvwxyz";
string uppers = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string number = "0123456789";

Random random = new Random();

string generated = "!";
for (int i = 1; i <= lowercase; i++)
generated = generated.Insert(
random.Next(generated.Length),
lowers[random.Next(lowers.Length - 1)].ToString()
);

for (int i = 1; i <= uppercase; i++)
generated = generated.Insert(
random.Next(generated.Length),
uppers[random.Next(uppers.Length - 1)].ToString()
);

for (int i = 1; i <= numerics; i++)
generated = generated.Insert(
random.Next(generated.Length),
number[random.Next(number.Length - 1)].ToString()
);

return generated.Replace("!", string.Empty);
}
}
}
3 changes: 3 additions & 0 deletions src/Models/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ public class AppSettings
[JsonProperty(PropertyName = "ClientSecret")]
public string ClientSecret { get; set; }

[JsonProperty(PropertyName = "B2cExtensionAppClientId")]
public string B2cExtensionAppClientId { get; set; }

[JsonProperty(PropertyName = "UsersFileName")]
public string UsersFileName { get; set; }

Expand Down
8 changes: 8 additions & 0 deletions src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ static async Task Main(string[] args)
case "6":
await UserService.BulkCreate(config, graphClient);
break;
case "7":
await UserService.CreateUserWithCustomAttribute(graphClient, config.B2cExtensionAppClientId, config.TenantId);
break;
case "8":
await UserService.ListUsersWithCustomAttribute(graphClient, config.B2cExtensionAppClientId);
break;
case "help":
Program.PrintCommands();
break;
Expand Down Expand Up @@ -91,6 +97,8 @@ private static void PrintCommands()
Console.WriteLine("[4] Delete user by object ID");
Console.WriteLine("[5] Update user password");
Console.WriteLine("[6] Create users (bulk import)");
Console.WriteLine("[7] Create user with custom attributes and show result");
Console.WriteLine("[8] Get all users (one page) with custom attributes");
Console.WriteLine("[help] Show available commands");
Console.WriteLine("[exit] Exit the program");
Console.WriteLine("-------------------------");
Expand Down
129 changes: 127 additions & 2 deletions src/Services/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Graph;
Expand Down Expand Up @@ -32,6 +33,40 @@ public static async Task ListUsers(GraphServiceClient graphClient)
}
}

public static async Task ListUsersWithCustomAttribute(GraphServiceClient graphClient, string b2cExtensionAppClientId)
{
if (string.IsNullOrWhiteSpace(b2cExtensionAppClientId))
{
throw new ArgumentException("B2cExtensionAppClientId (its Application ID) is missing from appsettings.json. Find it in the App registrations pane 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($"Getting list of users with the custom attributes '{customAttributeName1}' (string) and '{customAttributeName2}' (boolean)");
Console.WriteLine();

// Get all users (one page)
var result = await graphClient.Users
.Request()
.Select($"id,displayName,identities,{favouriteSeasonAttributeName},{lovesPetsAttributeName}")
.GetAsync();

foreach (var user in result.CurrentPage)
{
Console.WriteLine(JsonConvert.SerializeObject(user));

// Only output the custom attributes...
//Console.WriteLine(JsonConvert.SerializeObject(user.AdditionalData));
}
}

public static async Task GetUserById(GraphServiceClient graphClient)
{
Console.Write("Enter user object ID: ");
Expand Down Expand Up @@ -135,7 +170,7 @@ public static async Task SetPasswordByUserId(GraphServiceClient graphClient)

var user = new User
{
PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword",
PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword",
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = false,
Expand Down Expand Up @@ -202,5 +237,95 @@ public static async Task BulkCreate(AppSettings config, GraphServiceClient graph
}
}
}

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 = "[email protected]"
}
},
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();
}
}
}
}
}
1 change: 1 addition & 0 deletions src/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"TenantId": "your-b2c-tenant.onmicrosoft.com",
"AppId": "Application (client) ID",
"ClientSecret": "Client secret",
"B2cExtensionAppClientId": "Find this Application (client) ID in the App registrations pane in the Azure portal. The app registration is named 'b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.'.",
"UsersFileName": "users.json"
}
}
4 changes: 2 additions & 2 deletions src/b2c-ms-graph.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>netcoreapp3.1</TargetFramework>
<RootNamespace>b2c_ms_graph</RootNamespace>
</PropertyGroup>

Expand All @@ -13,7 +13,7 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.2" />
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.3" />
<PackageReference Include="Microsoft.Graph.Beta" Version="0.12.0-preview" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.8.2" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.9.0" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit c4ad778

Please sign in to comment.