How do I access browser local storage from .cs files in blazor?

Viewed 3810

First of all, I can access localstorage data in .razor pages. I mean I cannot access localstorage data in .cs files. How can I access?

_Imports.razor:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
@inject ProtectedLocalStorage protectedLocalStorage

anyone .razor file:

await protectedLocalStorage.SetAsync(key, JsonSerializer.Serialize(instance));

Above code works for me but I want to call protectedLocalStorage from .cs files additionally.

P.S sorry for grammar mistakes

Edit: I am using IHttpClientFactory in startup.cs and I want to add token as a header before api request.

startup.cs

    services.AddHttpClient("api", hc =>
    {
        hc.BaseAddress = new Uri("http://localhost:5000/");

        string tokenVal = tokenService.GetToken();

        if (!String.IsNullOrEmpty(tokenVal))
            hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenVal);
    });

I want to take token value from local storage from this .cs file

public class TokenService : ITokenService
{
    private IHttpContextAccessor httpContextAccessor;

    public TokenService(IHttpContextAccessor HttpContextAccessor, IProtected) => httpContextAccessor = HttpContextAccessor;

    public string GetToken()
    {
        return "";
    }
}
2 Answers

How do I access browser local storage from .cs files in blazor?

ASP.NET supports injection in most constructors. Expanding OP's example:

// Startup.cs -> ConfigureServices(IServiceCollection services)
    // Probably not necessary in your case but, to be thorough:
    services.AddScoped<ProtectedLocalStorage>();

// SomeFile.cs
public class TokenService : ITokenService
{
    // Ignore for the moment that these are being used in the same context
    private IHttpContextAccessor httpContextAccessor;
    private readonly ProtectedBrowserStorage _storage;

    // Injection can happen here in ASP.NET
    public TokenService(
        IHttpContextAccessor HttpContextAccessor, 
        ProtectedBrowserStorage storage) 
    {
        httpContextAccessor = HttpContextAccessor;
        // injection works but the PBS service might not: see below
        _storage = storage;
    }

    //..
}

However, I don't recommend this for ProtectedBrowserStorage, since it uses IJSRuntime under the hood. If you try to use this in a non-javascript aware context (e.g. during Startup.Configure where the client is still awaiting a response and there is no way to execute javascript), you will run into errors. In Blazor, ProtectedBrowserStorage should only be called - directly or indirectly - from a Blazor component; to keep it simple, wrap it in a class you only use with components, or keep it in the component itself.

Thus, if you are trying to do this:

I am using IHttpClientFactory in startup.cs and I want to add token as a header before api request.

ProtectedBrowserStorage is not the tool for you. Use cookies or another web server technology.

How I solved in the end:

I have created custom authentication class inherited AuthenticationStateProvider. Then I designed all of check processes to be solved on ProtectedLocalStorage.

AuthenticationService

    public class AuthenticationService : AuthenticationStateProvider
    {
        private const string USER_SESSION_OBJECT_KEY = "user_session_obj";
        private const string ACCESS_TOKEN = "accesstoken";
        private const string USER_PERMISSIONS = "userpermissions";

        private readonly ProtectedLocalStorage _protectedLocalStorage;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public AuthenticationService(ProtectedLocalStorage protectedSessionStore, IHttpContextAccessor httpContextAccessor)
        {
            _protectedLocalStorage = protectedSessionStore;
            _httpContextAccessor = httpContextAccessor;
        }

        public string IpAddress => _httpContextAccessor?.HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? string.Empty;

        private User User { get; set; }
        private List<UserPermission> UserPermissionList { get; set; }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            try
            {
                User userSession = await GetUserSession();
                List<UserPermission> userPermissions = await GetUserPermission();

                if (userSession != null)
                    return await GenerateAuthenticationState(userSession, userPermissions);
                return await GenerateEmptyAuthenticationState();
            }
            catch
            {
                await LogoutAsync();
                return null;
            }
          
        }

        public async Task LoginAsync(User user,List<UserPermission> userPermissions)
        {
            await SetUserSession(user);
            await SetUserPermissionSession(userPermissions);

            NotifyAuthenticationStateChanged(GenerateAuthenticationState(user, userPermissions));
        }

        public async Task LogoutAsync()
        {
            //await SetUserSession(null);
            RefreshUserSession(null);
            await _protectedLocalStorage.DeleteAsync(USER_SESSION_OBJECT_KEY);
            await _protectedLocalStorage.DeleteAsync(ACCESS_TOKEN);
            await _protectedLocalStorage.DeleteAsync(USER_PERMISSIONS);

            NotifyAuthenticationStateChanged(GenerateEmptyAuthenticationState());
        }

        public async Task<User> GetUserSession()
        {
            if (User != null)
                return User;
            //TODO burda localUserJson get yaparken hata alıyor. try catch işi çözmezse buraya tekrardan bakılacak.
            try
            {
                var localUserJson = await _protectedLocalStorage.GetAsync<string>(USER_SESSION_OBJECT_KEY);

                if (string.IsNullOrEmpty(localUserJson.Value))
                    return null;

                return RefreshUserSession(JsonConvert.DeserializeObject<User>(localUserJson.Value));
            }
            catch
            {
                await LogoutAsync();
                return null;
            }
        }
        public async Task<List<UserPermission>> GetUserPermission()
        {
            if (UserPermissionList != null)
                return UserPermissionList;
            try
            {
                var localUserPermissionJson = await _protectedLocalStorage.GetAsync<string>(USER_PERMISSIONS);

                if (string.IsNullOrEmpty(localUserPermissionJson.Value))
                    return null;

                return RefreshUserPermissionSession(JsonConvert.DeserializeObject<List<UserPermission>>(localUserPermissionJson.Value));
            }
            catch
            {
                await LogoutAsync();
                return null;
            }
        }
        private async Task SetUserSession(User user)
        {
            RefreshUserSession(user);

            await _protectedLocalStorage.SetAsync(USER_SESSION_OBJECT_KEY, JsonConvert.SerializeObject(user));
        }

        private async Task SetUserPermissionSession(List<UserPermission> userPermissions)
        {
            RefreshUserPermissionSession(userPermissions);

            await _protectedLocalStorage.SetAsync(USER_PERMISSIONS, JsonConvert.SerializeObject(userPermissions));
        }

        private User RefreshUserSession(User user) => User = user;
        private List<UserPermission> RefreshUserPermissionSession(List<UserPermission> userPermission) => UserPermissionList = userPermission;

        private Task<AuthenticationState> GenerateAuthenticationState(User user, List<UserPermission> userPermission)
        {
            ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, user.Id.ToString()),
                new Claim(ClaimTypes.Role, userPermission.ToString()),
            }, "auth");

            ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
            return Task.FromResult(new AuthenticationState(claimsPrincipal));
        }

        private Task<AuthenticationState> GenerateEmptyAuthenticationState() => Task.FromResult(new AuthenticationState(new ClaimsPrincipal()));
    }

Then I registered this class in startup.cs

Startup

services.AddScoped<AuthenticationStateProvider, AuthenticationService>();

During changing page, authentication system interrupt showing page to check if it is authenticate or not thanks to below code.

_Imports

@attribute [Authorize]

*You can set localstorage at login page. You can create your way to check thanks to this way.

Related