Guardhouse.SDK 1.0.1

Prefix Reserved
dotnet add package Guardhouse.SDK --version 1.0.1
                    
NuGet\Install-Package Guardhouse.SDK -Version 1.0.1
                    
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="Guardhouse.SDK" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Guardhouse.SDK" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Guardhouse.SDK" />
                    
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 Guardhouse.SDK --version 1.0.1
                    
#r "nuget: Guardhouse.SDK, 1.0.1"
                    
#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 Guardhouse.SDK@1.0.1
                    
#: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=Guardhouse.SDK&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Guardhouse.SDK&version=1.0.1
                    
Install as a Cake Tool

Guardhouse SDK for .NET

Official .NET SDK for Guardhouse Cloud. Use it in .NET applications to request access tokens or validate incoming tokens against your Guardhouse instance.

Installation

Install via NuGet:

dotnet add package Guardhouse.SDK

Or via NuGet Package Manager Console:

Install-Package Guardhouse.SDK

Supported Frameworks: .NET 6.0, 7.0, 8.0, 9.0, 10.0

Quick Start

Client Application (Requesting Access Tokens)

Configure your application as a Client to request access tokens:

using Guardhouse.SDK.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGuardhouseClient(options =>
{
    options.Authority = "https://your-guardhouse-server.com";
    options.ClientId = "your-client-id";
    options.ClientSecret = "your-client-secret";
    options.Scope = "api";
    options.EnableTokenCaching = true;
    options.EnableTokenRefresh = true;
    options.IncludeOfflineAccessScope = true;
});

var app = builder.Build();

// Example: Get access token and call protected API
app.MapGet("/api/call-protected", async (IGuardhouseTokenService tokenService) =>
{
    var accessToken = await tokenService.GetAccessTokenAsync();

    using var client = new HttpClient();
    client.DefaultRequestHeaders.Authorization =
        new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

    var response = await client.GetAsync("https://protected-api.com/data");
    var data = await response.Content.ReadAsStringAsync();

    return Results.Ok(new
    {
        Response = data,
        AccessTokenPreview = accessToken.Substring(0, Math.Min(20, accessToken.Length)) + "..."
    });
});

app.Run();

Minimal configuration:

builder.Services.AddGuardhouseClient(
    authority: "https://your-guardhouse-server.com",
    clientId: "your-client-id",
    clientSecret: "your-client-secret",
    scope: "api offline_access"
);

Resource Server (Protecting APIs)

Configure your API as a Resource to validate incoming tokens:

using Guardhouse.SDK.Extensions;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddGuardhouseResource(options =>
{
    options.Authority = "https://your-guardhouse-server.com";
    options.Audience = "my_resource_api";
    options.ValidationMode = TokenValidationMode.JwtSignature;
});

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

// Protected endpoint
app.MapGet("/api/protected", [Authorize] () =>
{
    return new { Message = "This is protected data", Timestamp = DateTime.UtcNow };
});

app.Run();

With Token Introspection (RFC 7662):

builder.Services.AddGuardhouseResource(options =>
{
    options.Authority = "https://your-guardhouse-server.com";
    options.Audience = "my_resource_api";
    options.ValidationMode = TokenValidationMode.Introspection;
    options.IntrospectionClientId = "your-introspection-client-id";
    options.IntrospectionClientSecret = "your-introspection-client-secret";
});

By default, the SDK sends introspection credentials as form data (client_id and client_secret).

If your identity server requires HTTP Basic Authentication for introspection, set credential transmission to BasicAuth:

builder.Services.AddGuardhouseResource(options =>
{
    options.Authority = "https://your-guardhouse-server.com";
    options.Audience = "my_resource_api";
    options.ValidationMode = TokenValidationMode.Introspection;
    options.IntrospectionClientId = "your-introspection-client-id";
    options.IntrospectionClientSecret = "your-introspection-client-secret";
    options.IntrospectionCredentialTransmission = IntrospectionCredentialTransmission.BasicAuth;
});

Configuration

Client Options

builder.Services.AddGuardhouseClient(options =>
{
    options.Authority = "https://your-guardhouse-server.com";            // Required
    options.RequireHttps = true;                                          // Default: true
    options.ClientId = "your-client-id";                                 // Required
    options.ClientSecret = "your-client-secret";                         // Required
    options.Scope = "api";                                                // Default: "api"

    // Token caching & refresh
    options.EnableTokenCaching = true;                                   // Default: true
    options.CacheExpirationBufferSeconds = 60;                           // Default: 60
    options.RefreshTokenCacheDurationDays = 30;                          // Default: 30
    options.EnableTokenRefresh = true;                                   // Default: true
    options.IncludeOfflineAccessScope = false;                           // Default: false (set true if your identity server requires offline_access for refresh tokens)

    // Resilience
    options.EnableHttpResilience = true;                                 // Default: true
    options.RequestTimeoutSeconds = 30;                                  // Default: 30
    options.MaxRetryAttempts = 3;                                        // Default: 3

    // Introspection (optional - for debugging purposes)
    options.IntrospectionClientId = "your-introspection-client-id";      // Optional
    options.IntrospectionClientSecret = "your-introspection-client-secret"; // Optional
    options.IntrospectionCredentialTransmission = IntrospectionCredentialTransmission.FormData; // Default: FormData

    options.EnableDebug = false;                                         // Default: false
});

Resource Options

builder.Services.AddGuardhouseResource(options =>
{
    options.Authority = "https://your-guardhouse-server.com";            // Required
    options.Audience = "my_resource_api";                                // Required
    options.PolicyName = "Guardhouse";                                    // Default: "Guardhouse"

    // Validation mode: JWT Signature or Introspection
    options.ValidationMode = TokenValidationMode.JwtSignature;            // Default: JWT Signature

    // Required for Introspection mode
    options.IntrospectionClientId = "your-client-id";                     // Required for introspection
    options.IntrospectionClientSecret = "your-client-secret";             // Required for introspection
    options.IntrospectionCredentialTransmission = IntrospectionCredentialTransmission.FormData; // Default: FormData

    // Token validation settings
    options.ValidateIssuer = true;                                         // Default: true
    options.ValidateAudience = true;                                       // Default: true
    options.ValidateLifetime = true;                                       // Default: true
    options.ValidateIssuerSigningKey = true;                               // Default: true

    // JWT validation
    options.ValidAlgorithms = new[] { "RS256" };                           // Default: RS256
    options.TokenTypes = new[] { "at+jwt" };                               // Default: at+jwt
    options.IntrospectionTokenTypes = new[] { "access_token" };            // Optional

    // JWKS caching
    options.JwksCacheDurationHours = 24;                                   // Default: 24 hours
    options.JwksRefreshIntervalMinutes = 5;                                // Default: 5 minutes
    options.JwksAllowedHosts = new[] { "keys.guardhouse.cloud" };          // Optional

    // Introspection caching (micro-cache for burst traffic)
    options.IntrospectionCacheTtlSeconds = 5;                              // Default: 5 seconds
    options.IntrospectionNegativeCacheTtlSeconds = 10;                     // Default: 10 seconds

    // HTTPS and metadata
    options.RequireHttps = true;                                           // Default: true
    options.RequireHttpsMetadata = null;                                   // Default: derived from authority
    options.SaveToken = false;                                             // Default: false

    options.RequestTimeoutSeconds = 30;                                    // Default: 30
    options.MaxRetryAttempts = 3;                                          // Default: 3
    options.EnableDebug = false;                                           // Default: false
});

Features

Token Management

Automatic Token Caching - Tokens cached in memory with configurable expiration buffer

Automatic Token Refresh - Uses refresh tokens when available

HTTP Resilience - Retry and timeout policies with Polly

Client Introspection - Optional RFC 7662 introspection for debugging

Resource Server Validation

JWT Signature Validation - JWKS discovery with lazy refresh on unknown keys

Introspection Mode - RFC 7662 validation with micro-cache and negative cache

Configurable Validation Rules - Issuer, audience, lifetime, algorithms, token types

Backchannel Hardening - Allowed hosts and HTTPS enforcement for metadata/JWKS

Developer Experience

Configuration Validation - Fails fast at startup for missing required settings

Dependency Injection - AddGuardhouseClient, AddGuardhouseResource, AddGuardhouse

Logging Integration - Debug logging for token and JWKS diagnostics

API Reference

Client Service

public interface IGuardhouseTokenService
{
    Task<string> GetAccessTokenAsync(CancellationToken cancellationToken = default);
    Task<TokenResponse> RequestTokenAsync(CancellationToken cancellationToken = default);
    Task<TokenResponse> RefreshTokenAsync(string refreshToken, CancellationToken cancellationToken = default);
    Task<IntrospectionResponse> IntrospectTokenAsync(string token, CancellationToken cancellationToken = default);
    Task<bool> IsTokenActiveAsync(string token, CancellationToken cancellationToken = default);
}

Resource Service

public interface IGuardhouseResourceService
{
    Task<ClaimsPrincipal?> ValidateTokenAsync(string token, CancellationToken cancellationToken = default);
    Task<IntrospectionResponse> IntrospectTokenAsync(string token, CancellationToken cancellationToken = default);
    Task<AuthenticationScheme?> GetDefaultSchemeAsync(CancellationToken cancellationToken = default);
}

Security Features

Validation Modes

JWT Signature Mode (Default):

  • Uses OpenID configuration and JWKS for token validation
  • Automatic key rotation support with lazy refresh on unknown kid
  • Validates signature, issuer, audience, lifetime, algorithm, token type

Introspection Mode (RFC 7662):

  • Validates tokens via Guardhouse introspection endpoint
  • Client credentials required for introspection calls
  • Micro-cache for burst traffic handling with positive/negative TTLs

Validation Checks

  • Algorithm allowlist with explicit rejection of "none"
  • Token type allowlist (default: at+jwt)
  • Issuer and audience validation with normalized issuer matching
  • Lifetime validation with clock skew
  • HTTPS requirement and allowed host checks for metadata/JWKS/introspection

JWKS Caching with Lazy Refresh

The SDK implements "Lazy Refresh on Unknown Key" strategy:

  1. Extract kid (Key ID) from incoming JWT token header
  2. Check local cache for key
  3. If key exists: Validate signature (fast path)
  4. If key missing: Trigger JWKS refresh via discovery or /.well-known/jwks
  5. Re-check cache and validate with new keys

This ensures your API accepts valid tokens even after key rotation, while using configurable cache and refresh intervals for regular refresh behavior.

Introspection Micro-Cache Strategy

The SDK implements a micro-cache for introspection results:

  • Active TTL: 5 seconds (default)
  • Inactive TTL: 10 seconds (default)
  • Purpose: Handles burst traffic while keeping revocation checks near real-time

Use introspection mode when you need real-time revocation checking, and tune TTLs to balance freshness and throughput.

Examples

Calling Protected APIs

public class ExternalApiService
{
    private readonly IGuardhouseTokenService _tokenService;

    public ExternalApiService(IGuardhouseTokenService tokenService)
    {
        _tokenService = tokenService;
    }

    public async Task<string> CallProtectedEndpoint()
    {
        var accessToken = await _tokenService.GetAccessTokenAsync();

        using var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization =
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

        var response = await client.GetAsync("https://protected-api.com/data");
        return await response.Content.ReadAsStringAsync();
    }
}

Protecting Endpoints

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    [HttpGet]
    public IActionResult GetAll()
    {
        return Ok(new[] { "Product 1", "Product 2" });
    }

    [HttpPost]
    [Authorize]
    public IActionResult Create([FromBody] CreateProductRequest request)
    {
        return CreatedAtAction(nameof(GetById), new { id = 1 }, request);
    }

    [HttpDelete("{id}")]
    [Authorize]
    public IActionResult Delete(int id)
    {
        return Ok(new { Message = $"Product {id} deleted" });
    }
}

Authorization Policies

builder.Services.AddAuthorization(options =>
{
    // Require specific scope
    options.AddPolicy("ReadAccess", policy =>
        policy.RequireClaim("scope", "read"));

    // Require admin role
    options.AddPolicy("AdminOnly", policy =>
        policy.RequireRole("admin"));

    // Require multiple conditions
    options.AddPolicy("CanManage", policy =>
        policy.RequireClaim("scope", "manage")
              .RequireRole("manager"));
});

[HttpGet("admin")]
[Authorize(Policy = "AdminOnly")]
public IActionResult AdminEndpoint()
{
    return Ok("Admin data");
}

Dependencies

  • .NET 6.0 or later (supports .NET 6.0, 7.0, 8.0, 9.0, 10.0)
  • Microsoft.Extensions.DependencyInjection
  • Microsoft.Extensions.Http
  • Microsoft.Extensions.Http.Polly
  • Microsoft.Extensions.Caching.Memory
  • Microsoft.Extensions.Options
  • Microsoft.AspNetCore.Authentication.JwtBearer
  • System.IdentityModel.Tokens.Jwt
  • System.Text.Json
  • NodaTime
  • Polly

Troubleshooting

Configuration Errors

Error: "IntrospectionClientId is required when ValidationMode is set to Introspection"

Add introspection credentials to your configuration:

builder.Services.AddGuardhouseResource(options =>
{
    options.ValidationMode = TokenValidationMode.Introspection;
    options.IntrospectionClientId = "your-client-id";
    options.IntrospectionClientSecret = "your-client-secret";
});

Token Validation Issues

Error: "401 Unauthorized"

  1. Verify your Guardhouse credentials are correct
  2. Check that Authority URL matches your Guardhouse instance
  3. Ensure client is enabled in Guardhouse Cloud
  4. Verify token hasn't expired

Error: "invalid_client" when using introspection

If your identity server requires Basic Authentication headers, switch to BasicAuth credential transmission:

builder.Services.AddGuardhouseResource(options =>
{
    options.ValidationMode = TokenValidationMode.Introspection;
    options.IntrospectionCredentialTransmission = IntrospectionCredentialTransmission.BasicAuth;
});

This sends credentials in the HTTP Authorization: Basic header.

JWKS Refresh Issues

Error: "kid not found" or signature validation fails

  1. Check your Authority URL is correct
  2. Verify Guardhouse server is accessible
  3. Check that JwksRefreshIntervalMinutes is appropriate (default 5)
  4. Check application logs for JWKS refresh errors

Documentation

License

Licensed under Apache License 2.0

Support

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.0.1 81 3/2/2026
1.0.0 116 2/20/2026
1.0.0-beta17 261 1/4/2026
1.0.0-beta16 96 1/4/2026
1.0.0-beta15 92 1/2/2026
1.0.0-beta14 92 1/2/2026
1.0.0-beta1 95 1/2/2026

1.0.1 - patch release with introspection validation reliability fix.

           Fixes:
           - Prevent false "Algorithm '' is not allowed" rejections when introspection returns active=true without alg
           - For JWT tokens, derive alg from JWT header when introspection alg is missing
           - Keep algorithm allowlist enforcement (including none rejection) and add regression tests