I have a blazor server application with authentication (single user accounts). The plan was to leave the default EF database handling all the authentication stuff and have a separate SQL database with the application data, which I access via my own data access layer.
The EF database stores email address to the username and displays this as "Hello, [email protected]" if a user is logged in. Since I store the first and last name of the user in my application database, I wanted the text to change to display the first name: "Hello, abc!"
For some reason in a blazor project we have:
- Shared/LoginDisplay.razor
- Pages/Shared/_LoginPartial.cshtml
- Areas/Pages/Shared_LoginPartial.cshtm
In the Shared/LoginDisplay.razor file I managed to display the first name as follows (IUserData coming from my data access lib):
@inject UserManager<IdentityUser> _userManager
@inject AuthenticationStateProvider _authenticationStateProvider
@inject IUserData _userData;
<AuthorizeView>
<Authorized>
<a href="Identity/Account/Manage">Hello, @displayUserName</a>
<form method="post" action="Identity/Account/LogOut">
<button type="submit" >Log out</button>
</form>
</Authorized>
<NotAuthorized>
<a href="Identity/Account/Register">Register</a>
<a href="Identity/Account/Login">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code {
private string displayUserName;
protected override async Task OnInitializedAsync()
{
displayUserName = "";
var user = (await _authenticationStateProvider.GetAuthenticationStateAsync()).User;
if (user.Identity.IsAuthenticated)
{
string userID = _userManager.GetUserId(user);
displayUserName = (await _userData.GetUserByID(userID)).FirstName;
}
}
}
But the two .cshtml
use different methods:
Pages/Shared/_LoginPartial
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
<ul >
@if (SignInManager.IsSignedIn(User))
{
<li >
<a id="manage" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
...
}
...
</ul>
Areas/Identity/Pages/Shared/_LoginPartial.cshtml
@using Microsoft.AspNetCore.Identity
@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<ul >
@if (SignInManager.IsSignedIn(User))
{
<li >
<a asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
</li>
..
}
..
</ul>
Where does User
come from?(There should be a model somewhere, right?) Why are there three different LoginPartial
files in the first place (this is just the top of the page, showing login and logout buttons, the actual login page is in yet another file)?
How can I execute the same (async) method as in a .razor
page?
CodePudding user response:
You just need to inject the UserManager class and SignInManager onto whatever page you wanting to access that layer.
@using Microsoft.AspNetCore.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
You can than access your account fields. I think do something like this to get UserName
<ul >
@if (SignInManager.IsSignedIn(User))
{
<li >
<a id="manage" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @UserManager.GetUserName(User)!</a>
</li>
<li >
<form id="logoutForm" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="@Url.Page("/Index", new { area = "" })">
<button id="logout" type="submit" >Logout</button>
</form>
</li>
}
else
{
<li >
<a id="register" asp-area="Identity" asp-page="/Account/Register">Register</a>
</li>
<li >
<a id="login" asp-area="Identity" asp-page="/Account/Login">Login</a>
</li>
}
</ul>
This does a check to see if user is logged in. If they are not logged in it will display the else statement. If they are logged in it will display the other options.
If you want to access other layers inject a service into your front end and do something like this.
public class DashboardService
{
#region Private Variables
private readonly IHttpContextAccessor _accessor;
private readonly LinkGenerator _generator;
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly ILogger<DashboardService> _logger;
public DashboardService(ApplicationDbContext context,
UserManager<ApplicationUser> userManager,
IHttpContextAccessor accessor,
LinkGenerator generator,
ILogger<DashboardService> logger)
{
_context = context;
_userManager = userManager;
_accessor = accessor;
_generator = generator;
_logger = logger;
}
public async Task<string> GetuserInfo(ClaimsPrincipal userClaims)
{
//if user is not logged in this will return an error, but do we even allow this?
var user = await _userManager.GetUserAsync(userClaims);
string name = user.FirstName user.LastName;
return name;
}
}
Then on the blazor page inject this service.
@inherits OwningComponentBase<DashboardService>
and third method. You can add the AuthenticationState on blazor page in the code section like this.
@code{
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
var user = (await authenticationStateTask).User;
deposits = await Service.GetDepositsAsync(user);
withdrawals = await Service.GetWithdrawalsAsync(user);
}
}