c# - Getting username of logged in user with NegotiateWindows domain credentials - Stack Overflow
What I thought would be a very simple task has turned out to be the opposite of that.
I'm building a fairly simple web app with blazor and I've got windows domain credential based authentication working fine with Negotiate. That was fairly simple to set up.
Now let's say I want to display a "Welcome: " type of message on a banner or include the username to keep track of form submissions. Every solution I've come across seems to suggest doing something like this directly in the component I need to access the username in.
Normally, I would just store the username string that was entered in the username field, but since Negotiate uses that browser popup window for logins instead of a web page, I don't think that's possible. (maybe I'm wrong?)
This isn't my exact code but it's basically what I'm trying to do. This works in MainLayout.razor but any other component inside of it will hit a null value exception.
@page "/"
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Authorization
@inject AuthenticationStateProvider AuthStateProvider
@attribute [Authorize]
<h1> Welcome Back: @username <h1>
@code{
public string? username;
protected override async OnInitializedAsync(){
var authstate = await AuthStateProvider.GetAuthenticationStateAsync();
username = authstate.User.Identity.Name.Substring(9);
}
}
I think the issue may be the fact that the razor components are rendering twice which is causing the authstate variable to pull twice resulting in the antiforgery errors I keep getting.
I really think the key here is somehow forcing my components to only render once. That way, I only need to pull the auth state at the time of login and cache only what I need from it so I can access it in all of my components without causing errors. Performance is not a concern at all for me with this project. It's a very lightweight application serving a very small number of users so I'm willing to sacrifice some performance to get this to work.
For all the time I've spent on this , I probably could have built my own traditional authentication system and login component and added a users table to my database. But I wanted something that was more streamlined for users (eventually would like to move to an SSO model) and at this point, I feel like I'm in too deep.
I feel like this is probably a lot easier than I'm making it. I assumed negotiate would have some built in methods or properties that could be used to get the username without crashing my project but I guess not.
I've made a few games with Unity and C# and a few websites with flask and js, but this is my first real blazor project and I've been able to feel my way through most of it but this has completely brickwalled me.
Any help here is greatly appreciated.
Project Detials
- Dotnet 8.0
- Default blazor Template
- InteractiveServer Render mode
I have tried several different solutions for this but none of them work.
The first thing I tried was the simplest which was simply calling GetAuthenticationStateAsync() in the constructor method of whatever razor component I needed the username in. Obviously this doesn't work because of the double render problem.
The next thing I tried was putting around my router in routes.razor which irreparably screwed up my project and forced me to restore from backup. So I learned my lesson about even looking at anything with the word "route" in it. So Instead I tried encapsulating my MainLayout.razor in the block and while that didn't destroy my project, it still doesn't work. Same for when I try to simply pass only the username from MainLayout.razor with a . I found out the reason this doesn't work, is again, because the razor components like to render twice. The reason I know this is because I put a Console.Writeline() statement in OnInitializedAsync() that is supposed to print the user name to the debug console. When I load the page, I get not one but TWO print statements and only the first contains the username. Meaning that the Cascading Parameter thing only passes the value on the first render.
Now I would LOVE to change the render mode but InteractiveServer is the only thing it will let me put after @rendermode
I thought about adding a scoped service that stores the username as a static string but that would only hold the username of the last logged in user.
Gemini has suggested building a state container that updates a username variable every time state change event occurs but I'm still worried that will have the same result as the scoped service thing I already tried.
What I thought would be a very simple task has turned out to be the opposite of that.
I'm building a fairly simple web app with blazor and I've got windows domain credential based authentication working fine with Negotiate. That was fairly simple to set up.
Now let's say I want to display a "Welcome: " type of message on a banner or include the username to keep track of form submissions. Every solution I've come across seems to suggest doing something like this directly in the component I need to access the username in.
Normally, I would just store the username string that was entered in the username field, but since Negotiate uses that browser popup window for logins instead of a web page, I don't think that's possible. (maybe I'm wrong?)
This isn't my exact code but it's basically what I'm trying to do. This works in MainLayout.razor but any other component inside of it will hit a null value exception.
@page "/"
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Authorization
@inject AuthenticationStateProvider AuthStateProvider
@attribute [Authorize]
<h1> Welcome Back: @username <h1>
@code{
public string? username;
protected override async OnInitializedAsync(){
var authstate = await AuthStateProvider.GetAuthenticationStateAsync();
username = authstate.User.Identity.Name.Substring(9);
}
}
I think the issue may be the fact that the razor components are rendering twice which is causing the authstate variable to pull twice resulting in the antiforgery errors I keep getting.
I really think the key here is somehow forcing my components to only render once. That way, I only need to pull the auth state at the time of login and cache only what I need from it so I can access it in all of my components without causing errors. Performance is not a concern at all for me with this project. It's a very lightweight application serving a very small number of users so I'm willing to sacrifice some performance to get this to work.
For all the time I've spent on this , I probably could have built my own traditional authentication system and login component and added a users table to my database. But I wanted something that was more streamlined for users (eventually would like to move to an SSO model) and at this point, I feel like I'm in too deep.
I feel like this is probably a lot easier than I'm making it. I assumed negotiate would have some built in methods or properties that could be used to get the username without crashing my project but I guess not.
I've made a few games with Unity and C# and a few websites with flask and js, but this is my first real blazor project and I've been able to feel my way through most of it but this has completely brickwalled me.
Any help here is greatly appreciated.
Project Detials
- Dotnet 8.0
- Default blazor Template
- InteractiveServer Render mode
I have tried several different solutions for this but none of them work.
The first thing I tried was the simplest which was simply calling GetAuthenticationStateAsync() in the constructor method of whatever razor component I needed the username in. Obviously this doesn't work because of the double render problem.
The next thing I tried was putting around my router in routes.razor which irreparably screwed up my project and forced me to restore from backup. So I learned my lesson about even looking at anything with the word "route" in it. So Instead I tried encapsulating my MainLayout.razor in the block and while that didn't destroy my project, it still doesn't work. Same for when I try to simply pass only the username from MainLayout.razor with a . I found out the reason this doesn't work, is again, because the razor components like to render twice. The reason I know this is because I put a Console.Writeline() statement in OnInitializedAsync() that is supposed to print the user name to the debug console. When I load the page, I get not one but TWO print statements and only the first contains the username. Meaning that the Cascading Parameter thing only passes the value on the first render.
Now I would LOVE to change the render mode but InteractiveServer is the only thing it will let me put after @rendermode
I thought about adding a scoped service that stores the username as a static string but that would only hold the username of the last logged in user.
Gemini has suggested building a state container that updates a username variable every time state change event occurs but I'm still worried that will have the same result as the scoped service thing I already tried.
Share Improve this question asked 18 hours ago SunkistSunkist 1 New contributor Sunkist is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.1 Answer
Reset to default 0I'm not sure how your configuring your solution, so here's a basic demo.
Net8 - Server - Global Interactivity
Project File:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.Negotiate" Version="8.0.11" />
</ItemGroup>
</Project>
Program, registering the necessary Authentication/Authorization services
using BlazorApp.Components;
using Microsoft.AspNetCore.Authentication.Negotiate;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy.
options.FallbackPolicy = options.DefaultPolicy;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Routes, cascading the Authentication State.
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
</CascadingAuthenticationState>
Home page, using the cascaded context to get the User.
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
<AuthorizeView>
Hello, @context.User.Identity?.Name. Welcome to your new app.
</AuthorizeView>
The relevant code for AuthorizeView
is:
private AuthenticationState? currentAuthenticationState;
private bool? isAuthorized;
//.....
[Parameter] public RenderFragment<AuthenticationState>? ChildContent { get; set; }
[Parameter] public RenderFragment<AuthenticationState>? NotAuthorized { get; set; }
protected override async Task OnParametersSetAsync()
{
if (ChildContent != null && Authorized != null)
throw new InvalidOperationException($"Do not specify both '{nameof(Authorized)}' and '{nameof(ChildContent)}'.");
if (AuthenticationState == null)
throw new InvalidOperationException($"Authorization requires a cascading parameter of type Task<{nameof(AuthenticationState)}>. Consider using {typeof(CascadingAuthenticationState).Name} to supply this.");
isAuthorized = null;
currentAuthenticationState = await AuthenticationState;
isAuthorized = await IsAuthorizedAsync(currentAuthenticationState.User);
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
if (isAuthorized == null)
builder.AddContent(0, Authorizing);
else if (isAuthorized == true)
{
var authorized = Authorized ?? ChildContent;
builder.AddContent(0, authorized?.Invoke(currentAuthenticationState!));
}
else
builder.AddContent(0, NotAuthorized?.Invoke(currentAuthenticationState!));
}
//.... more code
The complete code: https://github.com/dotnet/aspnetcore/blob/main/src/Components/Authorization/src/AuthorizeViewCore.cs
The result:
- 移动互联网迅猛发展连接人与服务成为重要趋势
- 安卓4.4 vs iOS 7 谁是最佳移动操作系统
- 云计算是新的商业基础设施
- 山寨平板电脑走到穷途末路:方案公司纷纷倒闭
- 只是为文艺? 专业工作室为何钟情苹果
- c++ - What is the fastest way to upcast std::shared_ptr<std::unique_ptr<T>>? - Stack Overflow
- windows 10 - Gamemaker Mobile device Inconsistent Drag Speed Across Different Operating Systems (Win7 vs. Win10) - Stack Overflo
- javascript - In Quill, how to add one button to the toolbar to add some text? - Stack Overflow
- Best practices for Python imports in a production project: handling relativeabsolute imports across different directories and te
- c# - I have a time clock connected to my local network and I have a Windows server on localweb and I can't get the clock
- How to find kafka broker username and password - Stack Overflow
- verilog - Is it possible to create task within interface for specific modport? - Stack Overflow
- postgresql - How do I connect my AIRFLOW which is installed on WSL to POSTGRES DATABASE which is installed on windows environmen
- amazon web services - Update a view attribute in aws connect - Stack Overflow
- amazon web services - Python boto3: download files from s3 to local only if there are differences between s3 files and local one
- c++ - Pointer of an object which has static storage as template non-type parameter, Clang and GCC agrees, MSVC doesn't -
- node.js - Nestjs can't find build source after creating workspace - Stack Overflow