Skip to main content

Outbound Integrations

Aggregator-backed providers reach out to a third-party API (Enode, OEM cloud, Tibber, …) using OAuth2 client-credentials. The SDK ships a typed-HttpClient + token-cache helper so every adapter doesn't rebuild this from scratch.

Quick start

// Program.cs
builder.Services.AddOutboundOAuth2Client<EnodeApiClient>(
builder.Configuration, "enode")
.ConfigureHttpClient((sp, http) =>
{
var opts = sp.GetRequiredService<IOptions<EnodeOptions>>().Value;
http.BaseAddress = new Uri(opts.ApiBaseUrl);
});

EnodeApiClient is an ordinary typed client whose constructor takes HttpClient:

public sealed class EnodeApiClient
{
private readonly HttpClient _http;
public EnodeApiClient(HttpClient http) { _http = http; }

public async Task<JsonNode?> ListChargersAsync(CancellationToken ct = default)
{
using var resp = await _http.GetAsync("/chargers", ct);
resp.EnsureSuccessStatusCode();
return await JsonNode.ParseAsync(
await resp.Content.ReadAsStreamAsync(ct), cancellationToken: ct);
}
// ... etc.
}

The handler attached by AddOutboundOAuth2Client:

  1. Adds Authorization: Bearer <token> to every outgoing request,
  2. Caches the access token in process,
  3. Refreshes within RefreshSkewSeconds of expiry,
  4. On a 401 response, forces a refresh and replays the request once (the body is cloned so POST/PUT/PATCH retry cleanly).

Configuration

{
"Outbound": {
"enode": {
"TokenUrl": "https://oauth.sandbox.enode.io/oauth2/token",
"ClientId": "<from secret store>",
"ClientSecret": "<from secret store>",
"Scope": null,
"Audience": null,
"RefreshSkewSeconds": 60
}
}
}

Use dotnet user-secrets for development; in production source secrets from your secret store. The name (enode here) is both the options-binding key and the per-call cache slot — multiple integrations under the same host coexist by name.

What you get for free

  • One IOutboundTokenProvider singleton, one cache slot per name.
  • SemaphoreSlim per name so concurrent first-time fetches don't duplicate the token call.
  • Refresh-on-401 with body cloning.
  • Standard ASP.NET resilience (timeouts, retry policies) layered via the IHttpClientBuilder returned from AddOutboundOAuth2Client.

What you don't get

  • Refresh-token / authorization-code flows — only client-credentials. File a separate request if a probe needs interactive auth.
  • mTLS-authenticated outbound calls — separate concern; layer your own handler.

See also