File size: 2,725 Bytes
0acba57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b866d20
 
 
 
 
 
 
 
 
 
0acba57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;

namespace BlazorFrontend.Providers;

public class JwtAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly ProtectedLocalStorage _localStorage;
    private readonly ClaimsPrincipal _anonymous = new(new ClaimsIdentity());
    
    public string? Token { get; private set; }

    public JwtAuthenticationStateProvider(ProtectedLocalStorage localStorage)
    {
        _localStorage = localStorage;
    }

    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        if (string.IsNullOrWhiteSpace(Token))
            return Task.FromResult(new AuthenticationState(_anonymous));

        try
        {
            var handler = new JwtSecurityTokenHandler();
            var jwt = handler.ReadJwtToken(Token);

            // JwtSecurityTokenHandler maps standard ClaimTypes to short names (unique_name, role) in the JWT payload.
            // When reading back raw claims, we need to specify which claim types to use for the Identity properties.
            var identity = new ClaimsIdentity(jwt.Claims, "jwt", "unique_name", "role");
            
            // If unique_name is not present, fallback to "name" or default ClaimTypes.Name
            if (identity.Name == null)
            {
                identity = new ClaimsIdentity(jwt.Claims, "jwt", "name", "role");
            }

            var user = new ClaimsPrincipal(identity);

            return Task.FromResult(new AuthenticationState(user));
        }
        catch
        {
            return Task.FromResult(new AuthenticationState(_anonymous));
        }
    }

    public async Task LoadTokenAsync()
    {
        try
        {
            var tokenResult = await _localStorage.GetAsync<string>("AuthToken");
            if (tokenResult.Success && !string.IsNullOrWhiteSpace(tokenResult.Value))
            {
                Token = tokenResult.Value;
                NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
            }
        }
        catch
        {
            // Ignore JS interop errors if they somehow still happen
        }
    }

    public async Task LoginAsync(string token)
    {
        Token = token;
        await _localStorage.SetAsync("AuthToken", token);
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    public async Task LogoutAsync()
    {
        Token = null;
        await _localStorage.DeleteAsync("AuthToken");
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_anonymous)));
    }
}