{"id":20026282,"url":"https://github.com/nventive/requestssignature","last_synced_at":"2025-05-05T02:31:16.576Z","repository":{"id":65414003,"uuid":"198294856","full_name":"nventive/RequestsSignature","owner":"nventive","description":"Signs and validates HTTP requests.","archived":false,"fork":false,"pushed_at":"2025-03-29T05:43:23.000Z","size":122,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-08T15:49:33.137Z","etag":null,"topics":["aspnet-core","dotnet","dotnet-standard2","hmac","hmac-authentication","httpclient","postman"],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nventive.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-22T20:09:11.000Z","updated_at":"2024-05-29T16:06:18.000Z","dependencies_parsed_at":"2024-06-19T17:13:06.161Z","dependency_job_id":"4f06cd53-9cff-41b8-bb29-3387841af3b4","html_url":"https://github.com/nventive/RequestsSignature","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FRequestsSignature","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FRequestsSignature/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FRequestsSignature/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nventive%2FRequestsSignature/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nventive","download_url":"https://codeload.github.com/nventive/RequestsSignature/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252427818,"owners_count":21746280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","dotnet","dotnet-standard2","hmac","hmac-authentication","httpclient","postman"],"created_at":"2024-11-13T09:06:05.060Z","updated_at":"2025-05-05T02:31:15.622Z","avatar_url":"https://github.com/nventive.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RequestsSignature\n\nSigns and validates HTTP requests.\n\nThis projects can help you implements [HMAC](https://en.wikipedia.org/wiki/HMAC) signature to HTTP requests in .NET.\n\nIt consists of .NET Standard 2.0 assemblies to help implement:\n- HMAC Signature Validation in a ASP.NET Core project (server-side)\n- a HTTP Client Delegating Handler that signs requests (client-side)\n\nAdditionally, it provides a [Postman](https://www.getpostman.com/) Pre-request script to help testing APIs with signature validation.\n\nFor front-end projects in Flutter, use the [RequestsSignature-Dart](https://github.com/nventive/RequestsSignature-Dart) library to implement [HMAC](https://en.wikipedia.org/wiki/HMAC) signatures.\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Build Status](https://dev.azure.com/nventive-public/nventive/_apis/build/status/nventive.RequestsSignature?branchName=master)](https://dev.azure.com/nventive-public/nventive/_build/latest?definitionId=5\u0026branchName=master)\n![Nuget](https://img.shields.io/nuget/v/RequestsSignature.Core.svg)\n\n## Getting Started\n\n### Implementing Requests Signature Validation in ASP.NET Core:\n\nInstall the package:\n\n```\nInstall-Package RequestsSignature.AspNetCore\n```\n\nThen in the `Startup` class adds the configuration and the services:\n\n```csharp\n\nusing RequestsSignature.AspNetCore;\n\npublic class Startup\n{\n    // This method gets called by the runtime. Use this method to add services to the container.\n    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...\n\n        // Configure the RequestTracingMiddlewareOptions. This is one way of doing it.\n        services.Configure\u003cRequestsSignatureOptions\u003e(options =\u003e\n        {\n            // List of client ids and secrets that are accepted and validated.\n            options.Clients = new[]\n            {\n                new RequestsSignatureClientOptions\n                {\n                    ClientId = \"9e616f36fde8424e9f71afa4a31e128a\",\n                    ClientSecret = \"df46ca91155142e99617a5fc5dea1f50\",\n                },\n            };\n        });\n        \n        // Alternatively, options can be loaded from a configuration section.\n        services.Configure\u003cRequestsSignatureOptions\u003e(\n          _configuration.GetSection(nameof(RequestsSignatureOptions)));\n\n        // Adds the requests signature validation services.\n        services.AddRequestsSignatureValidation();\n    }\n}\n\n```\n\nThen you need to decide whether you want to implement it as a *Middleware* or as part\nof *ASP.NET Core Authentication*\n\n### Implement as a middleware\n\nImplementing as a middleware means that you have the most control over the validation process.\nA middleware intercepts the request and validates the signature, without taking\nany other decision. It is then up to you to handle the result.\n\nTo implement as a middleware, us the `Configure` method in the `Startup` class:\n\n```csharp\n\nusing RequestsSignature.AspNetCore;\n\npublic class Startup\n{\n    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.\n    public void Configure(IApplicationBuilder app, IHostingEnvironment env)\n    {\n        // Make sure this is sufficiently early in the request pipeline.\n        app.UseRequestsSignatureValidation();\n        // ...\n        app.UseMvc(); // Or app.UseEndpoints(endpoints =\u003e ...) for ASP.NET Core 3.0+\n    }\n}\n\n```\n\nThis alone only adds the middleware, performs the validation for each incoming request,\nand store the result in the `IRequestsSignatureFeature` HTTP Feature.\n\nResults for the validation are accessible through the `HttpContext.GetSignatureValidationResult()` extension method:\n\n```csharp\nHttpContext context;\nvar validationResults = context.GetSignatureValidationResult();\n\n// This is a shortcut for this:\nvar validationResults = context.Features.Get\u003cIRequestsSignatureFeature\u003e().ValidationResult;\n\n```\n\n#### Integration with ASP.NET Core MVC\n\n2 additional MVC Filters are provided for convenience:\n\n- `RequireRequestsSignatureValidationAttribute`: it is an [Action Filter](https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-6.0#action-filters) that can force the validation to be performed:\n\n```csharp\n[HttpGet(\"my-action\")]\n[RequireRequestsSignatureValidation]\npublic IActionResult MyAction()\n{\n    ...\n}\n```\n\nThis will throw a `RequestsSignatureValidationException` if the requests is not successfully validated. Additional option allow you to specify the list of client ids to accept.\n\n- `IgnoreRequestsSignatureValidationAttribute`: disable the action of the previous filter.\nThis is useful when you implement the filter globally:\n\n```csharp\n\n// In Startup.cs, adds the filter globally:\nservices.AddMvc(options =\u003e\n{\n    // Makes signature validation mandatory for all actions\n    options.Filters.Add(new RequireRequestsSignatureValidationAttribute());\n});\n\n// Then on specific controller action disable it:\n\n[HttpGet(\"my-action\")]\n[IgnoreRequestsSignatureValidation]\npublic IActionResult MyAction()\n{\n    // Signature validation is disabled here. \n    ...\n}\n```\n\nThis works in the same way as the `[AllowAnonymous]` filter for the authentication system.\n\n\n### Implement as part of ASP.NET Core Authentication\n\nAlternatively, you can implement requests signature validation as an ASP.NET Core Authentication scheme. This allows you to consider requests signature as a means to authenticate requests, the same way that [`AddJwtBearer`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.jwtbearerextensions.addjwtbearer?view=aspnetcore-6.0) authenticates requests using JSON Web Tokens.\n\nTo do so, configure the authentication in the `Startup` class:\n\n```csharp\n\nusing RequestsSignature.AspNetCore;\nusing RequestsSignature.AspNetCore.Authentication;\n\npublic class Startup\n{\n    // This method gets called by the runtime. Use this method to add services to the container.\n    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...\n\n        // Configure the RequestTracingMiddlewareOptions. This is one way of doing it.\n        services.Configure\u003cRequestsSignatureOptions\u003e(options =\u003e\n        {\n            // List of client ids and secrets that are accepted and validated.\n            options.Clients = new[]\n            {\n                new RequestsSignatureClientOptions\n                {\n                    ClientId = \"9e616f36fde8424e9f71afa4a31e128a\",\n                    ClientSecret = \"df46ca91155142e99617a5fc5dea1f50\",\n                },\n            };\n        });\n        \n        // Alternatively, options can be loaded from a configuration section.\n        services.Configure\u003cRequestsSignatureOptions\u003e(\n          _configuration.GetSection(nameof(RequestsSignatureOptions)));\n\n        // Adds the requests signature validation services.\n        services.AddRequestsSignatureValidation();\n\n\n        // Configure Authentication with Requests Signature as the Default Scheme.\n        services\n            .AddAuthentication(RequestsSignatureAuthenticationConstants.AuthenticationScheme)\n            .AddRequestsSignature();\n    }\n}\n\n```\n\n### Implementing Requests Signature in HTTP Client\n\nTo implement the creation of signed requests client-side (using `HttpClient`):\n\nInstall the package:\n\n```\nInstall-Package RequestsSignature.HttpClient\n```\n\nConfigure the `RequestsSignatureDelegatingHandler` with the `HttpClient` instance:\n\n```csharp\n// Create the client with the RequestsSignatureDelegatingHandler\nvar client = new System.Net.Http.HttpClient(\n    new RequestsSignature.HttpClient.RequestsSignatureDelegatingHandler(\n        new RequestsSignatureOptions\n        {\n            // These options must be the same as the server-side client options.\n            ClientId = \"9e616f36fde8424e9f71afa4a31e128a\",\n            ClientSecret = \"df46ca91155142e99617a5fc5dea1f50\",\n        })\n    {\n        InnerHandler = new HttpClientHandler(),\n    });\n\n// Use the client normally\nvar response = await client.GetAsync(\"...\");\n\n// Or register it with the IHttpClientFactory\nservices\n    .AddHttpClient\u003cIService\u003e()\n    .AddRequestsSignature(\n        new RequestsSignatureOptions\n        {\n            ClientId = StartupWithMiddleware.DefaultClientId,\n            ClientSecret = StartupWithMiddleware.DefaultClientSecret,\n        });\n```\n\n### Testing with [Postman](https://www.getpostman.com/)\n\nThe repository includes a [Postman Pre-request script](https://learning.getpostman.com/docs/postman/scripts/pre_request_scripts/) that can be used\nto sign the requests when using [Postman](https://www.getpostman.com/).\n\nSimply copy the content of the [`Postman.Pre-request Script.js`](Postman.Pre-request%20Script.js) file and configure the following [variables](https://learning.getpostman.com/docs/postman/environments_and_globals/variables/):\n\n- `signatureClientId`\n- `signatureClientSecret`\n\nThe outgoing requests will then be properly signed.\n\n## Features\n\n### Default Header signature and algorithm\n\nBy default, here is how the header is constructed:\n\nThe final header has the following specification: `{ClientId}:{Nonce}:{Timestamp}:{SignatureBody}` where:\n- `{ClientId}`: is the client id as specified by the configuration\n- `{Nonce}`: is a random value unique to each request (a UUID/GUID is perfectly suitable)\n- `{Timestamp}`: is the current time when the request is sent, in Unix Epoch time (in seconds)\n- `{SignatureBody}`: Is the Base-64 encoded value of the HMAC SHA256 Signature of the signature components\n\nSignature components (the source for the SignatureBody HMAC value) is a binary value composed of the following values sequentially:\n- Nonce: UTF-8 encoded binary values of the Nonce\n- Timestamp: UTF-8 encoded binary values of the Timestamp (as a string value)\n- Request method: UTF-8 encoded binary values of the **uppercase** Request method\n- Request scheme: UTF-8 encoded binary values of the Request Uri scheme (e.g. `https`)\n- Request host: UTF-8 encoded binary values of the Request Uri host (e.g. `example.org`)\n- Request local path: UTF-8 encoded binary values of the Request Uri local path (e.g. `/api/v1/users`)\n- Request query string: UTF-8 encoded binary values of the Request Query string, including the leading `?` (e.g. `?q=search`)\n- Request body: Raw bytes of the request body\n\n*For more information see the [`SignatureBodySourceBuilder`](RequestsSignature.Core/SignatureBodySourceBuilder.cs) class or the [`Postman.Pre-request Script.js`](Postman.Pre-request%20Script.js) file.*\n\n*See the Configuration section on how to customize the signature.*\n\n### Nonce repository\n\nBy default, nonce are not stored and checked, which means that you are vulnerable to\nreplay attacks for the duration of the clock skew.\n\nTo enable nonce check, you must configure a `INonceRepository` that is responsible\nfor storing and checking the nonce at least for the duration of twice the clock skew.\n\nTwo implementations are provided: one that relies on the [`IMemoryCache`](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-6.0), and another that relies on the [`IDistributedCache`](https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-6.0). Consider using one of these\n(or your own implementation) to enable nonce management.\n\n```csharp\nusing RequestsSignature.AspNetCore;\nusing RequestsSignature.AspNetCore.Nonces;\n\npublic class Startup\n{\n    // This method gets called by the runtime. Use this method to add services to the container.\n    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...\n\n        // Enable nonce repository using the Memory Cache\n        services\n            .AddMemoryCache()\n            .AddSingleton\u003cINonceRepository, MemoryCacheNonceRepository\u003e()\n            .AddRequestsSignatureValidation();\n\n        // Or using the distributed cache\n        services\n            .AddDistributedMemoryCache()\n            .AddSingleton\u003cINonceRepository, DistributedCacheNonceRepository\u003e()\n            .AddRequestsSignatureValidation();\n    }\n}\n```\n\n### Auto retry on clock skew detection (client)\n\nThe `RequestsSignatureDelegatingHandler` has a specific features that tries to detect\nwhen a client's clock is not properly synchronized with the server and compensate\nfor the delta. This is useful when dealing with clients that are not under your control.\n\nThe way this work is when the client receives either a 401 or 403 status code and the \nresponse includes a Date header, it compares the date received from the server and the\nclient current time. If the difference is more than the configured `ClockSkew`, it\ncomputes the delta, adjust the time based on the computation and automatically re-tries\nthe request. All subsequent invocation will also apply the same time delta, until another \npotential clock skew is detected.\n\nThis behavior can be de-activated using the `DisableAutoRetryOnClockSkew` client option.\n\n### Configuration options\n\n**It is important to properly synchronize the settings between the server and all the clients, otherwise the signature will be improperly computed and compared.**\n\n#### Server-side\n\nThe following parameters can be configured client-side:\n\n- `ClockSkew`: The duration of time that a timestamp will still be considered valid when\n  comparing with the current time (+/-). Defaults to 5 minutes.\n- `HeaderName`: The name of the header that contains the signature. Defaults to `X-RequestSignature`.\n- `SignaturePattern`: The pattern that is used to create the final header value.\n  Defaults to `{ClientId}:{Nonce}:{Timestamp}:{SignatureBody}`.\n- `Disabled`: When set to true, disable the signature validation. Useful when running\n  tests or local development environment.\n- `SignatureBodySourceComponents`: The list of requests components that is used for signature validation. For example, to only include the Nonce, Timestamp, Host and a custom header (`X-ClientId`) for the signature body, this is how it should be configured:\n\n```csharp\nclientOptions.SignatureBodySourceComponents.Add(SignatureBodySourceComponents.Nonce);\nclientOptions.SignatureBodySourceComponents.Add(SignatureBodySourceComponents.Timestamp);\nclientOptions.SignatureBodySourceComponents.Add(SignatureBodySourceComponents.Host);\nclientOptions.SignatureBodySourceComponents.Add(SignatureBodySourceComponents.Header(\"X-ClientId\"));\n```\n\n#### Client-side\n\n- `ClockSkew`: The duration of time that a timestamp will still be considered valid when\n  comparing with the current time (+/-). Defaults to 5 minutes.\n- `HeaderName`: The name of the header that contains the signature. Defaults to `X-RequestSignature`.\n- `SignaturePattern`: The pattern that is used to create the final header value.\n  Defaults to `{ClientId}:{Nonce}:{Timestamp}:{SignatureBody}`.\n- `DisableAutoRetryOnClockSkew`: When set to true, the handler will not attempt to \n  detect clock skew and auto-retry.\n\n#### Postman script\n\nThe following variables can be used to configure the Postman Pre-request script:\n\n- `signatureClientId`: The client id\n- `signatureClientSecret`: The client secret\n- `signatureHeaderName`: The name of the header that contains the signature. Defaults to `X-RequestSignature`.\n- `signaturePattern`: The pattern that is used to create the final header value.\n  Defaults to `{ClientId}:{Nonce}:{Timestamp}:{SignatureBody}`.\n- `signatureBodySourceComponents`: The requests components used to compute the signature;\n  If customized, must be a JSON array of `SignatureBodySourceComponents` string values\n\n### Further customization\n\nIt is possible to further customize the behavior of the component by providing \ncustom implementation of the following interfaces:\n\n- `ISignatureBodySourceBuilder`: Builds the source data for the signature computation\n- `ISignatureBodySigner`: Creates the signature body value (from the signature body source)\n- `IRequestsSignatureValidationService`: Performs the signature validation\n\nAdditionally, the Hash algorithm used can be customized by constructing the \n`HashAlgorithmSignatureBodySigner` using a custom `hashAlgorithmBuilder`:\n\n```csharp\nusing System.Security.Cryptography;\nusing System.Text;\nusing RequestsSignature.AspNetCore;\nusing RequestsSignature.Core;\n\npublic class Startup\n{\n    // This method gets called by the runtime. Use this method to add services to the container.\n    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940\n    public void ConfigureServices(IServiceCollection services)\n    {\n        // ...\n        services\n            .AddSingleton\u003cISignatureBodySigner\u003e(\n                sp =\u003e new HashAlgorithmSignatureBodySigner(\n                    parameters =\u003e new HMACSHA512(Encoding.UTF8.GetBytes(parameters.ClientSecret))))\n            .AddRequestsSignatureValidation();\n    }\n}\n```\n\n### Diagnose problems\n\nThe `RequestsSignatureValidationService` provides extensive logging capabilities\nto try to diagnose signature errors.\n\nEnable the logging (with a minimum log level of `Warning`) to see diagnostic information:\n\n```json\n{\n  \"Logging\": {\n    \"LogLevel\": {\n      \"RequestsSignature\": \"Warning\"\n    }\n  }\n}\n```\n\nHere is the list of events that are logged:\n\n| Event Id | Event Name                   | Description                                               |\n|----------|------------------------------|-----------------------------------------------------------|\n| 500      | SignatureValidationSucceeded | When a signature is successfully validated                |\n| 501      | SignatureValidationIgnored   | When signature validation is disabled (via configuration) |\n| 510      | SignatureValidationFailed    | When signature validation fails                           |\n\nThe logged properties and the `SignatureValidationResult` provides a detailed\naccount as to what exactly failed the validation step, including intermediary\nsignature body source and expected signature value.\n\n## Changelog\n\nPlease consult the [CHANGELOG](CHANGELOG.md) for more information about version\nhistory.\n\n## License\n\nThis project is licensed under the Apache 2.0 license - see the\n[LICENSE](LICENSE) file for details.\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](CONTRIBUTING.md) for details on the process for\ncontributing to this project.\n\nBe mindful of our [Code of Conduct](CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Frequestssignature","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnventive%2Frequestssignature","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnventive%2Frequestssignature/lists"}