Skip to content

Using Standard OIDC Authentication and CustomAuthenticationStateProvider Together Causes Error #54268

@MustafaSuyi

Description

@MustafaSuyi

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

We have a Blazor Web Assembly Client that needs OIDC and custom login together, the existing solutions from the Microsoft documentations work fine when used separately,

builder.Services.AddOidcAuthentication(x =>
{
//options
});

Or

builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthenticationStateProvider>();

But when we use them together, this exception thrown:
Unhandled exception rendering component: Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.

I have seen a similar issues here and there but there's no solution using these standard components without implementing multiple interfaces like IRemoteAuthenticationService & IAccessTokenProvider to CustomAuthenticationStateProvider.

Do you plan an update so we can use these together, if not can you point out a work around?

Expected Behavior

No response

Steps To Reproduce

This is a sample CustomProvider, we want to be able to login this way as well as with OIDC

public class CustomAuthenticationStateProvider : AuthenticationStateProvider, IDisposable
{
private readonly UserService _userService;
public User CurrentUser { get; private set; } = new User(string.Empty, string.Empty);

public CustomAuthenticationStateProvider(UserService userService)
{
    _userService = userService;
    AuthenticationStateChanged += OnAuthenticationStateChangedAsync;
}

public async Task LoginAsync(IClinician clinician)
{
    var principal = new ClaimsPrincipal();
    var user = _userService.ConvertClinicianToUser(clinician);

    if (user is not null)
    {
        await _userService.PersistUserToBrowserAsync(user);
        principal = User.ToClaimsPrincipal(user);
    }

    NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(principal)));
}

public async Task LogoutAsync()
{
    await _userService.ClearBrowserUserDataAsync();
    NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(new())));
}

public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
    var principal = new ClaimsPrincipal();
    var user = await _userService.FetchUserFromBrowserAsync();

    if (user is not null)
    {
        bool userExists = await _userService.CheckIfUserExistsInDatabaseAsync(user);

        if (userExists)
        {
            principal = User.ToClaimsPrincipal(user);
            CurrentUser = user;
        }
    }

    return new AuthenticationState(principal);
}

public void Dispose()
{
    AuthenticationStateChanged -= OnAuthenticationStateChangedAsync;
}

private async void OnAuthenticationStateChangedAsync(Task<AuthenticationState> task)
{
    var authenticationState = await task;

    if (authenticationState is not null)
    {
        CurrentUser = User.FromClaimsPrincipal(authenticationState.User);
    }
}

}

Exceptions (if any)

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
Unhandled exception rendering component: Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.
at Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.<>c__11[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=8.0.2.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].<AddAuthenticationStateProvider>b__1_0(IServiceProvider sp) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeServiceProviderEngine.<>c__DisplayClass4_0.<RealizeService>b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.AspNetCore.Components.ComponentFactory.<>c__DisplayClass9_0.<CreatePropertyInjector>g__Initialize|1(IServiceProvider serviceProvider, IComponent component) at Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent(IServiceProvider serviceProvider, Type componentType, IComponentRenderMode callerSpecifiedRenderMode, Nullable1 parentComponentId)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.InstantiateChildComponentOnFrame(RenderTreeFrame[] frames, Int32 frameIndex, Int32 parentComponentId)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewComponentFrame(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InitializeNewSubtree(DiffContext& diffContext, Int32 frameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.InsertNewFrame(DiffContext& diffContext, Int32 newFrameIndex)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.AppendDiffEntriesForRange(DiffContext& diffContext, Int32 oldStartIndex, Int32 oldEndIndexExcl, Int32 newStartIndex, Int32 newEndIndexExcl)
at Microsoft.AspNetCore.Components.RenderTree.RenderTreeDiffBuilder.ComputeDiff(Renderer renderer, RenderBatchBuilder batchBuilder, Int32 componentId, ArrayRange1 oldTree, ArrayRange1 newTree)
at Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment, Exception& renderFragmentException)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()

.NET Version

.NET 8

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs: Author FeedbackThe author of this issue needs to respond in order for us to continue investigating this issue.Needs: ReproIndicates that the team needs a repro project to continue the investigation on this issueStatus: No Recent Activityarea-blazorIncludes: Blazor, Razor Components

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions