Skip to content

Instantly share code, notes, and snippets.

@rbrayb
Created June 13, 2025 05:12
Show Gist options
  • Save rbrayb/a80ae95c37e63d00e2ec0ee9ed3db15e to your computer and use it in GitHub Desktop.
Save rbrayb/a80ae95c37e63d00e2ec0ee9ed3db15e to your computer and use it in GitHub Desktop.
Provisioning user MFA programatically in Azure AD B2C
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Graph.Models.ODataErrors;
using Microsoft.Identity.Client;
using System;
using System.Threading.Tasks;
namespace GraphPhoneAuthenticationDemo
{
class Program
{
// Asynchronous Main to run our sample.
static async Task Main(string[] args)
{
// Usage: dotnet run [new|update|delete] <userId> [newPhoneNumber]
if (args.Length < 2 || (args[0] != "list" && args[0] != "new" && args[0] != "update" && args[0] != "delete"))
{
Console.WriteLine("Usage: ManagePhoneNumber [list|new|update|delete] userObjectID [phoneNumber] [B|M]");
Console.WriteLine(" where B=Business phone and M=Mobile phone");
Console.WriteLine("\nExample: ManagePhoneNumber new 12345678-1234-1234-1234-123456789012 +645555551234 M");
return;
}
// Replace with your Azure AD app registration details.
var clientId = "cd...05";
var tenantId = "65...16";
var clientSecret = "iK...wy";
// Instantiate our helper with the required credentials.
var graphHelper = new GraphHelper(clientId, tenantId, clientSecret);
// Get userId and newPhoneNumber from command line
string userId = args[1];
// Choose the type of phone number to update:
// For mobile phone: "3179e48a-750b-4051-897c-87b9720928f7"
// For business/office phone: "e37fc753-ff3b-4958-9484-eaa9425c82bc"
string phoneType = args[3];
if (phoneType != "B" && phoneType != "M")
{
Console.WriteLine("\nInvalid phone type. Use 'B' for business phone or 'M' for mobile phone.");
return;
}
string phoneMethodId = String.Empty;
if (phoneType == "B")
{
// Business phone method ID
phoneMethodId = "e37fc753-ff3b-4958-9484-eaa9425c82bc";
}
else if (phoneType == "M")
{
// Mobile phone method ID
phoneMethodId = "3179e48a-750b-4051-897c-87b9720928f7";
}
string newPhoneNumber = args.Length > 2 ? args[2] : null;
try
{
if (args[0] == "list")
{
if (string.IsNullOrEmpty(userId))
{
Console.WriteLine("For list, you must provide a user ID.");
return;
}
// List all phone authentication methods for the user.
await graphHelper.ListPhoneAuthenticationMethodsAsync(userId);
}
else if (args[0] == "update")
{
if (string.IsNullOrEmpty(newPhoneNumber) || string.IsNullOrEmpty(userId))
{
Console.WriteLine("For update, you must provide a user ID and phone number.");
return;
}
await graphHelper.UpdatePhoneAuthenticationMethodAsync(userId, phoneMethodId, newPhoneNumber);
}
else if (args[0] == "new")
{
if (string.IsNullOrEmpty(newPhoneNumber) || string.IsNullOrEmpty(userId))
{
Console.WriteLine("For new, you must provide a user ID and new phone number.");
return;
}
await graphHelper.AddPhoneAuthenticationMethodAsync(userId, newPhoneNumber);
}
else if (args[0] == "delete")
{
if (string.IsNullOrEmpty(newPhoneNumber) || string.IsNullOrEmpty(userId))
{
Console.WriteLine("For delete, you must provide a user ID and phone number.");
return;
}
await graphHelper.DeletePhoneAuthenticationMethodAsync(userId, phoneMethodId);
}
}
catch (Exception ex)
{
if (ex is ODataError odataError)
{
Console.WriteLine("\nODataError Code: " + odataError.Error.Code);
Console.WriteLine("\nODataError Message: " + odataError.Error.Message);
}
else
{
Console.WriteLine("\nException: " + ex.Message);
}
}
}
}
public class GraphHelper
{
private readonly GraphServiceClient _graphClient;
public GraphHelper(string clientId, string tenantId, string clientSecret)
{
// Use Azure.Identity's ClientSecretCredential for authentication.
var credential = new ClientSecretCredential(tenantId, clientId, clientSecret);
_graphClient = new GraphServiceClient(credential);
}
/// <summary>
/// Retrieves and displays all phone authentication methods for a given user.
/// </summary>
/// <param name="userId">The user's object id or UPN.</param>
public async Task ListPhoneAuthenticationMethodsAsync(string userId)
{
var methods = await _graphClient.Users[userId]
.Authentication
.PhoneMethods
.GetAsync();
Console.WriteLine("\nCurrent phone authentication methods:");
bool foundPhoneNumber = false;
if (methods?.Value != null)
{
foreach (var method in methods.Value)
{
if (string.IsNullOrEmpty(method.PhoneNumber))
{
Console.WriteLine($"Method Id/Type: {method.Id}, Phone Number: Not set");
continue;
}
else
{
foundPhoneNumber = true;
Console.WriteLine($"Method Id/Type: {method.Id}, Phone Number: {method.PhoneNumber}");
}
}
}
if (!foundPhoneNumber)
{
Console.WriteLine("\nNo phone authentication methods with a phone number found for this user.");
}
}
/// <summary>
/// Updates a specific phone authentication method with a new phone number.
/// </summary>
/// <param name="userId">The user's object id or UPN.</param>
/// <param name="phoneMethodId">
/// The identifier for the phone authentication method to update.
/// (e.g., mobile phone: "3179e48a-750b-4051-897c-87b9720928f7", business phone: "e37fc753-ff3b-4958-9484-eaa9425c82bc")
/// </param>
/// <param name="newPhoneNumber">The new phone number to set (properly formatted, e.g., "+1 5555551234").</param>
public async Task UpdatePhoneAuthenticationMethodAsync(string userId, string phoneMethodId, string newPhoneNumber)
{
var updateData = new PhoneAuthenticationMethod
{
PhoneNumber = newPhoneNumber
};
try
{
await _graphClient.Users[userId]
.Authentication
.PhoneMethods[phoneMethodId]
.PatchAsync(updateData);
Console.WriteLine("Phone number updated successfully.");
}
catch (Exception ex)
{
if (ex is ODataError odataError)
{
Console.WriteLine("\nODataError Code: " + odataError.Error.Code);
Console.WriteLine("\nODataError Message: " + odataError.Error.Message);
}
else
{
Console.WriteLine("\nException: " + ex.Message);
}
}
}
/// <summary>
/// Adds a new phone authentication method for a given user.
/// </summary>
/// <param name="userId">The user's object id or UPN.</param>
/// <param name="newPhoneNumber">The new phone number to add (properly formatted, e.g., "+1 5555551234").</param>
public async Task AddPhoneAuthenticationMethodAsync(string userId, string newPhoneNumber)
{
var newPhoneMethod = new PhoneAuthenticationMethod
{
PhoneNumber = newPhoneNumber,
PhoneType = AuthenticationPhoneType.Mobile, // or .AlternateMobile, .Office
SmsSignInState = AuthenticationMethodSignInState.NotSupported // or Supported, if needed
};
try
{
await _graphClient.Users[userId]
.Authentication
.PhoneMethods
.PostAsync(newPhoneMethod);
Console.WriteLine("\nNew phone authentication method added successfully.");
}
catch (Exception ex)
{
if (ex is ODataError odataError)
{
Console.WriteLine("\nODataError Code: " + odataError.Error.Code);
Console.WriteLine("\nODataError Message: " + odataError.Error.Message);
}
else
{
Console.WriteLine("\nException: " + ex.Message);
}
}
}
/// <summary>
/// Deletes a specific phone authentication method for a given user.
/// </summary>
/// <param name="userId">The user's object id or UPN.</param>
/// <param name="phoneMethodId">The identifier for the phone authentication method to delete.</param>
public async Task DeletePhoneAuthenticationMethodAsync(string userId, string phoneMethodId)
{
try
{
await _graphClient.Users[userId]
.Authentication
.PhoneMethods[phoneMethodId]
.DeleteAsync();
Console.WriteLine("\nPhone authentication method deleted successfully.");
}
catch (Exception ex)
{
if (ex is ODataError odataError)
{
Console.WriteLine("\nODataError Code: " + odataError.Error.Code);
Console.WriteLine("\nODataError Message: " + odataError.Error.Message);
}
else
{
Console.WriteLine("\nException: " + ex.Message);
}
}
}
}
}
@rbrayb
Copy link
Author

rbrayb commented Jun 13, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment