From 6f4b0fabcd4a4ffc9d71e69d85284f1e7b5eabe1 Mon Sep 17 00:00:00 2001 From: Lev Khobotov Date: Wed, 20 Mar 2024 23:14:05 +0300 Subject: [PATCH] Fix JWT deserialization --- Amphasis.Azure.WebPortal/Helpers/JwtHelper.cs | 40 +++++++++++++++++++ .../SimaLand/Services/SimaLandService.cs | 12 +++--- 2 files changed, 46 insertions(+), 6 deletions(-) create mode 100644 Amphasis.Azure.WebPortal/Helpers/JwtHelper.cs diff --git a/Amphasis.Azure.WebPortal/Helpers/JwtHelper.cs b/Amphasis.Azure.WebPortal/Helpers/JwtHelper.cs new file mode 100644 index 0000000..91f1c3e --- /dev/null +++ b/Amphasis.Azure.WebPortal/Helpers/JwtHelper.cs @@ -0,0 +1,40 @@ +using System; +using System.Text.Json; +using Microsoft.AspNetCore.WebUtilities; + +namespace Amphasis.Azure.WebPortal.Helpers; + +public static class JwtHelper +{ + public static DateTime GetValidTo(string encodedJsonWebToken) + { + var destination = new Span(new Range[3]); + var partsCount = encodedJsonWebToken.AsSpan().Split(destination, '.'); + + if (partsCount < 3) + throw new JwtHelperException("Could not extract payload"); + + var payloadRange = destination[1]; + var (offset, count) = payloadRange.GetOffsetAndLength(encodedJsonWebToken.Length); + var bytes = WebEncoders.Base64UrlDecode(encodedJsonWebToken, offset, count); + var payload = JsonSerializer.Deserialize(bytes, _jsonSerializerOptions); + + return DateTime.UnixEpoch.AddSeconds(payload.Exp); + } + + static JwtHelper() + { + _jsonSerializerOptions = new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase}; + } + + private static readonly JsonSerializerOptions _jsonSerializerOptions; + + private sealed class JwtHelperException : Exception + { + public JwtHelperException(string message) : base(message) + { + } + } + + private sealed record JwtPayload(int Exp); +} diff --git a/Amphasis.Azure.WebPortal/SimaLand/Services/SimaLandService.cs b/Amphasis.Azure.WebPortal/SimaLand/Services/SimaLandService.cs index fc64158..0645e5b 100644 --- a/Amphasis.Azure.WebPortal/SimaLand/Services/SimaLandService.cs +++ b/Amphasis.Azure.WebPortal/SimaLand/Services/SimaLandService.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; -using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Net.Http; using System.Threading.Tasks; using Amphasis.Azure.WebPortal.Extensions; +using Amphasis.Azure.WebPortal.Helpers; using Amphasis.Azure.WebPortal.SimaLand.Models; using Amphasis.Azure.WebPortal.SimaLand.Models.Enums; using Amphasis.SimaLand; @@ -15,7 +15,7 @@ namespace Amphasis.Azure.WebPortal.SimaLand.Services { - public class SimaLandService + public class SimaLandService { private const string ApiClientAccessTokenKey = nameof(ApiClientAccessTokenKey); private static readonly TimeSpan TokenExpirationSkew = TimeSpan.FromSeconds(30); @@ -74,10 +74,10 @@ private async ValueTask AuthorizeAsync() } private async Task TokenFactoryAsync(ICacheEntry cacheEntry) - { - string token = await _apiClient.GetAccessTokenAsync(_configuration.Email, _configuration.Password); - var jwtSecurityToken = new JwtSecurityToken(token); - cacheEntry.AbsoluteExpiration = jwtSecurityToken.ValidTo - TokenExpirationSkew; + { + var token = await _apiClient.GetAccessTokenAsync(_configuration.Email, _configuration.Password); + var validTo = JwtHelper.GetValidTo(token); + cacheEntry.AbsoluteExpiration = validTo - TokenExpirationSkew; return token; }