{"id":18494303,"url":"https://github.com/workleap/wl-authentication-clientcredentialsgrant","last_synced_at":"2025-04-08T22:31:12.758Z","repository":{"id":154848909,"uuid":"627477793","full_name":"workleap/wl-authentication-clientcredentialsgrant","owner":"workleap","description":null,"archived":false,"fork":false,"pushed_at":"2025-03-15T02:36:48.000Z","size":299,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":30,"default_branch":"main","last_synced_at":"2025-03-15T14:03:20.781Z","etag":null,"topics":[],"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/workleap.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-13T14:47:23.000Z","updated_at":"2025-03-15T02:36:51.000Z","dependencies_parsed_at":"2023-08-11T15:08:01.541Z","dependency_job_id":"fe80838f-d23d-440b-94b9-f31bf8daf3f5","html_url":"https://github.com/workleap/wl-authentication-clientcredentialsgrant","commit_stats":null,"previous_names":["gsoft-inc/wl-authentication-clientcredentialsgrant","gsoft-inc/gsoft-authentication-clientcredentialsgrant","workleap/wl-authentication-clientcredentialsgrant"],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/workleap%2Fwl-authentication-clientcredentialsgrant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/workleap%2Fwl-authentication-clientcredentialsgrant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/workleap%2Fwl-authentication-clientcredentialsgrant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/workleap%2Fwl-authentication-clientcredentialsgrant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/workleap","download_url":"https://codeload.github.com/workleap/wl-authentication-clientcredentialsgrant/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247940064,"owners_count":21021900,"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":[],"created_at":"2024-11-06T13:19:00.910Z","updated_at":"2025-04-08T22:31:07.745Z","avatar_url":"https://github.com/workleap.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Workleap.Authentication.ClientCredentialsGrant\n\n| Description                                           | Download link                                                                                                                                                                                                      | Build status                                                                                                                                                                                                                                                  |\n|-------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Client-side library for any .NET application          | [![nuget](https://img.shields.io/nuget/v/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant.svg?logo=nuget)](https://www.nuget.org/packages/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/) | [![build](https://img.shields.io/github/actions/workflow/status/gsoft-inc/wl-authentication-clientcredentialsgrant/publish.yml?logo=github\u0026branch=main)](https://github.com/gsoft-inc/wl-authentication-clientcredentialsgrant/actions/workflows/publish.yml) |\n| Server-side library for ASP.NET Core web applications | [![nuget](https://img.shields.io/nuget/v/Workleap.AspNetCore.Authentication.ClientCredentialsGrant.svg?logo=nuget)](https://www.nuget.org/packages/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/)           | [![build](https://img.shields.io/github/actions/workflow/status/gsoft-inc/wl-authentication-clientcredentialsgrant/publish.yml?logo=github\u0026branch=main)](https://github.com/gsoft-inc/wl-authentication-clientcredentialsgrant/actions/workflows/publish.yml) |\n\nThis set of two libraries enables **authenticated machine-to-machine HTTP communication** between a .NET application and an ASP.NET Core web application.\nHTTP requests are authenticated with JSON web tokens (JWT) **issued by an OAuth 2.0 authorization server** using [the client credentials grant flow](https://www.rfc-editor.org/rfc/rfc6749#section-4.4).\n\n```\n                            ┌────────────────────────────────┐\n                 ┌─────────►│ OAuth 2.0 authorization server │◄──────────┐\n                 │          └────────────────────────────────┘           │\n                 │                                                       │\n                 │ get token with                       get signing keys │\n                 │ client credentials grant flow                         │ validate\n                 │                                                       │    token\n               ┌─┴───────────┐                           ┌───────────────┴────────┐\n               │ Client .NET ├──────────────────────────►│ Protected ASP.NET Core │\n               │ application │  authenticated HTTP call  │         service        │\n               └─────────────┘                           └────────────────────────┘\n```\n\nThe **client-side library** includes:\n\n* Automatic acquisition and lifetime management of client credentials-based access tokens.\n* Optimized access token caching with two layers of cache using [IMemoryCache](https://learn.microsoft.com/en-us/aspnet/core/performance/caching/memory), [IDistributedCache](https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed), and [data protection](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/introduction) for encryption.\n* Built-in customizable [retry policy](https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/implement-http-call-retries-exponential-backoff-polly) for production-grade resilient HTTP requests made to the OAuth 2.0 authorization server.\n* Built as an extension for the [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http/) library.\n* Support for [.NET Standard 2.0](https://learn.microsoft.com/en-us/dotnet/standard/net-standard?tabs=net-standard-2-0).\n\nThe **server-side library** includes:\n\n* JWT authentication using the [Microsoft.AspNetCore.Authentication.JwtBearer](https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.JwtBearer) library.\n* Authorization attribute and policy to easily enforce granular scopes on your endpoints.\n* Authorization attribute and policy to easily enforce classic Workleap permissions (read, write, admin).\n* Support of OpenAPI security definition and security requirement generation when using Swashbuckle.\n* Non-intrusive: default policies must be explicitly used, and the default authentication scheme can be modified.\n* Support for ASP.NET Core 6.0 and later.\n\n**Requirements and Considerations**:\n\n* Your OAuth 2.0 authorization server **must expose its metadata** at the URL `\u003cAUTHORITY\u003e/.well-known/openid-configuration`, as described in [RFC 8414](https://www.rfc-editor.org/rfc/rfc8414.html#section-3).\n* The client-side application uses [data protection](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/introduction). It is important to note that your data protection configuration **should support distributed workloads if you have multiple instances of a client application**. [Microsoft recommends using a combination of Azure Key Vault and Azure Storage](https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/configuration/overview) to ensure that data encrypted by an instance of a client application can be read by another instance.\n\n\n## Getting started\n\n### Client-side library\n\nInstall the package [Workleap.Extensions.Http.Authentication.ClientCredentialsGrant](https://www.nuget.org/packages/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/) in your client-side application\nthat needs to communicate with the protected ASP.NET Core server. Then, use one of the following methods to configure an authenticated `HttpClient`:\n\n```csharp\n// Method 1: directly set the options values with C# code\nservices.AddHttpClient(\"MyClient\").AddClientCredentialsHandler(options =\u003e\n{\n    options.Authority = \"\u003coauth2_authorization_server_base_url\u003e\";\n    options.ClientId = \"\u003coauth2_client_id\u003e\";\n    options.ClientSecret = \"\u003coauth2_client_secret\u003e\"; // use a secret store instead of hardcoding the value\n    options.Scope = \"\u003coptional_requested_scope\u003e\"; // use \"Scopes\" for multiple values\n});\n\n// Method 2: bind the options to a configuration section\nservices.AddHttpClient(\"MyClient\").AddClientCredentialsHandler(configuration.GetRequiredSection(\"MyConfigSection\").Bind);\n\n// Method 3: Lazily bind the options to a configuration section\nservices.AddHttpClient(\"MyClient\").AddClientCredentialsHandler();\nservices.AddOptions\u003cClientCredentialsOptions\u003e(\"MyClient\").Bind(configuration.GetRequiredSection(\"MyConfigSection\"));\n\n// appsettings.json:\n{\n  \"MyConfigSection\": {\n    \"Authority\": \"\u003coauth2_authorization_server_base_url\u003e\",\n    \"ClientId\": \"\u003coauth2_client_id\u003e\",\n    \"ClientSecret\": \"\u003coauth2_client_secret\u003e\", // use a secret configuration provider instead of hardcoding the value\n    \"Scope\": \"\u003coptional_requested_scope\u003e\", // use \"Scopes\" for multiple values,\n    \"EnforceHttps\": \"\u003cboolean\u003e\", // use EnforceHttps to force all authenticated to be sent via https\n  }\n}\n\n// You can also use the generic HttpClient registration with any of these methods:\nservices.AddHttpClient\u003cMyClient\u003e().AddClientCredentialsHandler( /* [...] */);\n```\n\nNote on `EnforceHttps`.\nIt is possible to allow http authenticated requests, however, this should be limited to exceptional scenarios.\nIt is strongly advised that you always use https for authenticated requests transmitted as the token sent will be in clear.\n\nThen, instantiate the `HttpClient` later on using `IHttpClientFactory` or directly inject it in the constructor if you used the generic registration:\n\n```csharp\npublic class MyClient\n{\n    private readonly HttpClient _httpClient;\n\n    public MyClient(IHttpClientFactory httpClientFactory)\n    {\n        this._httpClient = httpClientFactory.CreateClient(\"MyClient\");\n    }\n\n    public async Task DoSomeAuthenticatedHttpCallAsync()\n    {\n        await this._httpClient.GetStringAsync(\"https://myservice\");\n    }\n}\n```\n\nStarting from version 1.3.0, tokens are pre-fetched and cached at app startup.\nSubsequently, there is a periodic refresh of the token before its expiration and cache eviction.\nThis behavior can be disabled by setting `ClientCredentialsOptions.EnablePeriodicTokenBackgroundRefresh` to `false`.\n\n_This client-side library is based on [Duende.AccessTokenManagement](https://github.com/DuendeSoftware/Duende.AccessTokenManagement/tree/1.1.0), Copyright (c) Brock Allen \u0026 Dominick Baier, licensed under the Apache License, Version 2.0._\n\n\n### Server-side library\n\nThe server-side library add the `RequireClientCredentials` attribute that simplify the use of the client credentials flow in your ASP.NET Core application:\n- Simply specify the required permissions in the attribute (e.g: `[RequireClientCredentials(\"read\")`]\n- Support multiple claims types (e.g: `scope`, `scp`, `http://schemas.microsoft.com/identity/claims/scope`)\n- Support multiple claims format (e.g: `read`, `{Audience}:read`)\n\nInstall the package [Workleap.AspNetCore.Authentication.ClientCredentialsGrant](https://www.nuget.org/packages/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/) in your server-side ASP.NET Core application and register the authentication services:\n\n```csharp\n// Registers Microsoft's JwtBearer handler with a default \"ClientCredentials\" authentication scheme.\n// This authentication scheme can be changed using other methods overloads.\nbuilder.Services.AddAuthentication().AddClientCredentials();\n```\n\nThis will automatically bind the configuration section `Authentication:Schemes:ClientCredentials` (unless you've changed the authentication scheme).\nFor instance, the example above works well with this `appsettings.json`:\n\n```json\n{\n  \"Authentication\": {\n    \"Schemes\": {\n      \"ClientCredentials\": {\n        \"Authority\": \"\u003coauth2_authorization_server_base_url\u003e\",\n        \"Audience\": \"\u003caudience\u003e\",\n        \"MetadataAddress\": \"\u003coauth2_authorization_server_metadata_address\u003e\"\n      }\n    }\n  }\n}\n```\n\nNext, protect your endpoints with the `RequireClientCredentials` attribute:\n\n```csharp\n// When using Controlled-Based\n[HttpGet]\n[Route(\"weather\")]\n[RequireClientCredentials(\"read\")]\npublic async Task\u003cIActionResult\u003e GetWeather()\n{...}\n\n// When using Minimal APIs\napp.MapGet(\"/weather\", () =\u003e {...}).RequireClientCredentials(\"read\");\n```\n\nNext, register the authorization services which all the required authorization policies:\n\n```csharp\nbuilder.Services\n    .AddClientCredentialsAuthorization();\n```\n\nFinally, register the authentication and authorization middlewares in your ASP.NET Core app.\n\n```csharp\nvar app = builder.Build();\n// [...]\n\napp.UseAuthentication();\napp.UseAuthorization();\n\n// [...] Map your endpoints\n```\n\n#### OpenAPI integration\n\nIf you are using [Swashbuckle](https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger) to document your API, the `[RequireClientCredentials]` attribute will automatically populate the security definitions and requirements in the OpenAPI specification. For minimal APIs, there is a corresponding `RequireClientCredentials()` method. \n\nFor example:\n\n```csharp\n// Controlled-based approach\n[HttpGet]\n[Route(\"weather\")]\n[RequireClientCredentials(\"read\")]\npublic async Task\u003cIActionResult\u003e GetWeather()\n{ /* ... */ }\n\n// Minimal APIs\napp.MapGet(\"/weather\", () =\u003e { /* ... */ }).RequireClientCredentials(\"read\");\n```\n\nWill generate this:\n\n```yaml\npaths:\n  /weather:\n    get:\n      summary: 'Required scope: read.'\n      responses:\n        '200':\n          description: OK\n        '401':\n          description: Unauthorized\n        '403':\n          description: Forbidden\n      security:\n        - clientcredentials:\n            - target-entity:b108bbc9-538e-403b-9faf-e5cd874eb17f:read # Based on the provided JwtBearerOptions.Audience\ncomponents:\n  securitySchemes:\n    clientcredentials:\n      type: oauth2\n      flows:\n        clientCredentials:\n          tokenUrl: https://localhost:9020/oauth2/token # Based on provided ClientCredentials.Authority\n          scopes:\n            target-entity:b108bbc9-538e-403b-9faf-e5cd874eb17f: Request all permissions for specified client ID\n            target-entity:b108bbc9-538e-403b-9faf-e5cd874eb17f:read: Request permission 'read' for specified client ID\n```\n\n\n## Building, releasing and versioning\n\nThe project can be built by running `Build.ps1`. It uses [Microsoft.CodeAnalysis.PublicApiAnalyzers](https://github.com/dotnet/roslyn-analyzers/blob/main/src/PublicApiAnalyzers/PublicApiAnalyzers.Help.md) to help detect public API breaking changes. Use the built-in roslyn analyzer to ensure that public APIs are declared in `PublicAPI.Shipped.txt`, and obsolete public APIs in `PublicAPI.Unshipped.txt`.\n\nA new *preview* NuGet package is **automatically published** on any new commit on the main branch. This means that by completing a pull request, you automatically get a new NuGet package.\n\nWhen you are ready to **officially release** a stable NuGet package by following the [SemVer guidelines](https://semver.org/), simply **manually create a tag** with the format `x.y.z`. This will automatically create and publish a NuGet package for this version.\n\n## License\n\nCopyright © 2023, Workleap. This code is licensed under the Apache License, Version 2.0. You may obtain a copy of this license at https://github.com/gsoft-inc/gsoft-license/blob/master/LICENSE.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkleap%2Fwl-authentication-clientcredentialsgrant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fworkleap%2Fwl-authentication-clientcredentialsgrant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fworkleap%2Fwl-authentication-clientcredentialsgrant/lists"}