Skip to content
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

CustomAuthenticationProvider does not work as expected(Does not fire NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); #41228

Closed
1 task done
nssidhu opened this issue Apr 17, 2022 · 3 comments
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-server ✔️ Resolution: Answered Resolved because the question asked by the original author has been answered. question Status: Resolved

Comments

@nssidhu
Copy link

nssidhu commented Apr 17, 2022

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

I am using VS 2022

using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using Blazored.LocalStorage;
using Models;
using Microsoft.JSInterop;
using Newtonsoft.Json;


namespace BlazorWasm.Client.Services
{
    public class CustomAuthenticationStateProvider : AuthenticationStateProvider
    {
       ILocalStorageService _localStorage;
       private readonly IJSRuntime _jsruntime;
        public  CustomAuthenticationStateProvider(ILocalStorageService LocalStorage, IJSRuntime jsruntime)
        {
            _localStorage = LocalStorage;
            _jsruntime = jsruntime;
        }

        private ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            //claimsPrincipal.Identity.IsAuthenticated = false;
            if(claimsPrincipal == null || claimsPrincipal.Identity?.IsAuthenticated == false)
            {
                //check if we have stored in the LocalStorage
                try
                {
                    //Issue: neither Jsonnet nor microsoft system.text.json parser could parse Claims back to object.
                    //A custom converter is used to read the value as string and than create instance of object
                   //var obj = await _jsruntime.InvokeAsync<string>("localStorage.getItem", "AuthInfo");
                    var authInfojson = await _localStorage.GetItemAsync<string>("AuthInfo");

                    if(!string.IsNullOrEmpty(authInfojson))
                    {
                        //system.text.json does not allow to use custom converter during deserialization
                        var authInfo = JsonConvert.DeserializeObject<AuthInfo>(authInfojson, new ClaimConverter());

                        if (authInfo != null)
                        {
                            var identity = new ClaimsIdentity(authInfo.Claims, "AuthInfo");

                            claimsPrincipal = new ClaimsPrincipal(identity);
                        }

                    }

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                  
                }
             
            }
            return (new AuthenticationState(claimsPrincipal));
        }

        public async Task SetAuthInfo(AuthInfo authInfo)
        {
          
            try
            {
                var identity = new ClaimsIdentity(authInfo.Claims, "AuthInfo");


                await _localStorage.SetItemAsync<AuthInfo>("AuthInfo", authInfo);

                claimsPrincipal = new ClaimsPrincipal(identity);
                NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
            }
            catch (Exception ex)
            {

                throw;
            }

          
        }

        public async Task Logout()
        {
            await _localStorage.RemoveItemAsync("AuthInfo");
            claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
            NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
        }
    }
    
}

I than register it program.cs

builder.Services.AddOptions(); //ToDo find out why this one is needed
builder.Services.AddAuthorizationCore();

builder.Services.AddScoped<ICountryDropDownDataService, CountryDropDownService>();
builder.Services.AddScoped<ILoginService, LoginService>();

builder.Services.AddScoped<CustomAuthenticationStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(provider => provider.GetRequiredService<CustomAuthenticationStateProvider>());

In my Login Service, i Inject the my CustomAuthStateProvider (showing partial code)

public class LoginService : ILoginService
    {
       

        private readonly DBContext _dbContext;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private UtilityClass UtilCls;
        private readonly IMapper _mapper;
        private TelemetryClient _telemetryClient;
        CustomAuthenticationStateProvider _custAuthProvider;
     

        public LoginService(IServiceProvider serviceProvider)
        {

            _mapper = serviceProvider.GetRequiredService<IMapper>(); ;
            _telemetryClient = serviceProvider.GetRequiredService<TelemetryClient>();
            _dbContext = serviceProvider.GetRequiredService<DBContext>();
            _httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
            _custAuthProvider = serviceProvider.GetRequiredService<CustomAuthenticationStateProvider>();
           
            UtilCls = new UtilityClass(serviceProvider, _telemetryClient);

        }

When my API Returns success i call the

await _custAuthProvider.SetAuthInfo(AuthInfo); //This Line is inside of Login Service.

This executes code inside the CustomAuthenticationStateProvide, but no Notification is generated.

I have couple of Places with Authorize View, those don't Update

Expected Behavior

Calling NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
should update all pages that are using Authorize View to reflect the new State

Steps To Reproduce

Mentioned Above in describe bug

Exceptions (if any)

None

.NET Version

6.0.300-preview.22154.4

Anything else?

C:\Users\nssid>dotnet --info
.NET SDK (reflecting any global.json):
Version: 6.0.300-preview.22154.4
Commit: 1eb22793b6

Runtime Environment:
OS Name: Windows
OS Version: 10.0.22000
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\6.0.300-preview.22154.4\

Host (useful for support):
Version: 6.0.2
Commit: 839cdfb0ec

.NET SDKs installed:
6.0.300-preview.22154.4 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 6.0.2 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

To install additional .NET runtimes or SDKs:
https://aka.ms/dotnet-download

@TanayParikh TanayParikh added the area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer label Apr 17, 2022
@nssidhu
Copy link
Author

nssidhu commented Apr 18, 2022

@blowdart blowdart added area-blazor Includes: Blazor, Razor Components and removed area-auth Includes: Authn, Authz, OAuth, OIDC, Bearer labels Apr 18, 2022
@javiercn
Copy link
Member

@nssidhu thanks for contacting us.

The issue is in the order in which you are registering services in DI.

https://github.com/nssidhu/BlazorServerCustomAuthenticationIssue/blob/master/BlazorServerCustomAuthenticationIssue/Program.cs#L16-L17

Those two lines must go after AddServerSideBlazor.

The reason why this is not working is because AddServerSideBlazor unconditionally registers the AuthenticationStateProvider in DI to make sure the implementation is compatible with Blazor Server. This might be something we might want to change, but I'll create a separate issue for now.

@guardrex can you add a note about the ordering here in the docs about this?

@nssidhu
Copy link
Author

nssidhu commented Apr 18, 2022

Yes, after making the suggested change it is working correctly

@ghost ghost locked as resolved and limited conversation to collaborators May 18, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-blazor Includes: Blazor, Razor Components feature-blazor-server ✔️ 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

4 participants