feat: implement native AOT-friendly JwtTokenValidator to prevent sending expired bearer tokens in auth handlers
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Threading;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NexusReader.Application.Abstractions.Services;
|
||||
using NexusReader.UI.Shared.Services;
|
||||
|
||||
namespace NexusReader.Maui.Infrastructure.Identity;
|
||||
|
||||
@@ -55,7 +56,12 @@ public class MobileAuthenticationHeaderHandler : DelegatingHandler
|
||||
if (tokenResult.IsSuccess && !string.IsNullOrEmpty(tokenResult.Value))
|
||||
{
|
||||
originalToken = tokenResult.Value;
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", originalToken);
|
||||
|
||||
// Only attach the Bearer token if it is not expired
|
||||
if (!JwtTokenValidator.IsExpired(originalToken))
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", originalToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace NexusReader.UI.Shared.Services;
|
||||
|
||||
/// <summary>
|
||||
/// A lightweight, Native AOT-friendly JWT validator that decodes the payload of a JWT token
|
||||
/// to verify expiration without standard library dependencies.
|
||||
/// </summary>
|
||||
public static class JwtTokenValidator
|
||||
{
|
||||
public static bool IsExpired(string? token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)) return true;
|
||||
|
||||
try
|
||||
{
|
||||
var parts = token.Split('.');
|
||||
if (parts.Length != 3) return true;
|
||||
|
||||
var payload = parts[1];
|
||||
|
||||
// Pad the base64 string
|
||||
var padLength = 4 - (payload.Length % 4);
|
||||
if (padLength < 4)
|
||||
{
|
||||
payload += new string('=', padLength);
|
||||
}
|
||||
|
||||
// Base64URL to standard Base64 conversion
|
||||
payload = payload.Replace('-', '+').Replace('_', '/');
|
||||
|
||||
var bytes = Convert.FromBase64String(payload);
|
||||
using var jsonDoc = JsonDocument.Parse(bytes);
|
||||
|
||||
if (jsonDoc.RootElement.TryGetProperty("exp", out var expElement))
|
||||
{
|
||||
var exp = expElement.GetInt64();
|
||||
var expTime = DateTimeOffset.FromUnixTimeSeconds(exp);
|
||||
|
||||
// Allow a small 10-second clock skew buffer
|
||||
return expTime <= DateTimeOffset.UtcNow.AddSeconds(10);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return true; // Treat invalid token as expired
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class NexusAuthenticationStateProvider : AuthenticationStateProvider
|
||||
var token = tokenResult.IsSuccess ? tokenResult.Value : null;
|
||||
|
||||
// 1. Try Token-based auth
|
||||
if (!string.IsNullOrWhiteSpace(token))
|
||||
if (!string.IsNullOrWhiteSpace(token) && !JwtTokenValidator.IsExpired(token))
|
||||
{
|
||||
var emailResult = await _storageService.GetSecureString(StorageKeys.UserEmail);
|
||||
var tenantIdResult = await _storageService.GetSecureString(StorageKeys.UserTenant);
|
||||
|
||||
@@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.Components.WebAssembly.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NexusReader.Application.Abstractions.Services;
|
||||
using NexusReader.UI.Shared.Services;
|
||||
|
||||
namespace NexusReader.Web.Client.Handlers;
|
||||
|
||||
@@ -48,7 +49,12 @@ public class AuthenticationHeaderHandler : DelegatingHandler
|
||||
if (tokenResult.IsSuccess && !string.IsNullOrEmpty(tokenResult.Value))
|
||||
{
|
||||
originalToken = tokenResult.Value;
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", originalToken);
|
||||
|
||||
// Only attach the Bearer token if it is not expired
|
||||
if (!JwtTokenValidator.IsExpired(originalToken))
|
||||
{
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", originalToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user