Get the ID of the current logged in user with Blazor Server
Asked Answered
M

4

9

I have a Blazor Server application and need to get the current user ID. Its easy to get the email and the user name, but not the ID. It was so easy in .net core 2.2. Why they did not expose the ID is beyond me. I am using .net core 5.0.

Multidisciplinary answered 10/5, 2021 at 7:5 Comment(0)
M
0

I've needed to do this recently, but in a newer Blazor Web App, using .Net 8.0, so thought I'd add my more recent example here.

I didn't want to copy/paste the same code into multiple pages, so came up with this:

Create the interface:

public interface IUserIdentityProcessor
{
    Task<string?> GetCurrentUserId();
}

Create the class:

using Microsoft.AspNetCore.Components.Authorization;

public class UserIdentityProcessor : IUserIdentityProcessor
{
    private readonly AuthenticationStateProvider _authenticationStateAsync;

    public UserIdentityProcessor(AuthenticationStateProvider authenticationStateAsync)
    {
        this._authenticationStateAsync = authenticationStateAsync;
    }

    public async Task<string?> GetCurrentUserId()
    {
        var authstate = await this._authenticationStateAsync.GetAuthenticationStateAsync();

        if (authstate == null)
        {
            return null;
        }

        var user = authstate.User;

        return user.FindFirst(u => u.Type.Contains("nameidentifier"))?.Value;
    }
}

Add the IoC line to your Program.cs:

builder.Services.AddScoped<IUserIdentityProcessor, UserIdentityProcessor>();

In your Blazor RazorComponent, add the using statement to the top of the page:

@inject IUserIdentityProcessor _userIdentityProcessor

...add this to your page load:

protected override async Task OnInitializedAsync()
{
    var userId = this._userIdentityProcessor.GetCurrentUserId();
}

The result is a reusable class to get the user's Id (guid) and return it as a string. This can be called from a number of razor components/pages, and expanded to return more details from the user object, if needed.

Murat answered 29/5 at 15:55 Comment(1)
Thanks for this much need update for the .net 8 Blazor Webapp template! Much appreciated.Multidisciplinary
G
13

There are a couple ways to do it. Here's one I found with a little hacking around. I like it because it only requires a single injection, and because ASP also handles roles. Surprisingly, "nameidentifier" is the UserId (which is a GUID) in a standard EF Core login:

@inject AuthenticationStateProvider _authenticationStateProvider
        
@code {
    async Task<string> getUserId(){
        var user = (await _authenticationStateProvider.GetAuthenticationStateAsync()).User;
        var UserId = user.FindFirst(u => u.Type.Contains("nameidentifier"))?.Value;
        return UserId;
    }
}

I recommend in Visual Studio setting a break point after retrieving the user, and then hovering over it. That will allow you to inspect it and see all the little bits and pieces-- you'll be surprised how much neat information you can dig up in the User object!

Gyroplane answered 10/5, 2021 at 11:34 Comment(6)
After nearly a full day of pulling my hair out, woke up this morning to this gift! It works like a charm, and then some!! Thank you ever so much!Multidisciplinary
Any idea how to get the username in the Program.cs where @inject can't be used?Vestavestal
Program.cs is setting up your environment. Trying to get the username at that point is kind of like trying to find out who lives in in a house while it's being built. Nobody lives there, because it hasn't been built yet.Gyroplane
@Bennyboy1973, I understand what you are saying but if you put a breakpoint on the first line of the program.cs and debug the app, you get the log in window then, it hits break point. Just doesn't make sense.Wynd
This worked for me except in .NET 6 I had to use the Type "sub" instead of "nameidentifier". Might be because I'm using social / external login? user.FindFirst(u => u.Type.Contains("sub"))?.ValuePatrickpatrilateral
You can also consider cascading your AuthenticationState instead of injecting it on every component that uses it: learn.microsoft.com/en-us/aspnet/core/blazor/security/…Gyroplane
S
2

The documentation indicates to use the Task<AuthenticationState> approach instead of using AuthenticationStateProvider directly since the component isn't notified automatically if the underlying authentication state data changes.

@code {
    [CascadingParameter]
    private Task<AuthenticationState> authenticationStateTask { get; set; }

    async Task<string> getUserName(){
        var user = (await authenticationStateTask).User;
        return user.Identity.Name;
    }
    async Task<string> getUserId(){
        var user = (await authenticationStateTask).User;
        var userid = user.FindFirst(u => u.Type.Contains("nameidentifier"))?.Value;
        return userid;
    }           
}
S answered 21/11, 2022 at 6:31 Comment(2)
The OP asked for the ID specifically. AuthenticationState returns a ClaimsPrincipal which doesn't contain the ID. I needed to add a line from @BennyBoy1973: var UserId = user.FindFirst(u => u.Type.Contains("nameidentifier"))?.Value;Loose
Both my Username and UserID are same value, what's the reason do you guess?Municipalize
M
0

I've needed to do this recently, but in a newer Blazor Web App, using .Net 8.0, so thought I'd add my more recent example here.

I didn't want to copy/paste the same code into multiple pages, so came up with this:

Create the interface:

public interface IUserIdentityProcessor
{
    Task<string?> GetCurrentUserId();
}

Create the class:

using Microsoft.AspNetCore.Components.Authorization;

public class UserIdentityProcessor : IUserIdentityProcessor
{
    private readonly AuthenticationStateProvider _authenticationStateAsync;

    public UserIdentityProcessor(AuthenticationStateProvider authenticationStateAsync)
    {
        this._authenticationStateAsync = authenticationStateAsync;
    }

    public async Task<string?> GetCurrentUserId()
    {
        var authstate = await this._authenticationStateAsync.GetAuthenticationStateAsync();

        if (authstate == null)
        {
            return null;
        }

        var user = authstate.User;

        return user.FindFirst(u => u.Type.Contains("nameidentifier"))?.Value;
    }
}

Add the IoC line to your Program.cs:

builder.Services.AddScoped<IUserIdentityProcessor, UserIdentityProcessor>();

In your Blazor RazorComponent, add the using statement to the top of the page:

@inject IUserIdentityProcessor _userIdentityProcessor

...add this to your page load:

protected override async Task OnInitializedAsync()
{
    var userId = this._userIdentityProcessor.GetCurrentUserId();
}

The result is a reusable class to get the user's Id (guid) and return it as a string. This can be called from a number of razor components/pages, and expanded to return more details from the user object, if needed.

Murat answered 29/5 at 15:55 Comment(1)
Thanks for this much need update for the .net 8 Blazor Webapp template! Much appreciated.Multidisciplinary
R
0

When you create a new Blazor Server project (.NET 8 & 9), you can select the option Auth: Individual Accounts, this will automatically create the authentication and authorization (login, registration, account management and deletion).

There is a class that is created

"IdentityUserAccessor.cs"

using Microsoft.AspNetCore.Identity;

namespace App.Components.Account
{
    internal sealed class IdentityUserAccessor(UserManager<User> userManager, IdentityRedirectManager redirectManager)
    {
        public async Task<User> GetRequiredUserAsync(HttpContext context)
        {
            var user = await userManager.GetUserAsync(context.User);

            if (user is null)
            {
                redirectManager.RedirectToWithStatus("Account/InvalidUser", $"Error: Unable to load user with ID '{userManager.GetUserId(context.User)}'.", context);
            }

            return user;
        }
    }

Component Usage Example:

@page "/Account/Manage/Disable2fa"

@using Microsoft.AspNetCore.Identity
@using App.Data

@inject UserManager<User> UserManager
@inject IdentityUserAccessor UserAccessor

@code {
    private User user = default!;

    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    protected override async Task OnInitializedAsync()
    {
        user = await UserAccessor.GetRequiredUserAsync(HttpContext);

        if (HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user))
        {
            throw new InvalidOperationException("Cannot disable 2FA for user as it's not currently enabled.");
        }
    }
}

The User.cs model:

using App.Data.Models;
using Microsoft.AspNetCore.Identity;

namespace App.Data
{
    // Add profile data for application users by adding properties to the User class
    public class User : IdentityUser
    {
    }

}
Reduplicate answered 15/6 at 15:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.