feat(security): explicit security scheme contract, API key extraction, and plugin validation - #32
Merged
Merged
Conversation
rian-be
marked this pull request as draft
September 11, 2026 11:12
rian-be
marked this pull request as ready for review
September 11, 2026 11:18
Address PR review: Name must stay a transport-agnostic scheme identity and must not double as the credential transport field. Add CredentialName to AuthKitSecuritySchemeDescriptor; the OpenAPI mapper and every location extractor fall back to location-specific defaults (X-Api-Key, api_key). Also addressed by the same review: - MaxBodySize is a hard read limit for Body extraction, independent of the BufferThreshold spill threshold. - StripBearerPrefix is opt-in instead of unconditional. - Custom authentication wording in ADR-018 is a warning, not a mapping rule. - Bump Swashbuckle to 10.2.3 / Microsoft.OpenApi 2.7.x so the mapper can use OpenApi3_1 and SecuritySchemeType.MutualTLS; fix namespaces moved by the development merge.
… credentials Replace ambient GetService<AuthKitSecuritySchemeDescriptor>() with request-aware resolution driven by endpoint metadata and a host-level scheme registry built from the security schemes contributed by loaded plugins. - Add SecuritySchemeAttribute endpoint metadata (Abstractions contract). - Add ISecuritySchemeRegistry/SecuritySchemeRegistry; duplicate scheme names across plugins are a startup configuration error. - Middleware now resolves the endpoint's scheme, rejects unknown schemes, and fails closed with 401 Unauthorized when a credential is present but invalid or cannot be extracted. Requests without a credential still continue so downstream middleware can decide how to handle them. - Register ApiKeyCredentialExtractor in the pipeline and wire AddApiKeyCredentialExtraction into host startup. Closes review feedback requesting request-aware scheme resolution and a no-credential vs. invalid-credential distinction.
Docker base image mcr.microsoft.com/dotnet/sdk:10.0 resolves a newer 10.0 feature band (10.0.400) than the pinned global.json (10.0.110). latestPatch does not cross feature bands, so dotnet build in the container failed with 'Requested SDK version 10.0.110'. Use latestFeature, which stays within major.minor 10.0 but accepts any installed 10.0 feature band.
…from user input' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
…ath.Combine' may silently drop its earlier arguments' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR extends the AuthKit plugin contract and host with an explicit security scheme model: additive security scheme and API key location enums, host side API key credential extraction, plugin contract validation, and explicit OpenAPI mapping. Every contract value is handled explicitly by the host and by the OpenAPI mapper.
Security Scheme Contract
AuthKitSecuritySchemeTypewithMutualTls,Session,Custom, andBasic, and makesOpenIdConnecta distinct value (the historicalOAuth2alias is removed)AuthKitApiKeyLocationwithGrpcMetadataandBodyDevTokens, compile and load unmodifiedAPI Key Extraction
API key credential normalizationthroughApiKeyValueNormalizerand validator/principal types (IApiKeyValidator,ApiKeyPrincipal)ApiKeyCredentialExtractormiddleware withApiKeyCredentialExtractorOptionsandSecurityServiceCollectionExtensionsregistrationPlugin Validation
PluginContractValidatorwith supported scheme types and API key locations, validating every declared scheme and location against themInvalidPluginContractException; unknown values are identified by numeric identity and never resolved toCustomor any known valueCustomrequires plugin suppliedDescription; missing description produces a validation warningPluginContractValidator.CreateCustomOpenAPI Mapping
AuthKitOpenApiSecuritySchemeMapperthat maps every value explicitly: semantically correct OpenAPI representations forApiKey,Http,OAuth2,OpenIdConnect, andBasicNotSupportedExceptionfor values with no correct 3.0 representation (MutualTls,Session,Custom;GrpcMetadataandBodyas API key locations) andArgumentOutOfRangeExceptionfor unknown valuesRestfulConfigurationcatches mapper failures and logs a warning while skipping the definition it never emits a generic substitute and never crashes document generationOpenApi:SpecVersion3.0;3.1is rejected withInvalidOperationExceptioninstead of silently emitting a 3.0 documentHost
AuthenticateApiKeycounterparts even though OpenAPI 3.0 cannot describe some of themTests
OAuth2andOpenID Connectenum aliasing regressionDocumentation
Result
Two explicit contract guarantees now exist: no silent fallback between schemes or locations, and unknown future values are always rejected. Plugins that declare unsupported mechanisms fail contract validation at startup; per-host extensions use
PluginContractValidator.CreateCustom. Old plugins compile and load unmodified, and persisted descriptors keep their meaning.Closes #4
Closes #5
Closes #6
Closes #7