ValidateJWT 1.3.0

dotnet add package ValidateJWT --version 1.3.0
                    
NuGet\Install-Package ValidateJWT -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ValidateJWT" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ValidateJWT" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="ValidateJWT" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ValidateJWT --version 1.3.0
                    
#r "nuget: ValidateJWT, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ValidateJWT@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ValidateJWT&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=ValidateJWT&version=1.3.0
                    
Install as a Cake Tool

ValidateJWT

NuGet Build Status License .NET Framework

A lightweight .NET Framework 4.7.2 library for validating JWT (JSON Web Token) expiration times with optional signature verification support.

? Features

  • ? Time-Based Validation - Check JWT expiration with configurable clock skew
  • ? Signature Verification - HMAC-SHA256 (HS256) and RSA-SHA256 (RS256) support
  • ? Zero Dependencies - Uses only built-in .NET Framework libraries
  • ? Thread-Safe - No shared mutable state
  • ? Well-Tested - 58+ unit tests with ~100% API coverage
  • ? Cross-Platform - Works on x86, x64, and AnyCPU
  • ? Fast & Lightweight - Minimal overhead
  • ? CI/CD Ready - Automated testing and deployment
  • ? Production-Ready - Comprehensive error handling

?? Platform Compatibility

ValidateJWT is built as AnyCPU and works on both x86 and x64 platforms:

  • ? Windows x86 (32-bit)
  • ? Windows x64 (64-bit)
  • ? .NET Framework 4.7.2 or higher
  • ? No native dependencies
  • ? Pure managed code

System Requirements

  • OS: Windows 7 SP1 or higher
  • .NET: Framework 4.7.2 or higher
  • Architecture: Any (x86, x64, AnyCPU)

?? Installation

NuGet Package Manager

Install-Package ValidateJWT

.NET CLI

dotnet add package ValidateJWT

PackageReference

<PackageReference Include="ValidateJWT" Version="1.1.0" />

?? Quick Start

Time-Based Validation (Fast Pre-Check)

using Johan.Common;

var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."";

// Check if token is expired
if (ValidateJWT.IsExpired(token))
{
    Console.WriteLine("Token has expired");
}

// Check if token is currently valid
if (ValidateJWT.IsValidNow(token))
{
    Console.WriteLine("Token is valid");
}

// Get expiration time
DateTime? expiration = ValidateJWT.GetExpirationUtc(token);
Console.WriteLine($"Expires: {expiration}");

Signature Verification (Complete Validation) ??

using Johan.Common;

var token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
var secret = "your-secret-key";

// Verify signature with HS256
var result = ValidateJWT.VerifySignature(token, secret);

if (result.IsValid && !result.IsExpired)
{
    Console.WriteLine("? Token is valid and not expired");
}
else if (!result.IsValid)
{
    Console.WriteLine($"? Invalid signature: {result.ErrorMessage}");
}
else if (result.IsExpired)
{
    Console.WriteLine("? Token has expired");
}

?? API Reference

Time-Based Validation Methods

IsExpired(jwt, clockSkew, nowUtc)

Checks if a JWT token has expired.

bool isExpired = ValidateJWT.IsExpired(token);
bool isExpired = ValidateJWT.IsExpired(token, TimeSpan.FromMinutes(10));

Parameters:

  • jwt (string) - JWT token
  • clockSkew (TimeSpan?) - Clock skew tolerance (default: 5 minutes)
  • nowUtc (DateTime?) - Current UTC time (default: DateTime.UtcNow)

Returns: bool - True if expired, false otherwise


IsValidNow(jwt, clockSkew, nowUtc)

Checks if a JWT token is currently valid.

bool isValid = ValidateJWT.IsValidNow(token);

Returns: bool - True if valid, false if expired or invalid


GetExpirationUtc(jwt)

Extracts the expiration time from a JWT token.

DateTime? expiration = ValidateJWT.GetExpirationUtc(token);

Returns: DateTime? - Expiration time in UTC, or null if not found


Signature Verification Methods ??

VerifySignature(jwt, secretKey)

Verifies JWT signature using HMAC-SHA256 (HS256).

var result = ValidateJWT.VerifySignature(token, "your-secret-key");

if (result.IsValid && !result.IsExpired)
{
    // Token is fully validated
}

Parameters:

  • jwt (string) - JWT token
  • secretKey (string) - Secret key used to sign the token

Returns: JwtVerificationResult

  • IsValid (bool) - Whether signature is valid
  • Algorithm (string) - Algorithm used (e.g., "HS256")
  • ErrorMessage (string) - Error details if failed
  • IsExpired (bool) - Whether token is expired

VerifySignatureRS256(jwt, publicKeyXml)

Verifies JWT signature using RSA-SHA256 (RS256).

var publicKey = "<RSAKeyValue>...</RSAKeyValue>";
var result = ValidateJWT.VerifySignatureRS256(token, publicKey);

Parameters:

  • jwt (string) - JWT token
  • publicKeyXml (string) - RSA public key in XML format

Returns: JwtVerificationResult


GetAlgorithm(jwt) ??

Gets the algorithm from the JWT header.

string algorithm = ValidateJWT.GetAlgorithm(token);
// Returns: "HS256", "RS256", etc.

Claim Validation Methods

IsIssuerValid(jwt, expectedIssuer)

Validates the 'iss' (issuer) claim in a JWT token.

bool isIssuerValid = ValidateJWT.IsIssuerValid(token, "https://my-issuer.example.com");

Returns: bool - True if the issuer matches, false otherwise or if the claim is missing/invalid.

IsAudienceValid(jwt, expectedAudience)

Validates the 'aud' (audience) claim in a JWT token. Supports both single audience and audience arrays.

bool isAudienceValid = ValidateJWT.IsAudienceValid(token, "my-api");

Returns: bool - True if the audience matches, false otherwise or if the claim is missing/invalid.

IsNotBeforeValid(jwt, clockSkew, nowUtc)

Validates the 'nbf' (not before) claim in a JWT token.

bool isNotBeforeValid = ValidateJWT.IsNotBeforeValid(token);
bool isNotBeforeValid = ValidateJWT.IsNotBeforeValid(token, TimeSpan.FromMinutes(2));

Parameters:

  • jwt (string) - JWT token
  • clockSkew (TimeSpan?) - Clock skew tolerance (default: 5 minutes)
  • nowUtc (DateTime?) - Current UTC time (default: DateTime.UtcNow)

Returns: bool - True if the token is not being used before its 'nbf' time, false otherwise.

GetNotBeforeUtc(jwt)

Extracts the 'nbf' (not before) time from a JWT token.

DateTime? notBefore = ValidateJWT.GetNotBeforeUtc(token);

Returns: DateTime? - Not before time in UTC, or null if not found.

GetIssuedAtUtc(jwt)

Extracts the 'iat' (issued at) time from a JWT token.

DateTime? issuedAt = ValidateJWT.GetIssuedAtUtc(token);

Returns: DateTime? - Issued at time in UTC, or null if not found.

GetAudience(jwt)

Extracts the 'aud' (audience) claim from a JWT token.

string audience = ValidateJWT.GetAudience(token);

Returns: string - Audience value, or null if not found. For audience arrays, returns the first audience.


Helper Methods

Base64UrlDecode(input)

Decodes Base64URL encoded strings.

byte[] decoded = ValidateJWT.Base64UrlDecode("SGVsbG8gV29ybGQ");
Base64UrlEncode(input) ??

Encodes byte arrays to Base64URL format.

byte[] data = Encoding.UTF8.GetBytes("Hello");
string encoded = ValidateJWT.Base64UrlEncode(data);

?? Usage Scenarios

Scenario 1: Quick Expiration Check (Fastest)

// Fast pre-check before expensive operations
if (ValidateJWT.IsExpired(token))
{
    return Unauthorized("Token expired");
}

// Proceed with API call
// Full signature and expiration validation
var result = ValidateJWT.VerifySignature(token, secretKey);

if (!result.IsValid)
{
    return Unauthorized($"Invalid token: {result.ErrorMessage}");
}

if (result.IsExpired)
{
    return Unauthorized("Token expired");
}

// Token is fully validated

Scenario 3: Two-Stage Validation (Optimized)

// Stage 1: Quick time check (0.1ms)
if (ValidateJWT.IsExpired(token))
{
    return Unauthorized("Token expired");
}

// Stage 2: Signature verification (0.5-5ms)
var result = ValidateJWT.VerifySignature(token, secretKey);

if (!result.IsValid)
{
    return Unauthorized("Invalid signature");
}

// Token is valid

Scenario 4: Multi-Algorithm Support

var algorithm = ValidateJWT.GetAlgorithm(token);

JwtVerificationResult result;
switch (algorithm)
{
    case "HS256":
        result = ValidateJWT.VerifySignature(token, secretKey);
        break;
    case "RS256":
        result = ValidateJWT.VerifySignatureRS256(token, publicKey);
        break;
    default:
        return Unauthorized($"Unsupported algorithm: {algorithm}");
}

if (result.IsValid && !result.IsExpired)
{
    // Token is valid
}

?? Configuration

Clock Skew Tolerance

Account for time synchronization issues between servers:

// Default: 5 minutes
bool isExpired = ValidateJWT.IsExpired(token);

// Custom: 10 minutes
bool isExpired = ValidateJWT.IsExpired(token, TimeSpan.FromMinutes(10));

// No clock skew
bool isExpired = ValidateJWT.IsExpired(token, TimeSpan.Zero);

Time Injection (Testing)

Inject custom time for deterministic testing:

var testTime = new DateTime(2025, 1, 15, 12, 0, 0, DateTimeKind.Utc);
bool isExpired = ValidateJWT.IsExpired(token, null, testTime);

?? Security Considerations

? What This Library Provides

  • ? JWT expiration validation
  • ? Signature verification (HS256, RS256)
  • ? Algorithm detection
  • ? Clock skew handling
  • ? Comprehensive error handling

?? What This Library Does NOT Provide

  • ? Issuer (iss) claim validation
  • ? Audience (aud) claim validation
  • ? Not-before (nbf) claim validation (planned for v1.2)
  • ? Token generation/signing

?? Best Practices

  1. Always verify signatures in production:

    var result = ValidateJWT.VerifySignature(token, secret);
    if (!result.IsValid) return Unauthorized();
    
  2. Store secrets securely:

    // ? Good - from configuration
    var secret = _configuration["JWT:Secret"];
    
    // ? Bad - hardcoded
    var secret = "my-secret-123";
    
  3. Use appropriate algorithm:

    • HS256 - Shared secret, both parties trust each other
    • RS256 - Public/private key, issuer signs, anyone verifies
  4. Combine with full JWT validation:

    // Step 1: Quick expiration check
    if (ValidateJWT.IsExpired(token)) return Unauthorized();
    
    // Step 2: Signature verification
    var result = ValidateJWT.VerifySignature(token, secret);
    if (!result.IsValid) return Unauthorized();
    
    // Step 3: Validate claims (issuer, audience, etc.)
    // ... your claim validation logic
    

?? Testing

Comprehensive test suite with 58+ unit tests:

// Test expiration
[TestMethod]
public void IsExpired_ExpiredToken_ReturnsTrue()
{
    var token = CreateExpiredToken();
    Assert.IsTrue(ValidateJWT.IsExpired(token));
}

// Test signature verification
[TestMethod]
public void VerifySignature_ValidToken_ReturnsTrue()
{
    var result = ValidateJWT.VerifySignature(validToken, secret);
    Assert.IsTrue(result.IsValid);
}

Test Coverage:

  • 13 tests for IsExpired()
  • 12 tests for IsValidNow()
  • 10 tests for GetExpirationUtc()
  • 18 tests for Base64UrlDecode()
  • Plus additional tests for signature verification

?? Performance

Operation Time Notes
Time Check ~0.1ms Very fast
HS256 Verify ~0.5-1ms HMAC verification
RS256 Verify ~2-5ms RSA verification (slower)

Optimization tip: Check expiration first, then verify signature.

?? Version History

v1.3.0 (Latest) ??

  • Added comprehensive JWT claim validation
    • IsAudienceValid() for 'aud' claim validation (supports single and array formats)
    • IsNotBeforeValid() for 'nbf' claim validation
    • GetNotBeforeUtc() for extracting 'nbf' timestamps
    • GetIssuedAtUtc() for extracting 'iat' timestamps
    • GetAudience() for extracting 'aud' claims
  • Enhanced claim parsing with better error handling
  • Support for audience arrays in addition to single audience strings
  • All previous features retained

v1.2.0

  • Added IsIssuerValid() for 'iss' claim validation
  • All previous features retained

v1.1.0

  • Added JWT signature verification (HS256, RS256)
  • New VerifySignature() and VerifySignatureRS256() methods
  • New JwtVerificationResult class
  • New GetAlgorithm() helper
  • New Base64UrlEncode() helper
  • 100% backward compatible with v1.0.x

v1.0.1

  • Documentation improvements
  • Enhanced NuGet package metadata
  • Clean namespace (Johan.Common)

v1.0.0

  • Initial release
  • Time-based JWT validation
  • Base64URL encoding/decoding
  • 58+ comprehensive tests

See CHANGELOG.md for complete history.

?? Documentation

User Documentation

Developer Documentation

Maintenance Documentation

?? CI/CD & Automation

GitHub Actions Workflows

ValidateJWT includes a complete CI/CD pipeline:

  • ?? Continuous Integration - Automated build and test on every push
  • ? Pull Request Validation - Automatic testing on PRs
  • ? Nightly Builds - Scheduled regression testing
  • ? Code Coverage - Automated coverage reporting
  • ? Security Scanning - Vulnerability detection
  • ? Automated Publishing - Zero-touch NuGet deployment

Automation Scripts

Testing:

# Run all tests with coverage
.\Run-AutomatedTests.ps1 -GenerateCoverage

# Fix and run tests
.\Fix-And-RunTests.ps1

Publishing:

# Build NuGet package
.\BuildNuGetPackage.bat

# Publish to NuGet.org
.\Publish-NuGet.ps1 -Version "1.1.0"

Maintenance:

# Remove unused references
.\Remove-UnusedReferences.ps1

# Clean company references
.\Remove-CompanyReferences.ps1

# Setup CI/CD
.\Setup-CICD.ps1

See CI_CD_GUIDE.md for complete automation documentation.

?? Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Add tests for new functionality
  4. Ensure all tests pass (.\Run-AutomatedTests.ps1)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

Development Setup

# Clone repository
git clone https://github.com/johanhenningsson4-hash/ValidateJWT.git
cd ValidateJWT

# Restore packages
nuget restore ValidateJWT.sln

# Build
msbuild ValidateJWT.sln /p:Configuration=Release

# Run tests
.\Run-AutomatedTests.ps1

Code Quality Standards

  • ? Maintain ~100% test coverage
  • ? Follow existing code style
  • ? Add XML documentation for public APIs
  • ? Include usage examples in tests
  • ? Update CHANGELOG.md for changes

?? Support

For questions, issues, or feature requests:

  • Open an issue on GitHub
  • Check CI_CD_GUIDE.md for automation help
  • Review test examples in ValidateJWT.Tests
  • See documentation files for specific topics

Made with ?? for the .NET community

Author: Johan Henningsson
Version: 1.1.0
Framework: .NET Framework 4.7.2
Last Updated: January 2026
Status: ? Production-Ready | ? CI/CD Enabled | ? Fully Automated

?? Build Instructions

To build the library and run tests, always use the platform string AnyCPU (no space):

msbuild ValidateJWT.sln /p:Configuration=Release /p:Platform=AnyCPU

If you use AnyCPU (with a space), you may get an error about BaseOutputPath/OutputPath property is not set.

Troubleshooting:

  • If you see an error about BaseOutputPath/OutputPath property is not set, check that you are using AnyCPU (no space) for the platform.
  • Both Debug and Release configurations are supported for AnyCPU, x64, and x86 (if defined in the project file).

Product Compatible and additional computed target framework versions.
.NET Framework net472 is compatible.  net48 was computed.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.7.2

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 56 2/7/2026
1.2.1 72 1/12/2026
1.1.0 77 1/6/2026
1.0.1 67 1/6/2026 1.0.1 is deprecated because it is no longer maintained.

v1.3.0 - Adds comprehensive JWT claim validation: IsAudienceValid, IsNotBeforeValid, GetNotBeforeUtc, GetIssuedAtUtc, GetAudience.