Aspnetcore: 'GetAuthenticationStateAsync was called before SetAuthenticationState.'

Created on 27 Nov 2019  路  12Comments  路  Source: dotnet/aspnetcore

Describe the bug

GetAuthenticationStateAsync throws an InvalidOperationException with the message:

'GetAuthenticationStateAsync was called before SetAuthenticationState.'

I do not know if this is an actual bug or just a lack of documentation on its correct use.

ref: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.server.serverauthenticationstateprovider?view=aspnetcore-3.0

To Reproduce

Inject a ServerAuthenticationStateProvider into a page. Then call 'GetAuthenticationStateAsync in any of its async-events.

Further technical details

<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" />
Answered Resolved area-blazor

All 12 comments

Thanks for contacting us.
@SteveSandersonMS can you please look into this, thanks!

I switched to using [CascadingParameter] Task<AuthenticationState> AuthenticationState { get; set; } instead. If that's actually the right way to do it then a simple note in the GetAuthenticationStateAsync docs would be sufficient.

You'll have to manually register the AuthenticationStateProvider in ConfigureServices:
services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();

This is a little weird as I expected that this service is already registered behind the scenes. :smirk:
I assume someone forgot to register this.

Update: Be aware not to use AddSingleton but use instead AddScoped. This is really important.

I get the same error when I try to access the AuthenticationStateProvider from _any_ service class.

I have the same problem.

I have a service called CurrentUserService with the goal of retrieving the current user. Each time anyone calls dbContext SaveChangesAsync() then I try to retrieve the current user through CurrentUserService but then I get the exception.

my code

public interface ICurrentUserService
{
    Task<string> GetUserId();
}

public class CurrentUserService : ICurrentUserService
{
    private readonly AuthenticationStateProvider _authenticationStateProvider;
    public CurrentUserService(AuthenticationStateProvider authenticationStateProvider)
    {
        _authenticationStateProvider = authenticationStateProvider;
    }

    public async Task<string> GetUserId()
    {
        var authState = await _authenticationStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;

        if (user.Identity.IsAuthenticated)
        {
            return user.Identity.Name;
        }
        else
        {
            return null;
        }
    }
}

my dbContext snapshot

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>, IApplicationDbContext
{
    private readonly ICurrentUserService _currentUserService;

    /* ... */

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, ICurrentUserService currentUserService) : base(options)
    {
        _currentUserService = currentUserService;
    }

    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
    {
        var userId = await _currentUserService.GetUserId();

        foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.Entity.CreatedBy = userId;
                    entry.Entity.Created = DateTimeOffset.Now;
                    break;

                case EntityState.Modified:
                    entry.Entity.LastModifiedBy = userId;
                    entry.Entity.LastModified = DateTimeOffset.Now;
                    break;
            }
        }

        return await base.SaveChangesAsync(cancellationToken);
    }

    /* ... */
}

I've noticed that when this service is called from a Blazor page then it works.

You'll have to manually register the AuthenticationStateProvider in ConfigureServices:
services.AddSingleton<AuthenticationStateProvider, ServerAuthenticationStateProvider>();

I presume this is intended to be AddScoped, @sven5? When I naively copied and pasted this registration, I ended up with one user's session receiving the other session's user details.

@nullpainter
Oh yes you could be right about this.
However, it seems that this is no longer needed. I'm currently working on a new Blazor project using .NET Core 3.1.2 - I didn't explicitly register the AuthenticationStateProvider and it's working.

I've noticed that if I change services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>(); to services.AddSingleton<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>(); then I don't have any exception, but I don't think this is a good solution. Are there other solutions?

Inject a ServerAuthenticationStateProvider into a page.

This is the problem. Don't do that, because you'll just receive an uninitialized instance.

You need to use the instance provided by the framework, which has been initialized. To get that, inject an AuthenticationStateProvider instead of ServerAuthenticationStateProvider.

For anyone else posting here, your issues may be different, since maybe you were not trying to inject ServerAuthenticationStateProvider. If you still have trouble, could you please post a separate issue with repro steps for your scenario? Thanks!

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.

I have the same problem.

I have a service called CurrentUserService with the goal of retrieving the current user. Each time anyone calls dbContext SaveChangesAsync() then I try to retrieve the current user through CurrentUserService but then I get the exception.

@Blackleones Did you ever get this to work? I'm doing basically the same thing and it's still not working.

Much like @eddiepaz , i'm still exeriencing the same issue. i think for me it's because i'm using custom Middleware and utilizing IServiceProvider with CreateScope to created a Scoped instance of my User provider.

the error can be demonstrated with the following (serviceProvider is an injected IServiceProvider):

using (var serviceScope = serviceProvider.CreateScope())
{
    var service = serviceScope.ServiceProvider.GetRequiredService<AuthenticationStateProvider>();
    var state= await service.GetAuthenticationStateAsync();
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ipinak picture ipinak  路  3Comments

Kevenvz picture Kevenvz  路  3Comments

rbanks54 picture rbanks54  路  3Comments

rynowak picture rynowak  路  3Comments

UweKeim picture UweKeim  路  3Comments