I've been experimenting with the new Blazor features and I'm attempting to pull user data from our Azure AD into a test app. These are the relevant snippets:
My Service
public class UserService
{
GraphServiceClient _graphClient { get; set; }
protected User _user = null;
public UserService(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public string GetUserName()
{
return User()?.DisplayName ?? "";
}
Startup
public void ConfigureServices(IServiceCollection services)
{
var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
// Add sign-in with Microsoft
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
// Add the possibility of acquiring a token to call a protected web API
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
// Enables controllers and pages to get GraphServiceClient by dependency injection
// And use an in memory token cache
.AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
services.AddSingleton<WeatherForecastService>();
services.AddScoped<UserService>();
The GraphServiceClient does get initialized in my.cs script but I get the error message:
Error: No account or login hint was passed to the AcquireTokenSilent call
Its not a problem (I think) with any azure configuration as everything works fine if I use the Microsoft sample and make a ComponentBase.
public class UserProfileBase : ComponentBase
{
[Inject]
GraphServiceClient GraphClient { get; set; }
protected User _user = new User();
protected override async Task OnInitializedAsync()
{
await GetUserProfile();
}
/// <summary>
/// Retrieves user information from Microsoft Graph /me endpoint.
/// </summary>
/// <returns></returns>
private async Task GetUserProfile()
{
try
{
var request = GraphClient.Me.Request();
_user = await request.GetAsync();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
My current thought is that the Authorize tag that the profile component uses (and thus the ComponentBase?) is doing something behind the scenes with the access token even though I am already authenticated?
@page "/profile"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
@inherits UserProfileBase
public class UserAccount
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
public string Email {get; set;}
}
public class UserService
{
GraphServiceClient _graphClient;
protected UserAccount _user {get; set;}
public UserService(GraphServiceClient graphClient)
{
_graphClient = graphClient;
}
public async Task<string> GetUserName()
{
UserAccount = await GetUserAsync();
return $"{UserAccount.FirstName} {UserAccount.LastName}";
}
public async Task<UserAccount> GetUserAsync()
{
var user = awiat __graphClient.Me.Request.Select( e => new
{
e.Id,
e.GivenName,
e.Surname,
e.Identities,
}).GetAsync();
if(user != null)
{
var email = user.Identities.ToList().FirstOrDefault(x => x.SignInType == "emailAddress")?.IssuerAssignedId;
return new UserAccount
{
Id= user.Id,
FirstName= user.GivenName,
LastName= user.Surname,
Email= email
};
}
else {return null;}
}
}
Sorry this answer is over a year late - hopefully this will help someone else in the future. I was trying to solve this exact issue today too, and I got the missing pieces of this puzzle from this demo project .
In addition to your ConfigureServices
method, you need to make sure that you have controllers mapped in your endpoints so that the Identity UI can map responses.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers(); // Important for Microsoft Identity UI
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
And then you need to have some exception handling on your scoped service. I'm pretty sure this is because of Blazor's pre-rendering feature not initially authenticating the user in the first render. But don't quote me on that
I can't see enough of the OP's service, so here's mine:
using Microsoft.Graph;
using Microsoft.Identity.Web;
namespace MyProject.Services
{
public class UserService
{
private readonly GraphServiceClient _graphServiceClient;
private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;
public UserService(
GraphServiceClient graphServiceClient,
MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler)
{
_graphServiceClient = graphServiceClient;
_consentHandler = consentHandler;
}
public async Task<User?> GetUserAsync()
{
try
{
return await _graphServiceClient.Me.Request().GetAsync();
}
catch (Exception ex)
{
_consentHandler.HandleException(ex);
return null;
}
}
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.