Client Credentials
Service-to-service authentication with Client Credentials flow.
Client Credentials Flow
The Client Credentials flow is used for service-to-service authentication where no user is involved.
Use Cases
- Background services calling APIs
- Microservice communication
- Scheduled jobs
- Server-side integrations
Register Service Client
csharp
public async Task SeedServiceClient(){var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();if (await manager.FindByClientIdAsync("background-service") == null){await manager.CreateAsync(new OpenIddictApplicationDescriptor{ClientId = "background-service",ClientSecret = "service-secret-here",DisplayName = "Background Service",Permissions ={Permissions.Endpoints.Token,Permissions.GrantTypes.ClientCredentials,Permissions.Prefixes.Scope + "api1"}});}}
Request Access Token
http
POST /connect/tokenContent-Type: application/x-www-form-urlencodedgrant_type=client_credentials&client_id=background-service&client_secret=service-secret-here&scope=api1
Response:
json
{"access_token": "eyJhbGciOiJSUzI1NiIs...","token_type": "Bearer","expires_in": 3600}
C# Implementation
csharp
public class ServiceAuthenticator{private readonly HttpClient _httpClient;private readonly string _tokenEndpoint;private readonly string _clientId;private readonly string _clientSecret;private string? _accessToken;private DateTime _tokenExpiry;public async Task<string> GetAccessTokenAsync(){if (_accessToken != null && DateTime.UtcNow < _tokenExpiry){return _accessToken;}var request = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint){Content = new FormUrlEncodedContent(new Dictionary<string, string>{["grant_type"] = "client_credentials",["client_id"] = _clientId,["client_secret"] = _clientSecret,["scope"] = "api1"})};var response = await _httpClient.SendAsync(request);var content = await response.Content.ReadAsStringAsync();var token = JsonSerializer.Deserialize<TokenResponse>(content);_accessToken = token.AccessToken;_tokenExpiry = DateTime.UtcNow.AddSeconds(token.ExpiresIn - 60);return _accessToken;}}
Using the Token
csharp
public class ApiClient{private readonly HttpClient _httpClient;private readonly ServiceAuthenticator _authenticator;public async Task<T> GetAsync<T>(string endpoint){var token = await _authenticator.GetAccessTokenAsync();var request = new HttpRequestMessage(HttpMethod.Get, endpoint);request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);var response = await _httpClient.SendAsync(request);var content = await response.Content.ReadAsStringAsync();return JsonSerializer.Deserialize<T>(content);}}
Background Service Example
csharp
public class DataSyncService : BackgroundService{private readonly ApiClient _apiClient;private readonly ILogger<DataSyncService> _logger;protected override async Task ExecuteAsync(CancellationToken stoppingToken){while (!stoppingToken.IsCancellationRequested){try{var data = await _apiClient.GetAsync<SyncData>("/api/sync/pending");await ProcessData(data);}catch (Exception ex){_logger.LogError(ex, "Sync failed");}await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);}}}
Security Considerations
- Secure secret storage - Use Azure Key Vault or AWS Secrets Manager
- Rotate secrets regularly - Implement secret rotation
- Minimal scopes - Only request necessary permissions
- Network isolation - Restrict service communication to internal networks