1

I have a new Blazor Server App - using default VS2019 template (counter application example). I have enabled Windows Authentication on the app. When the app loads on my server it shows the domain/username in top right of screen which is great. I want to get the user name in the in my c# code sop I can build logic around this, eg if user is X do Y.

So I added the this to my startup file services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

and try and access the username via: string userName = _httpContextAccessor.HttpContext.User.Identity.Name;

However I keep getting a 405 error. See attached screen shot of code, error and settings

Any help would be much appreciated

TIA

Screen Shot

user13714356
  • 99
  • 1
  • 7

1 Answers1

1

You can't access the HttpContext object in Blazor Server App as the protocol used is SignalR, except for the initial request which is always HTTP, in which case, you can grab the HttpContext object (before the Blazor App is instantiated), extract values, and pass them to your Blazor App. See this answer how to do that...

You can, however, inject the AuthenticationStateProvider service into your components, and access the ClaimsPrincipal (magically converted from the WindowsPrincipal) object for the claims, as the code below demonstrate.

Copy, run and test...

@page "/"

@using System.Security.Claims;

<div>@output</div>

@if (claims.Count() > 0)
{
    <table class="table">
        @foreach (var claim in claims)
        {
            <tr>
                <td>@claim.Type</td>
                <td>@claim.Value</td>
            </tr>
        }
    </table>
}

@code{


    private string output;
    private IList<Claim> claims;
    [Inject] AuthenticationStateProvider AuthenticationStateProvider { get; set; }

    protected override async Task OnInitializedAsync()
    {

        var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            output = $"{user.Identity.Name} is authenticated.";
            claims = user.Claims.ToList();

        }
        else
        {
            output = "The user is not authenticated.";
        }
    }
}

UPDATE:

Actually, there are three ways to do that. The first one above, which is not recommended, as the AuthenticationState is never updated after it has been accessed. You'll need to refresh the page to access a new instance of it.

The second way to do that is to cascade the a Task of the AuthenticationState into your component, await the Task, and then access the AuthenticationState. Here's how you can do that:

@page "/"

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

   protected async override Task OnInitializedAsync()
    {
        // Await Task<AuthenticationState> 
        // and then retrieve the ClaimsPrincipal
        var user = (await authenticationStateTask).User;

        if (user.Identity.IsAuthenticated)
         {
                // Assign the user name to the 
                UserName = user.Identity.Name;
         }
    } 
}

Note that Task<AuthenticationState> is cascaded by the CascadingAuthenticationState component, residing in the App component (App.razor). The CascadingAuthenticationState component contains code that stream down the newly changed AuthenticationState, whenever it changes.

The third way is to create a class service as follow:

public class UsertService
{
    private readonly AuthenticationStateProvider authProvider;
   
    public UserService (AuthenticationStateProvider authProvider)
    {
        this.authProvider = authProvider;
    }

    public async Task<string> GetUserName()
    {
        var authenticationStateTask = await 
                   authProvider.GetAuthenticationStateAsync();
                 
        var user = authenticationStateTask.User;
        
        if (user.Identity.IsAuthenticated)
        {
            // Executed only when the user is authenticated
            return user.Identity.Name;
        }

        return "User not authenticated";
    }
}

Note that you may extend that class to provide other values, etc.

Now you have to add this service as Scoped in the Startup class, as follows:

public void ConfigureServices(IServiceCollection services)
{
  // Code removed...
     services.AddScoped<UserService>();

}

And this is how you use it in your components:

@page "/"
@inject UserService UserService


<div>Your user name: @name</div>


@code
{
    private string name;

    protected override async Task OnInitializedAsync()
    {
        name = await UserService.GetUserName();
    }
   
}

Note: If you need more help, you may contact me through my email. See in profile...

enet
  • 41,195
  • 5
  • 76
  • 113
  • Hi Thanks for this, you are a star! It really helped Is there anyway I can wrap this into a class file. So I just call one method that will return user.Identity.Name as a string. As I may need to check who the users is in various components parts of my app – user13714356 Dec 27 '21 at 11:38
  • You can do that in various ways, as for instance, you can define a class service into which you inject the AuthenticationStateProvider service, and exposing a property named UserName. Would you please upvote my answer: Click on the up image located above the 0 character. – enet Dec 27 '21 at 12:26
  • I dont suppose you have an example of how to create such a class.... im new to Blazor and I keep getting errors. I tried to follow this example but no joy: https://stackoverflow.com/questions/59736931/how-to-inject-service-authenticationstateprovider-in-blazor-class Sure, I have clicked the Up arrow, I didnt realise i needed to do this - my bad. Thanks ever so much for your help – user13714356 Dec 27 '21 at 14:34
  • `I have clicked the Up arrow` No, you didn't. On the left side of the answer is an up image, below which is the string `0`, and then down image, below which is a green-ticked image, indicating that that answer has been accepted. When you click on the up image you upvote the answer, in addition to accepting it... you'll need to see the string `0` changes to `1` ....... I'll create the class you need and add it to the end of my answer. – enet Dec 27 '21 at 17:55
  • Hi, I clicked it several times but i get a message saying "Thanks for the feedback! You need at least 15 reputation to cast a vote, but your feedback has been recorded." It flashes to 1 but then changes to 0 again I am forever grateful for your amazing help. Let m know if there anything else I need to do to show my appreciation. Thanks once again – user13714356 Dec 27 '21 at 19:13
  • I didn't know about this limitation... However, you can now do that as I upvoted your question, so that now you've 15 scores. – enet Dec 27 '21 at 19:40
  • 1
    Hi, I just tried it again and the number 1 has stayed this time Thanks ever so much for your help on this - its been amazing. Wishing you a Merry Christmas and an amazing New Year – user13714356 Dec 27 '21 at 19:42