diff --git a/sources/Clients.Admin.Web/Layout/MainLayout.razor b/sources/Clients.Admin.Web/Layout/MainLayout.razor index 0fcaba5..ea00894 100644 --- a/sources/Clients.Admin.Web/Layout/MainLayout.razor +++ b/sources/Clients.Admin.Web/Layout/MainLayout.razor @@ -6,13 +6,22 @@
- - - Login - + + + + Logout + + + + + Login + + +
+ @Body
diff --git a/sources/Clients.Admin.Web/Program.cs b/sources/Clients.Admin.Web/Program.cs index 4c9cdca..8ec003d 100644 --- a/sources/Clients.Admin.Web/Program.cs +++ b/sources/Clients.Admin.Web/Program.cs @@ -1,3 +1,4 @@ +using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MadWorldNL.Clients.Admin.Web; @@ -9,6 +10,7 @@ builder.RootComponents.Add("head::after"); builder.Services.AddRadzenComponents(); +builder.Services.AddBlazoredLocalStorage(); builder.AddIdentity(); diff --git a/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationLocalStorage.cs b/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationLocalStorage.cs new file mode 100644 index 0000000..9484314 --- /dev/null +++ b/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationLocalStorage.cs @@ -0,0 +1,27 @@ +using Blazored.LocalStorage; +using MadWorldNL.Clients.Identity.Api.Contracts.Authentications; + +namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications; + +public class AuthenticationLocalStorage : IAuthenticationStorage +{ + private const string AuthenticationResponseName = "AuthenticationResponse"; + + private readonly ILocalStorageService _localStorage; + + public AuthenticationLocalStorage(ILocalStorageService localStorage) + { + _localStorage = localStorage; + } + + public async Task SetAccessTokenAsync(LoginProxyResponse response) + { + await _localStorage.SetItemAsync(AuthenticationResponseName, response); + } + + public async Task GetAccessTokenAsync() + { + return await _localStorage.GetItemAsync(AuthenticationResponseName) + ?? new LoginProxyResponse(); + } +} \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationManager.cs b/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationManager.cs index b4d427b..83e3327 100644 --- a/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationManager.cs +++ b/sources/Clients.Identity.Blazor.Shared/Authentications/AuthenticationManager.cs @@ -1,18 +1,33 @@ using MadWorldNL.Clients.Identity.Api.Contracts.Authentications; +using Microsoft.AspNetCore.Components.Authorization; namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications; public class AuthenticationManager : IAuthenticationManager { private readonly IAuthenticationService _authenticationService; + private readonly IAuthenticationStorage _authenticationStorage; + private readonly AuthenticationStateProvider _authenticationStateProvider; - public AuthenticationManager(IAuthenticationService authenticationService) + public AuthenticationManager(IAuthenticationService authenticationService, IAuthenticationStorage authenticationStorage, AuthenticationStateProvider authenticationStateProvider) { _authenticationService = authenticationService; + _authenticationStorage = authenticationStorage; + _authenticationStateProvider = authenticationStateProvider; } public async Task LoginAsync(LoginProxyRequest request) { - return await _authenticationService.LoginAsync(request); + var response = await _authenticationService.LoginAsync(request); + await _authenticationStorage.SetAccessTokenAsync(response); + await _authenticationStateProvider.GetAuthenticationStateAsync(); + + return response; + } + + public async Task LogoutAsync() + { + await _authenticationStorage.SetAccessTokenAsync(new LoginProxyResponse()); + await _authenticationStateProvider.GetAuthenticationStateAsync(); } } \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationManager.cs b/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationManager.cs index 225d6ad..cee5152 100644 --- a/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationManager.cs +++ b/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationManager.cs @@ -5,4 +5,5 @@ namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications; public interface IAuthenticationManager { Task LoginAsync(LoginProxyRequest request); + Task LogoutAsync(); } \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationStorage.cs b/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationStorage.cs new file mode 100644 index 0000000..538a6cb --- /dev/null +++ b/sources/Clients.Identity.Blazor.Shared/Authentications/IAuthenticationStorage.cs @@ -0,0 +1,9 @@ +using MadWorldNL.Clients.Identity.Api.Contracts.Authentications; + +namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications; + +public interface IAuthenticationStorage +{ + Task SetAccessTokenAsync(LoginProxyResponse response); + Task GetAccessTokenAsync(); +} \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Authentications/MyAuthenticationStateProvider.cs b/sources/Clients.Identity.Blazor.Shared/Authentications/MyAuthenticationStateProvider.cs index 4c9c310..5fd75c2 100644 --- a/sources/Clients.Identity.Blazor.Shared/Authentications/MyAuthenticationStateProvider.cs +++ b/sources/Clients.Identity.Blazor.Shared/Authentications/MyAuthenticationStateProvider.cs @@ -1,3 +1,4 @@ +using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Microsoft.AspNetCore.Components.Authorization; @@ -5,11 +6,39 @@ namespace MadWorldNL.Clients.Identity.Blazor.Shared.Authentications; public class MyAuthenticationStateProvider : AuthenticationStateProvider { - public override Task GetAuthenticationStateAsync() + private readonly IAuthenticationStorage _authenticationStorage; + + public MyAuthenticationStateProvider(IAuthenticationStorage authenticationStorage) + { + _authenticationStorage = authenticationStorage; + } + + public override async Task GetAuthenticationStateAsync() { - var state = new AuthenticationState(new ClaimsPrincipal()); + var accessToken = await _authenticationStorage.GetAccessTokenAsync(); + + var identity = new ClaimsIdentity(); + if (accessToken.IsSuccess) + { + identity = RetrieveUserFromJwt(accessToken.AccessToken); + } + + var state = new AuthenticationState(new ClaimsPrincipal(identity)); NotifyAuthenticationStateChanged(Task.FromResult(state)); - return Task.FromResult(state); + return state; + } + + private static ClaimsIdentity RetrieveUserFromJwt(string jwt) + { + var claims = ParseClaimsFromJwt(jwt).ToList(); + return new ClaimsIdentity(claims, "jwt", "nameid", "role"); + } + + private static IEnumerable ParseClaimsFromJwt(string jwt) + { + var handler = new JwtSecurityTokenHandler(); + var token = handler.ReadJwtToken(jwt); + return token.Claims; } } \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Clients.Identity.Blazor.Shared.csproj b/sources/Clients.Identity.Blazor.Shared/Clients.Identity.Blazor.Shared.csproj index 8ac321f..1bf6074 100644 --- a/sources/Clients.Identity.Blazor.Shared/Clients.Identity.Blazor.Shared.csproj +++ b/sources/Clients.Identity.Blazor.Shared/Clients.Identity.Blazor.Shared.csproj @@ -10,21 +10,19 @@ + + - - - - diff --git a/sources/Clients.Identity.Blazor.Shared/Extensions/WebAssemblyHostBuilderExtensions.cs b/sources/Clients.Identity.Blazor.Shared/Extensions/WebAssemblyHostBuilderExtensions.cs index 97c0b65..59c75dc 100644 --- a/sources/Clients.Identity.Blazor.Shared/Extensions/WebAssemblyHostBuilderExtensions.cs +++ b/sources/Clients.Identity.Blazor.Shared/Extensions/WebAssemblyHostBuilderExtensions.cs @@ -20,6 +20,7 @@ public static void AddIdentity(this WebAssemblyHostBuilder builder) builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.AddIdentityClients(); } diff --git a/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor b/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor index 1335119..cc11791 100644 --- a/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor +++ b/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor @@ -2,8 +2,6 @@ -@TempLoginMessage -

You are logged in

diff --git a/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor.cs b/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor.cs index 235eb05..83b2af5 100644 --- a/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor.cs +++ b/sources/Clients.Identity.Blazor.Shared/Pages/Login.razor.cs @@ -11,11 +11,10 @@ public partial class Login private LoginProxyRequest _loginRequest = new(); private bool _showError = false; - private string TempLoginMessage = string.Empty; - - public async Task LoginAsync() + private async Task LoginAsync() { var response = await AuthenticationManager.LoginAsync(_loginRequest); + StateHasChanged(); if (!response.IsSuccess) { @@ -25,7 +24,5 @@ public async Task LoginAsync() } _showError = false; - - TempLoginMessage = response.Expiration.ToString(); } } \ No newline at end of file diff --git a/sources/Clients.Identity.Blazor.Shared/Pages/Logout.razor b/sources/Clients.Identity.Blazor.Shared/Pages/Logout.razor new file mode 100644 index 0000000..ba456be --- /dev/null +++ b/sources/Clients.Identity.Blazor.Shared/Pages/Logout.razor @@ -0,0 +1,13 @@ +@page "/Logout" +@using MadWorldNL.Clients.Identity.Blazor.Shared.Authentications +

You are logged out now!

+ +@code { + [Inject] public IAuthenticationManager AuthenticationManager { get; set; } = null!; + + protected override async Task OnInitializedAsync() + { + await AuthenticationManager.LogoutAsync(); + } + +} \ No newline at end of file diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 5dbb194..c37da38 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -1,5 +1,6 @@ + @@ -9,16 +10,16 @@ - - - - - - - - - - + + + + + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -29,7 +30,7 @@ - +