I am currently facing an issue with acquiring an access token for an Azure Function after authenticating the user via Azure AD B2C. The user successfully logs in, but when attempting to trigger an Azure Function that requires an access token, I receive the following error:
Error is happening on this line of the code
// Try acquiring the access token silently
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
Error Message:
pgsql
Copy
MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
Steps to Reproduce:
The user authenticates via Azure AD B2C and completes the sign-in process.
After successful authentication, the application attempts to acquire an access token for an Azure Function using the ITokenAcquisition.GetAccessTokenForUserAsync()
method.
The following error occurs: MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.
What I Have Tried:
I have configured both Access Tokens and ID Tokens in the Azure AD B2C app registration under the Tokens section.
The Azure AD B2C app has been granted permission to access the Azure Function API with the correct scope (api://{azure-function-client-id}/.default
).
I have attempted to handle the MsalUiRequiredException
by triggering a reauthentication flow, but the issue persists.
Expected Behavior: After authenticating the user, I expect the app to acquire an access token for the Azure Function silently (if possible). If the access token cannot be acquired silently, the app should handle this by prompting the user for consent or reauthentication.
Code where the issue occurs:
csharp
Copy
option.Events.OnTokenValidated = async context =>
{
// Extract user claims (for example, user principal, object ID, etc.)
var claims = context.Principal.Claims;
var signClaimUser = new SignClaimModel
{
email = claims.FirstOrDefault(c => c.Type == "emails" || c.Type == "email")?.Value,
givenName = claims.FirstOrDefault(c => c.Type == "given_name")?.Value,
surname = claims.FirstOrDefault(c => c.Type == "family_name")?.Value,
objectId = claims.FirstOrDefault(c => c.Type == "oid")?.Value,
userPrincipalName = claims.FirstOrDefault(c => c.Type == "upn")?.Value,
tenantId = claims.FirstOrDefault(c => c.Type == "tid")?.Value,
};
// Serialize the model
var json = JsonConvert.SerializeObject(signClaimUser);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Prepare HTTP client to call the Azure Function
using var httpClient = new HttpClient();
// Get the function URL and key from your appsettings.json
var functionBaseUrl = builder.Configuration["AzureFunction:FunctionUrl"]?.TrimEnd('/');
var functionKey = builder.Configuration["AzureFunction:FunctionKey"];
var functionUrl = $"{functionBaseUrl}?code={functionKey}";
// Retrieve ITokenAcquisition from the DI container to acquire a token
var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>();
// The scope for the Azure Function (make sure your Azure Function API client ID is correctly set in appsettings.json)
var azureFunctionApiClientId = builder.Configuration["AzureAdB2CAzureFunctionAPI:ClientId"];
string[] scopes = new[] { $"api://{azureFunctionApiClientId}/.default" };
try
{
// Try acquiring the access token silently
string accessToken = await tokenAcquisition.GetAccessTokenForUserAsync(scopes);
// If we get a valid access token, add it to the authorization header for the request
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// Call the Azure Function
var response = await httpClient.PostAsync(functionUrl, content);
// Optionally, handle the response (e.g., logging, error handling, etc.)
if (!response.IsSuccessStatusCode)
{
var errorMessage = await response.Content.ReadAsStringAsync();
Console.WriteLine($"Error calling Azure Function: {errorMessage}");
}
}
catch (MsalUiRequiredException)
{
// This exception occurs if the user needs to reauthenticate and provide consent
// Trigger the user sign-in process interactively
var redirectUrl = context.HttpContext.Request.Scheme + "://" + context.HttpContext.Request.Host.Value + "/signin-oidc"; // Your redirect URI
// Redirect the user to the sign-in page
context.Response.Redirect(redirectUrl);
}
catch (Exception ex)
{
Console.WriteLine($"Error acquiring token or calling Azure Function: {ex.Message}");
}
};
Environment:
- Azure AD B2C User Flow: ["SignUpSignIn" flow]
- Microsoft.Identity.Web Version: [3.8.3 number of the library being used]
- .NET Version: [Provide .NET version, e.g., .NET 9]