Skip to content

[Blazor] Availability of User in Startup #35611

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
mrlife opened this issue Aug 23, 2021 · 18 comments
Closed

[Blazor] Availability of User in Startup #35611

mrlife opened this issue Aug 23, 2021 · 18 comments
Assignees
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question Status: Resolved

Comments

@mrlife
Copy link
Contributor

mrlife commented Aug 23, 2021

Is your feature request related to a problem? Please describe.

Sometimes, configuration needs to run once on the server, such as in Startup.Configure(). With ASP.NET Core, the IHttpContextAccessor can be used, however, this isn't reliable in Blazor. Is there another way?

Describe the solution you'd like

Any way to access properties of the current user in configuration that runs once on the server would be beneficial.

Additional context

Please see this issue for background and more info: thepirat000/Audit.NET#440

@pranavkm pranavkm added the area-blazor Includes: Blazor, Razor Components label Aug 23, 2021
@pranavkm
Copy link
Contributor

In Blazor, you could use IAuthenticationStateProvider which should act as an equivalent replacement to getting the User from an IHttpContextAccessor. The service should be available from DI the same way you acquire IHttpContextAccessor

@pranavkm pranavkm added the ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. label Aug 23, 2021
@ghost ghost added the Status: Resolved label Aug 23, 2021
@mrlife
Copy link
Contributor Author

mrlife commented Aug 23, 2021

@pranavkm Thank you. Could you provide an example? I can't find IAuthenticationStateProvider with Intellisense or in the docs.

@pranavkm
Copy link
Contributor

My bad, it's AuthenticationStateProvider. Here's an example of it being used - https://docs.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-5.0#authenticationstateprovider-service-1

@mrlife
Copy link
Contributor Author

mrlife commented Aug 24, 2021

Thank you. I tried AuthenticationStateProvider initially and got this error. It appears the auth state is accessed before the user has logged in.

System.InvalidOperationException: "GetAuthenticationStateAsync was called before SetAuthenticationState."

When does SetAuthenticationState get called? I can't find it in the docs. Thank you.

@ghost
Copy link

ghost commented Aug 25, 2021

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

@mrlife
Copy link
Contributor Author

mrlife commented Aug 25, 2021

@pranavkm Can this be reopened so this doesn't get lost?

@pranavkm pranavkm reopened this Aug 25, 2021
@pranavkm pranavkm removed ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. Status: Resolved labels Aug 25, 2021
@mrlife
Copy link
Contributor Author

mrlife commented Aug 25, 2021

@pranavkm Thanks for reopening this issue.

From what I can tell, including a call to AuthenticationStateProvider.GetAuthenticationStateAsync() in Startup.Configure() always results in the error:

System.InvalidOperationException: "GetAuthenticationStateAsync was called before SetAuthenticationState."

Looks like I forgot I've run into this before and it wasn't supported at the time. It also looks like a number of folks are trying to get to User information outside of a Blazor page but not having a way.

Is it possible to access the User in Startup.Configure() now?

@javiercn javiercn assigned pranavkm and javiercn and unassigned pranavkm Aug 26, 2021
@mkArtakMSFT mkArtakMSFT added this to the Discussions milestone Aug 30, 2021
@mrlife
Copy link
Contributor Author

mrlife commented Sep 8, 2021

@javiercn @mkArtakMSFT In the meantime, is there any way to store a user-specific value and recall it, both actions outside of the "Blazor scope"?

@enetstudio
Copy link

@mrlife, don't call AuthenticationStateProvider.GetAuthenticationStateAsync()
from the Startup class...

You can access the HttpContext object from the Configure method like this:

        app.UseAuthentication();
        app.UseAuthorization();

        app.Use(async (context, next) =>
        {
            await next.Invoke();
            if (context.User.Identity.IsAuthenticated)
            {
                var userName = context.User.Identity.Name;
                using (var dbContext = context.RequestServices.GetRequiredService<ApplicationDbContext>())
                {
                    var user = dbContext.Users.Where(u => u.UserName == userName).FirstOrDefault();
                    if (user != null)
                    {
                        //user.PhoneNumber = System.DateTime.Now;
                        //user.LastIpAddress = context.Connection?.RemoteIpAddress?.ToString();

                        user.PhoneNumber = "1234567";

                        dbContext.Update(user);
                        dbContext.SaveChanges();
                    }
                }
            }
        });

Hope this help...

@mrlife
Copy link
Contributor Author

mrlife commented Sep 20, 2021

@enetstudio Thank you, does your example use IHttpContextAccessor for context like in the following example?

public void Configure(IApplicationBuilder app, IHttpContextAccessor contextAccessor)
{
    Audit.Core.Configuration.AddCustomAction(ActionType.OnScopeCreated, scope =>
    {
        scope.Event.Environment.CustomFields["UserId"] = contextAccessor.HttpContext.User.FindFirstValue("UserId");
    });
    ...
}

This is how I've been using it, however Microsoft says:

The HttpContext isn't guaranteed to be available within the IHttpContextAccessor, nor is it guaranteed to be holding the context that started the Blazor app.

@enetstudio
Copy link

@mrlife, my code is a middleware that accesses the HttpContext object directly from the Configure method. You can access the HttpContext.User property, and other values and do something interesting with them, as for instance, passing the User IP address to a service that may be injected into your Razor components, etc.

The HttpContext isn't guaranteed to be available within the IHttpContextAccessor, nor is it guaranteed to be holding the context that started the Blazor app.

This simply means: Do not create, as for instance, a service which accesses the HttpContext object through IHttpContextAccessor, inject the service into a Razor component, and try to access the HttpContext object from within your component. This should not be done even if sometimes it seems to work perfectly well.

Note: The initial request to your web app is always HTTP one. This is the time to capture the HttpContext and to extricate values from it to be used later in your app. This is also applicable when a user login into your app... This is the time the HttpContext object is available, and you can access it with no issues

@mrlife
Copy link
Contributor Author

mrlife commented Sep 20, 2021

@enetstudio Thank you for this. Could you clarify how your context is defined in Configure?

@enetstudio
Copy link

What do you mean how it is defined ? This is a middleware from which I grab the HttpContext object passed in the HTTP pipeline. I do not define the context, it is passed as a parameter from preceding middleware... Did you try my code ? Did you play with it ? Why is this question ? Don't you know what is middleware ? Don't you know how the HTTP pipeline works ?

@mrlife
Copy link
Contributor Author

mrlife commented Sep 20, 2021

@enetstudio My use case is for a third party package called Audit.NET. I need to access the User like in this comment. I appreciate your example and having trouble applying it to my situation.

@ghost
Copy link

ghost commented Nov 19, 2021

Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue.

This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue!

@ghost ghost closed this as completed Nov 19, 2021
@mrlife
Copy link
Contributor Author

mrlife commented Nov 23, 2021

@mkArtakMSFT I believe this should still be open since it is a real need. Could you reopen this issue?

@pranavkm pranavkm reopened this Nov 23, 2021
@pranavkm pranavkm removed this from the Discussions milestone Nov 23, 2021
@javiercn
Copy link
Member

@mrlife What you are looking for is not possible, a Blazor application doesn't start until a websocket is established.

The best you can probably do is to use a circuit handler to grab the user there and setup some circuit specific state.

@javiercn javiercn added ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question and removed investigate labels Nov 23, 2021
@ghost ghost added the Status: Resolved label Nov 23, 2021
@ghost
Copy link

ghost commented Nov 24, 2021

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

@ghost ghost closed this as completed Nov 24, 2021
@ghost ghost locked as resolved and limited conversation to collaborators Dec 24, 2021
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question Status: Resolved
Projects
None yet
Development

No branches or pull requests

5 participants