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
<PackageReference Include="Guardhouse.SDK" Version="1.0.1" />
<PackageVersion Include="Guardhouse.SDK" Version="1.0.1" />
<PackageReference Include="Guardhouse.SDK" />
paket add Guardhouse.SDK --version 1.0.1
#r "nuget: Guardhouse.SDK, 1.0.1"
#:package Guardhouse.SDK@1.0.1
#addin nuget:?package=Guardhouse.SDK&version=1.0.1
#tool nuget:?package=Guardhouse.SDK&version=1.0.1
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:
- Extract
kid(Key ID) from incoming JWT token header - Check local cache for key
- If key exists: Validate signature (fast path)
- If key missing: Trigger JWKS refresh via discovery or
/.well-known/jwks - 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"
- Verify your Guardhouse credentials are correct
- Check that Authority URL matches your Guardhouse instance
- Ensure client is enabled in Guardhouse Cloud
- 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
- Check your Authority URL is correct
- Verify Guardhouse server is accessible
- Check that
JwksRefreshIntervalMinutesis appropriate (default 5) - Check application logs for JWKS refresh errors
Documentation
- Guardhouse Documentation: https://guardhouse.cloud/docs
- GitHub Issues: https://github.com/legiosoft/guardhouse-sdk-dotnet/issues
- Deployment Guide: docs/DEPLOYMENT.md
License
Licensed under Apache License 2.0
Support
- Guardhouse Cloud: https://guardhouse.cloud
- Documentation: https://guardhouse.cloud/docs
- GitHub Repository: https://github.com/legiosoft/guardhouse-sdk-dotnet
| Product | Versions 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. |
-
net10.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 10.0.1)
- Microsoft.Extensions.Http.Polly (>= 10.0.1)
- NodaTime (>= 3.2.3)
- Polly (>= 8.6.5)
- System.IdentityModel.Tokens.Jwt (>= 8.15.0)
-
net6.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 6.0.36)
- Microsoft.Extensions.Http.Polly (>= 6.0.33)
- NodaTime (>= 3.2.3)
- Polly (>= 8.6.5)
- System.IdentityModel.Tokens.Jwt (>= 8.15.0)
-
net7.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 7.0.20)
- Microsoft.Extensions.Http.Polly (>= 7.0.20)
- NodaTime (>= 3.2.3)
- Polly (>= 8.6.5)
- System.IdentityModel.Tokens.Jwt (>= 8.15.0)
-
net8.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.12)
- Microsoft.Extensions.Http.Polly (>= 8.0.12)
- NodaTime (>= 3.2.3)
- Polly (>= 8.6.5)
- System.IdentityModel.Tokens.Jwt (>= 8.15.0)
-
net9.0
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 9.0.2)
- Microsoft.Extensions.Http.Polly (>= 9.0.2)
- NodaTime (>= 3.2.3)
- Polly (>= 8.6.5)
- System.IdentityModel.Tokens.Jwt (>= 8.15.0)
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