From f43d71538c9653c196c4359ef548765c8ef74ff2 Mon Sep 17 00:00:00 2001 From: Peace Date: Tue, 6 Aug 2024 16:52:10 +0900 Subject: [PATCH] refresh token --- .../Components/Pages/Weather.razor | 8 +++- .../Controllers/AccountController.cs | 8 ++++ .../FluentBlazorApp/DTOs/UserSession.cs | 7 ++++ .../FluentBlazorApp/Repos/Account.cs | 15 +++++++ .../FluentBlazorApp/Repos/IAccount.cs | 1 + .../Services/AccountService.cs | 40 ++++++++++++++++++- .../Services/IAccountService.cs | 1 + .../CustomAuthenticationStateProvider.cs | 18 ++------- .../States/DecryptJWTTokenService.cs | 28 +++++++++++++ 9 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 FluentBlazorApp/FluentBlazorApp/DTOs/UserSession.cs create mode 100644 FluentBlazorApp/FluentBlazorApp/States/DecryptJWTTokenService.cs diff --git a/FluentBlazorApp/FluentBlazorApp/Components/Pages/Weather.razor b/FluentBlazorApp/FluentBlazorApp/Components/Pages/Weather.razor index f55b64b..bd9cb22 100644 --- a/FluentBlazorApp/FluentBlazorApp/Components/Pages/Weather.razor +++ b/FluentBlazorApp/FluentBlazorApp/Components/Pages/Weather.razor @@ -26,5 +26,11 @@ else private IQueryable? forecasts; protected override async Task OnInitializedAsync() - => forecasts = (await AccountService.GetWeatherForecastsAync()).AsQueryable(); + { + var data = await AccountService.GetWeatherForecastsAync(); + if (data == null || data.Length < 1) + return; + + forecasts = data.AsQueryable(); + } } diff --git a/FluentBlazorApp/FluentBlazorApp/Controllers/AccountController.cs b/FluentBlazorApp/FluentBlazorApp/Controllers/AccountController.cs index b76f21e..e827c33 100644 --- a/FluentBlazorApp/FluentBlazorApp/Controllers/AccountController.cs +++ b/FluentBlazorApp/FluentBlazorApp/Controllers/AccountController.cs @@ -34,6 +34,14 @@ namespace FluentBlazorApp.Controllers return Ok(result); } + [HttpPost("refresh-token")] + [AllowAnonymous] + public ActionResult RefreshToken(UserSession model) + { + var result = _accountRepo.RefreshToken(model); + return Ok(result); + } + [HttpGet("weather")] [Authorize] public ActionResult GetWeatherForecast() diff --git a/FluentBlazorApp/FluentBlazorApp/DTOs/UserSession.cs b/FluentBlazorApp/FluentBlazorApp/DTOs/UserSession.cs new file mode 100644 index 0000000..2a83407 --- /dev/null +++ b/FluentBlazorApp/FluentBlazorApp/DTOs/UserSession.cs @@ -0,0 +1,7 @@ +namespace FluentBlazorApp.DTOs +{ + public class UserSession + { + public string JWTToken { get; set; } = string.Empty; + } +} diff --git a/FluentBlazorApp/FluentBlazorApp/Repos/Account.cs b/FluentBlazorApp/FluentBlazorApp/Repos/Account.cs index d32700d..91ce05b 100644 --- a/FluentBlazorApp/FluentBlazorApp/Repos/Account.cs +++ b/FluentBlazorApp/FluentBlazorApp/Repos/Account.cs @@ -2,6 +2,7 @@ using FluentBlazorApp.DTOs; using FluentBlazorApp.Models; using FluentBlazorApp.Responses; +using FluentBlazorApp.States; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; @@ -80,5 +81,19 @@ namespace FluentBlazorApp.Repos return new JwtSecurityTokenHandler().WriteToken(token); } + + public LoginResponse RefreshToken(UserSession userSession) + { + CustomUserClaims customUserClaims = DecryptJWTTokenService.DecryptToken(userSession.JWTToken); + if (customUserClaims is null) + return new LoginResponse(false, "Invalid token"); + + string newToken = GenerateToken(new ApplicationUser() + { + Name = customUserClaims.Name + }); + + return new LoginResponse(true, "New token", newToken); + } } } diff --git a/FluentBlazorApp/FluentBlazorApp/Repos/IAccount.cs b/FluentBlazorApp/FluentBlazorApp/Repos/IAccount.cs index 6fd242a..a6291b2 100644 --- a/FluentBlazorApp/FluentBlazorApp/Repos/IAccount.cs +++ b/FluentBlazorApp/FluentBlazorApp/Repos/IAccount.cs @@ -7,5 +7,6 @@ namespace FluentBlazorApp.Repos { Task RegisterAsync(RegisterDTO model); Task LoginAsync(LoginDTO model); + LoginResponse RefreshToken(UserSession userSession); } } diff --git a/FluentBlazorApp/FluentBlazorApp/Services/AccountService.cs b/FluentBlazorApp/FluentBlazorApp/Services/AccountService.cs index 18921b5..847a26a 100644 --- a/FluentBlazorApp/FluentBlazorApp/Services/AccountService.cs +++ b/FluentBlazorApp/FluentBlazorApp/Services/AccountService.cs @@ -32,13 +32,49 @@ namespace FluentBlazorApp.Services } public async Task GetWeatherForecastsAync() + { + GetProtectedClient(); + var response = await _httpClient.GetAsync($"{BASE_URL}/weather"); + bool check = CheckIfUnauthorized(response); + if (check) + { + await GetRefreshToken(); + return await GetWeatherForecastsAync(); + } + + var tmp = await response.Content.ReadFromJsonAsync(); + + return tmp; + } + + public async Task RefreshTokenAsync(UserSession userSession) + { + var resonse = await _httpClient.PostAsJsonAsync($"{BASE_URL}/refresh-token", userSession); + var result = await resonse.Content.ReadFromJsonAsync(); + return result; + } + + private static bool CheckIfUnauthorized(HttpResponseMessage httpResponseMessage) + { + if (httpResponseMessage.StatusCode == System.Net.HttpStatusCode.Unauthorized) + return true; + + return false; + } + + private void GetProtectedClient() { if (string.IsNullOrWhiteSpace(Constants.JWTToken)) - return null; + return; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Constants.JWTToken); + } - return await _httpClient.GetFromJsonAsync($"{BASE_URL}/weather"); + private async Task GetRefreshToken() + { + var response = await _httpClient.PostAsJsonAsync($"{BASE_URL}/refresh-token", new UserSession() { JWTToken = Constants.JWTToken }); + var result = await response.Content.ReadFromJsonAsync(); + Constants.JWTToken = result!.JWTToken; } } } diff --git a/FluentBlazorApp/FluentBlazorApp/Services/IAccountService.cs b/FluentBlazorApp/FluentBlazorApp/Services/IAccountService.cs index e139acc..cb7d98d 100644 --- a/FluentBlazorApp/FluentBlazorApp/Services/IAccountService.cs +++ b/FluentBlazorApp/FluentBlazorApp/Services/IAccountService.cs @@ -7,6 +7,7 @@ namespace FluentBlazorApp.Services { Task RegisterAsync(RegisterDTO model); Task LoginAsync(LoginDTO model); + Task RefreshTokenAsync(UserSession userSession); Task GetWeatherForecastsAync(); } } diff --git a/FluentBlazorApp/FluentBlazorApp/States/CustomAuthenticationStateProvider.cs b/FluentBlazorApp/FluentBlazorApp/States/CustomAuthenticationStateProvider.cs index c0f835d..fb5e685 100644 --- a/FluentBlazorApp/FluentBlazorApp/States/CustomAuthenticationStateProvider.cs +++ b/FluentBlazorApp/FluentBlazorApp/States/CustomAuthenticationStateProvider.cs @@ -16,7 +16,7 @@ namespace FluentBlazorApp.States if (string.IsNullOrEmpty(Constants.JWTToken)) return await Task.FromResult(new AuthenticationState(ANONYMOUS)); - var userClaims = DecryptToken(Constants.JWTToken); + var userClaims = DecryptJWTTokenService.DecryptToken(Constants.JWTToken); if (userClaims == null) return await Task.FromResult(new AuthenticationState(ANONYMOUS)); @@ -29,13 +29,13 @@ namespace FluentBlazorApp.States } } - public async void UpdateAuthenticationState(string jwtToken) + public void UpdateAuthenticationState(string jwtToken) { var claimsPrincipal = new ClaimsPrincipal(); if (!string.IsNullOrEmpty(jwtToken)) { Constants.JWTToken = jwtToken; - var getUserClaims = DecryptToken(jwtToken); + var getUserClaims = DecryptJWTTokenService.DecryptToken(jwtToken); claimsPrincipal = SetClaimPrincipal(getUserClaims); } else @@ -46,18 +46,6 @@ namespace FluentBlazorApp.States NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal))); } - public static CustomUserClaims DecryptToken(string jwtToken) - { - if (string.IsNullOrEmpty(jwtToken)) - return new CustomUserClaims(); - - var handler = new JwtSecurityTokenHandler(); - var token = handler.ReadJwtToken(jwtToken); - - var name = token.Claims.FirstOrDefault(t => t.Type == ClaimTypes.Name); - return new CustomUserClaims(name!.Value); - } - public static ClaimsPrincipal SetClaimPrincipal(CustomUserClaims claims) { if (claims.Name is null) diff --git a/FluentBlazorApp/FluentBlazorApp/States/DecryptJWTTokenService.cs b/FluentBlazorApp/FluentBlazorApp/States/DecryptJWTTokenService.cs new file mode 100644 index 0000000..eaf7f0a --- /dev/null +++ b/FluentBlazorApp/FluentBlazorApp/States/DecryptJWTTokenService.cs @@ -0,0 +1,28 @@ +using FluentBlazorApp.DTOs; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; + +namespace FluentBlazorApp.States +{ + public static class DecryptJWTTokenService + { + public static CustomUserClaims DecryptToken(string jwtToken) + { + try + { + if (string.IsNullOrEmpty(jwtToken)) + return null; + + var handler = new JwtSecurityTokenHandler(); + var token = handler.ReadJwtToken(jwtToken); + + var name = token.Claims.FirstOrDefault(t => t.Type == ClaimTypes.Name); + return new CustomUserClaims(name!.Value); + } + catch (Exception) + { + return null; + } + } + } +}