Skip to content
An easy way to make authenticated HTTP requests to protected by IdentityServer4 resources
C#
Branch: master
Clone or download

README.md

An HttpClient service for IdentityServer4

Build Status Nuget

An HttpClient service that makes it easy to make authenticated HTTP requests to protected by IdentityServer4 resources. Complex types are automatically serialized for requests / deserialized for responses, all with a fluent interface design:

var responseObject = await _requestServiceFactory
	//Create a instance of the service
	.CreateHttpClientService()
	//Supports many ways of setting IdentityServer options
	.SetIdentityServerOptions("ClientCredentialsOptions")
	//GET and deserialize the response body to IEnumerable<Customers>
	.GetAsync<IEnumerable<Customers>>("https://api/customers");

Check the Getting started guide for more details!


Table of Contents
  1. Getting started
    1. It’s a nuget package!
    2. IdentityServer4 Access Token Request Options
    3. Register the service
    4. You are done!
  2. How to setup an Access Token Request
    1. .SetIdentityServerOptions(String)
    2. .SetIdentityServerOptions<TOptions>(TOptions)
    3. .SetIdentityServerOptions<TOptions>(IOptions<TOptions>)
    4. .SetIdentityServerOptions<TOptions>(Action<TOptions>)
  3. More info on how to serialize request, deserialize response
    1. ResponseObject
    2. TypeContent(TRequestBody, Encoding, string)
  4. Configuring the colleration id
  5. Contributing

Getting started

Getting started with IdentityServer4.Contrib.HttpClientService is rather easy, you only need three things:

  1. Install the nuget package IdentityServer4.Contrib.HttpClientService
  2. Provide the options to request an access token in the appsettings.json
  3. Register the service in Startup.cs

It's a nuget package!

Install the IdentityServer4.Contrib.HttpClientService nuget package, using your favorite way.

IdentityServer4 Access Token Request Options

Add the IdentityServer4 Access Token Request Options to your appsettings.json (the configuration section should always be or end with ClientCredentialsOptions):

"ClientCredentialsOptions": {
    "Address": "https://demo.identityserver.io/connect/token",
    "ClientId": "m2m",
    "ClientSecret": "secret",
    "Scopes": "api"
}
 // The values above are part of the demo offered in https://demo.identityserver.io/

You could very well skip this step if you use one of the other ways of setting up an Access Token Request.

Register the service

Register the service In StartUp.cs in ConfigureServices(IServiceCollection services):

services.AddHttpClientService();

You would also need to configure the service if you plan to use the options pattern.

You are done!

Request the IHttpClientServiceFactory wherever you want to make the authenticated requests:

using IdentityServer4.Contrib.HttpClientService.Extensions;

[ApiController]
[Route("customers")]
public class CustomerController : ControllerBase
{
	//Request the IHttpClientServiceFactory instance in your controller or service
	private readonly IHttpClientServiceFactory _requestServiceFactory;
	public CustomerController(IHttpClientServiceFactory requestServiceFactory){
		_requestServiceFactory = requestServiceFactory;
	}

	[HttpGet]
	public async Task<IActionResult> Get(){
		//Make the request
		var responseObject = await _requestServiceFactory
			//Create a instance of the service
			.CreateHttpClientService()
			//Supports many ways of setting IdentityServer options
			.SetIdentityServerOptions("ClientCredentialsOptions")
			//GET and deserialize the response body to IEnumerable<Customers>
			.GetAsync<IEnumerable<Customers>>("https://api/customers");

		//Do something with the results					  
		if (!responseObject.HasError)
		{
			var customers = responseObject.BodyAsType;
			return Ok(customers);
		}
		else
		{
			var httpStatusCode = responseObject.StatusCode;
			var errorMessage = responseObject.Error;           
			return StatusCode((int)httpStatusCode, errorMessage);
		}
	}
}	

The .SetIdentityServerOptions("SomeClientCredentialsOptions") might be the simplest way of setting up an Access Token Request, the Options Pattern though is the suggested one. Keep in mind, that if you use the .SetIdentityServerOptions("SomeClientCredentialsOptions") approach, the configuration section should either be or end with ClientCredentialsOptions. HTTP verbs supported are: GET, POST, PUT, DELETE, PATCH and HEAD.

Continue reading for more details! You can also take a look at the Documentation for technical details, check the features sample or a more complete one.


How to setup an Access Token Request

The library supports multiple ways for setting up the necessary options for retrieving an access token. Upon success of retrieving one, the result is cached until the token expires; that means that a new request to a protected resource does not necessarily means a new request for an access token.

Currently, the library only supports ClientCredentialsTokenRequest and PasswordTokenRequest.

.SetIdentityServerOptions(String)

Setup IdentityServer options by defining the configuration section where you have your settings. The type of the options (ClientCredentialsOptions or PasswordOptions) will be determined based on the name of the section:

//...
.SetIdentityServerOptions("SomeClientCredentialsOptions")
//...

Although this option is not adviced for PasswordTokenRequest, the section can contain the properties of either the ClientCredentialsOptions or PasswordOptions objects. Keep in mind, that if you use the .SetIdentityServerOptions("SomeClientCredentialsOptions") approach, the configuration section should either be or end with ClientCredentialsOptions.

.SetIdentityServerOptions<TOptions>(TOptions)

Setup IdentityServer options by passing a ClientCredentialsOptions or PasswordOptions directly to the SetIdentityServerOptions:

//...
.SetIdentityServerOptions(
  new PasswordOptions
  {
    Address = "https://demo.identityserver.io/connect/token",
    ClientId = "ClientId",
    ClientSecret = "ClientSecret",
    Scope = "Scope",
    Username = "Username",
    Password = "Password"
  }
)
//...

.SetIdentityServerOptions<TOptions>(IOptions<TOptions>)

Setup IdentityServer options using the options pattern (read more about the options pattern in Microsoft Docs):

Startup.cs

//...
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddHttpClientService()
            .AddSingleton<IProtectedResourceService, ProtectedResourceService>()
            .Configure<ClientCredentialsOptions>(Configuration.GetSection(nameof(ClientCredentialsOptions)));    
        //...
    }
//...

ProtectedResourceService.cs

//...
public class ProtectedResourceService : IProtectedResourceService
{
  private readonly IHttpClientServiceFactory _requestServiceFactory;
  private readonly IOptions<ClientCredentialsOptions> _identityServerOptions;

  public ProtectedResourceService(IHttpClientServiceFactory requestServiceFactory, IOptions<ClientCredentialsOptions> identityServerOptions)
  {
    _requestServiceFactory = requestServiceFactory;
    _identityServerOptions = identityServerOptions;
  }

  public async Task<YourObject> Get(){
    _requestServiceFactory
    .CreateHttpClientService()
    .SetIdentityServerOptions(_identityServerOptions)
    .GetAsync<YourObject>("https://url_that_returns_YourObject");
  }
)
//...

.SetIdentityServerOptions<TOptions>(Action<TOptions>)

Setup IdentityServer options using a delegate:

//...
.SetIdentityServerOptions<PasswordOptions>( x => {
    x.Address = "https://demo.identityserver.io/connect/token";
    x.ClientId = "ClientId";
    x.ClientSecret = "ClientSecret";
    x.Scope = "Scope";
    x.Username = "Username";
    x.Password = "Password";
  }
)
//...

More info on how to serialize request, deserialize response

Responses can always be deserialized to the type TResponseBody defined in, for example, GetAsync<TResponseBody>:

//...
.GetAsync<ResponsePoco>("https://url_that_returns_ResponsePoco_in_json");
//...

Using a complex type as a request body for POST, PUT and PATCH requests is also very easy. In the example that follows the type TRequestBody of the PostAsync<TRequestBody,TResponseBody> sets the type of the requestPoco object. This will be serialized using JsonConvert.SerializeObject(requestPoco, Formatting.None):

//...
.PostAsync<RequestPoco,ResponsePoco>("https://url_that_accepts_RequestPoco_and_responds_with_ResponsePoco", requestPoco);
//...

If you want to fine tune how the requestPoco object is sent, please use the TypeContent(TRequestBody, Encoding, string). Without using TypeContent(...) to explitily set media-type and encoding, the defaults will be used: 'application/json' and 'UTF-8'.

ResponseObject

The variable responseObject contains multiple properties: from the entire HttpResponseMessage and HttpRequestMessage, to the HttpStatusCode and HttpResponseHeaders. The most exciting feature though, is the TResponseBody BodyAsType property which will contain the deserializabled complex types from JSON responses. For a complete list of all the properties, check the ResponseObject<TResponseBody> in the docs.

TypeContent(TRequestBody, Encoding, string)

You can also fine tune encoding and media-type by using the TypeContent(TRequestBody model, Encoding encoding, string mediaType) like this:

var responseObject = await _requestServiceFactory
                          //Create a instance of the service
                          .CreateHttpClientService()
                          //.PostAsync<TRequestBody,TResponseBody>(URL, customer of type Customer1)
                          .PostAsync<TypeContent<Customer1>,Customer2>("https://api/customers", new TypeContent(customer, Encoding.UTF8, "application/json"));

Configuring the colleration id

Starting from version 2.3, the colleration id used to for logging between cascading API calls, can be configured from appsettings using the options pattern:

appsettings.json

"HttpClientServiceOptions": {
	//Switches on or off the sychronization of the colleration id
	"HeaderCollerationIdActive": true,
	//Sets the name of the header
	"HeaderCollerationName": "X-Request-ID"
},
 // The values above are part of the demo offered in https://demo.identityserver.io/

Startup.cs

//...
    public void ConfigureServices(IServiceCollection services)
    {
        //...
        services.AddHttpClientService()
		.Configure<HttpClientServiceOptions>(Configuration.GetSection(nameof(HttpClientServiceOptions))); 
        //...
    }
//...

Contributing

Feedback and contibution is more than welcome, as there are many more things to do!

Just as a sample:

  1. Expand the IdentityServer4.Contrib.HttpClientService.CompleteSample with more functionality.
  2. Support JsonSerializerSettings for JsonConvert.DeserializeObject<TResponseBody>(apiResponse.BodyAsString) in HttpClientService.
  3. Support more than ClientCredentialsOptions and PasswordOptions.
  4. Set options for changing the x-header name
  5. Add logging.

Many more are coming soon and all of them should be issues, so feel free to open one and let's start discussing solutions!

You can’t perform that action at this time.