identity-testing-patterns
Testing patterns for IdentityServer-based systems including integration testing with WebApplicationFactory, mock token issuance, test authority configuration, protocol response validation, and end-to-end authentication flow testing.
What this skill does
# Identity Testing Patterns
## When to Use This Skill
Use this skill when:
- Writing integration tests for applications that issue or validate tokens using Duende IdentityServer
- Hosting IdentityServer in-memory with `WebApplicationFactory<T>` to test grant flows end-to-end
- Creating mock JWT tokens for testing protected APIs without a live authority
- Testing custom `IProfileService` implementations or claim transformation logic
- Verifying `IAuthorizationHandler` and policy-based authorization against specific claim sets
- Testing BFF endpoints that rely on cookie-based sessions and proxied API calls
## Core Principles
1. **Integration over unit** — Test token issuance, claim mapping, and policy enforcement against a real (in-process) IdentityServer instance. Avoid mocking the token pipeline itself; mock only external I/O (databases, downstream services).
2. **In-process authority** — Use `WebApplicationFactory<T>` to host IdentityServer inside the test process. This avoids network round-trips, eliminates certificate trust issues, and makes tests deterministic.
3. **Predictable signing keys** — Override key management in tests with a static development signing key so token signatures are verifiable without key rotation logic.
4. **Minimal test clients** — Register only the clients, scopes, and resources each test needs. Over-broad test configurations mask permission bugs.
5. **Test auth handler for API tests** — When testing protected APIs in isolation (without a live token endpoint), replace JWT Bearer authentication with a `TestAuthHandler` that accepts a fake scheme. Never disable authorization wholesale.
6. **Builder pattern for test data** — Use fluent builders for `Client`, `ApiScope`, `ApiResource`, and test users to keep test setup readable and reduce duplication.
## Related Skills
- `identityserver-configuration` — Production client and resource registration patterns
- `aspnetcore-authentication` — OIDC and JWT Bearer handler configuration
- `aspnetcore-authorization` — Policy definitions and requirement handlers
- `claims-authorization` — `IProfileService` and claim pipeline internals
- `duende-bff` — BFF session and proxy architecture being tested
Docs: https://docs.duendesoftware.com/identityserver/fundamentals
---
## Sub-Documents
| Document | Description | When to Load |
|----------|-------------|--------------|
| [docs/bff-testing.md](docs/bff-testing.md) | BFF endpoint testing with cookie simulation, antiforgery headers, and OIDC redirect bypass | BFF testing, CookieContainer, x-csrf header, BffFactory, session simulation |
| [docs/aspire-testing.md](docs/aspire-testing.md) | Full-stack Aspire testing with identity server health checks and token endpoint wiring | Aspire testing, DistributedApplicationTestingBuilder, WaitForResourceHealthyAsync, end-to-end |
---
## Testing Strategy Overview
| What to test | Recommended approach |
|---|---|
| Token issuance (client credentials, code flow) | In-process `WebApplicationFactory` hitting `/connect/token` |
| Claim mapping / `IProfileService` | Unit test with `DefaultProfileService` + mock context, or integration test |
| Authorization policy requirements | `IAuthorizationService` + `TestAuthHandler` in integration test |
| `IAuthorizationHandler` logic | Direct unit test with `AuthorizationHandlerContext` |
| Protected API access control | `WebApplicationFactory` with `TestAuthHandler` and constructed `ClaimsPrincipal` |
| BFF endpoints (login/logout/user) | `WebApplicationFactory` with cookie simulation |
| EF Core store implementations | In-memory EF provider or isolated SQL container |
---
## Pattern 1: WebApplicationFactory for IdentityServer
Host a complete IdentityServer in-memory. Override configuration to inject test clients, resources, and a static signing key.
### Required NuGet Packages
```xml
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="*" />
<PackageReference Include="xunit" Version="*" />
<PackageReference Include="xunit.runner.visualstudio" Version="*" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="*" />
<PackageReference Include="IdentityModel" Version="*" />
</ItemGroup>
```
### IdentityServer WebApplicationFactory
```csharp
// ✅ Factory that runs a real IdentityServer in-process
public sealed class IdentityServerFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
builder.ConfigureTestServices(services =>
{
// Remove any existing IdentityServer registration to replace it cleanly
var descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(IConfigureOptions<IdentityServerOptions>));
if (descriptor is not null)
services.Remove(descriptor);
services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseFailureEvents = true;
// Disable automatic key management — use a static key for predictability
options.KeyManagement.Enabled = false;
})
.AddInMemoryClients(TestConfig.Clients)
.AddInMemoryApiScopes(TestConfig.ApiScopes)
.AddInMemoryApiResources(TestConfig.ApiResources)
.AddInMemoryIdentityResources(TestConfig.IdentityResources)
.AddTestUsers(TestConfig.Users)
// Static development signing key — never use this in production
.AddDeveloperSigningCredential(persistKey: false);
});
}
}
```
### Requesting a Token in a Test
```csharp
[Collection("IdentityServer")]
public class TokenEndpointTests : IClassFixture<IdentityServerFactory>
{
private readonly HttpClient _client;
public TokenEndpointTests(IdentityServerFactory factory)
{
_client = factory.CreateClient();
}
[Fact]
public async Task ClientCredentials_ShouldReturnAccessToken()
{
var response = await _client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = "https://localhost/connect/token",
ClientId = "test.service",
ClientSecret = "test-secret",
Scope = "api1"
});
Assert.False(response.IsError, response.Error);
Assert.NotEmpty(response.AccessToken);
Assert.Equal("Bearer", response.TokenType);
}
[Fact]
public async Task ClientCredentials_InvalidScope_ShouldReturnError()
{
var response = await _client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
Address = "https://localhost/connect/token",
ClientId = "test.service",
ClientSecret = "test-secret",
Scope = "not.allowed" // ❌ scope not granted to this client
});
Assert.True(response.IsError);
Assert.Equal("invalid_scope", response.Error);
}
}
```
---
## Pattern 2: Test Configuration Builders
Use static builders — not scattered inline literals — so every test builds from a consistent baseline.
```csharp
public static class TestConfig
{
public static IEnumerable<Client> Clients =>
[
ClientBuilder.ClientCredentials("test.service", "test-secret")
.WithScopes("api1", "api2.read")
.Build(),
ClientBuilder.AuthorizationCode("test.webapp", "webapp-secret")
.WithRedirectUri("https://testapp/signin-oidc")
.WithScopes("openid", "profile", "api1")
.Build()
];
public static IEnumerable<ApiScope> ApiScopes =>
[
new ApiScope("api1", "Primary API"),
new ApiScope("api2.read", "Read from API 2")
];
public static IEnumerable<ApiResource> ApiResources =>
[
new ApiResource("apRelated in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.