-
Notifications
You must be signed in to change notification settings - Fork 8
Extensibility
This page describes the extensibility points.
On this page:
- Additional target stacks for verification
- Additional target stacks for storing clients and nonces
- Overriding creation of
ClaimsPrincipalinstances - Reading signatures from a different header
- Customizing the server-side header parsing
Currently, we have support for ASP.NET Core and Owin applications.
Add a reference to the Verification NuGet package in your project:
dotnet add package Dalion.HttpMessageSigning.Verificationor
PM> Install-Package Dalion.HttpMessageSigning.VerificationWe recommend that you wrap the IRequestSignatureVerificationOrchestrator interface in your service:
/// <summary>
/// Represents a service that orchestrates tasks for verifying a request signature.
/// </summary>
public interface IRequestSignatureVerificationOrchestrator : IDisposable {
/// <summary>
/// Verify the signature of the specified request.
/// </summary>
/// <param name="request">The request and its signature to verify.</param>
/// <returns>The result of the verification, success or failure.</returns>
Task<RequestSignatureVerificationResult> VerifySignature(HttpRequestForVerification request);
}Parse your input to an instance of HttpRequestForVerification, and pass it to the IRequestSignatureVerificationOrchestrator. It will return a generic verification result. That will contain the validated principal upon success, or information about the verification failure.
As an example, see the ASP.NET Core wrapper.
See Server-side storage for more information to roll-your-own.
PR #23 by zacuke added the ability to override the default implementation of the IClaimsPrincipalFactory interface.
You can roll your own implementation. E.g.
internal class MyCustomClaimsPrincipalFactory : IClaimsPrincipalFactory {
public ClaimsPrincipal CreateForClient(Client client) {
if (client == null) throw new ArgumentNullException(nameof(client));
var additionalClaims = new[] {new Claim("my-claim", "user.read")};
return new ClaimsPrincipal(
new ClaimsIdentity(
new[] {
new Claim(SignedHttpRequestClaimTypes.AppId, client.Id),
new Claim(SignedHttpRequestClaimTypes.Name, client.Name ?? client.Id),
new Claim(SignedHttpRequestClaimTypes.Version, "1.0")
}.Concat(additionalClaims),
"my-custom-auth-scheme",
SignedHttpRequestClaimTypes.Name,
SignedHttpRequestClaimTypes.Role));
}
}And register it in your IoC container. E.g.
public static void ConfigureServices(IServiceCollection services) {
services
...
.AddHttpMessageSignatureVerification()
...
.UseClaimsPrincipalFactory(new MyCustomClaimsPrincipalFactory());
}There are several overloads to the .UseClaimsPrincipalFactory() method. Use the one that suits your needs best.
By default, the signature is read from the Authorization header. You can override this behavior, by implementing a custom IAuthenticationHeaderExtractor.
An example:
public class CustomHeaderSupportingAuthenticationHeaderExtractor : IAuthenticationHeaderExtractor {
private readonly string _headerName;
public CustomHeaderSupportingAuthenticationHeaderExtractor(string headerName = "Authorization") {
_headerName = headerName ?? throw new ArgumentNullException(nameof(headerName));
}
public AuthenticationHeaderValue Extract(HttpRequest request) {
if (request == null) throw new ArgumentNullException(nameof(request));
var authHeader = request.Headers[_headerName];
if (authHeader == StringValues.Empty) {
return null;
}
var rawAuthHeader = (string) authHeader;
var separatorIndex = rawAuthHeader.IndexOf(' ');
var authScheme = rawAuthHeader.Substring(0, separatorIndex);
var authParam = rawAuthHeader.Substring(separatorIndex + 1);
return new AuthenticationHeaderValue(authScheme, authParam);
}
}And register this class in your IoC container:
public void ConfigureServices(IServiceCollection services) {
services
// ...
.AddSingleton<CustomHeaderSupportingAuthenticationHeaderExtractor>()
.AddHttpMessageSignatureVerification()
.UseAspNetCoreSignatureVerification()
.UseAuthenticationHeaderExtractor<CustomHeaderSupportingAuthenticationHeaderExtractor>()
// ...
}If you need to override the reading of the signature from the received HTTP request, and creating a custom IAuthenticationHeaderExtractor is not enough, you can also take matters into your own hands, by implementing a custom ISignatureParser.
An example:
public class CustomSignatureParser : ISignatureParser {
public SignatureParsingResult Parse(HttpRequest request, SignedRequestAuthenticationOptions options) {
// Your custom data extraction code here...
var parsedSignature = new Signature {
KeyId = keyId,
Algorithm = string.IsNullOrEmpty(algorithm) ? null : algorithm.Trim(),
Created = created,
Expires = expires,
Headers = headerNames.Any() ? headerNames : null,
Nonce = nonce,
String = signature
};
try {
parsedSignature.Validate();
}
catch (ValidationException ex) {
return new SignatureParsingFailure(
"The specified request does not specify a valid signature in the request. See inner exception.",
ex);
}
return new SignatureParsingSuccess(parsedSignature);
}
}And register this class in your IoC container:
public void ConfigureServices(IServiceCollection services) {
services
// ...
.AddSingleton<CustomHeaderSupportingAuthenticationHeaderExtractor>()
.AddHttpMessageSignatureVerification()
.UseAspNetCoreSignatureVerification()
.UseSignatureParser<CustomSignatureParser>()
// ...
}HttpMessageSigning | David Lievrouw