<a id="configure-dotnet-clients-oauth"></a>

# Configure .NET Clients for OAuth/OIDC on Confluent Cloud

Configure .NET Kafka and Schema Registry clients for OAuth/OIDC on Confluent Cloud.

## Prerequisites

Before you begin, ensure you have the following prerequisites:

- .NET 6+
- [Confluent.Kafka](https://www.nuget.org/packages/Confluent.Kafka/): 2.0.0 or later
- OAuth authentication is configured
- Client configuration details
  - **Client ID**: Identifier for requesting OAuth tokens
  - **Client secret**: Secret for requesting OAuth tokens
  - **Token endpoint URL**: Location of access tokens
  - **Scopes**: The permissions your application requires as defined in your identity provider. For example, `kafka:read kafka:write` for Kafka, `schema-registry` for Schema Registry.
  - **Cluster ID**: Your cluster ID based on cluster type
    - **Kafka cluster ID** (`lkc-xxxxx`): Your Kafka cluster identifier
    - **Schema Registry logical cluster ID** (`lsrc-xxxxx`): For Schema Registry operations
  - **Identity pool ID** (`pool-xxxxx`): Optional

Install the latest version with OAuth support:

```bash
dotnet add package Confluent.Kafka
```

<a id="configure-kafka-dotnet-clients-for-oauth"></a>

## Configure Kafka .NET clients

.NET Kafka clients can authenticate to Confluent Cloud clusters using the OAuth 2.0 protocol.
The .NET client uses the `Confluent.Kafka` library which is based on librdkafka.

### Configuration approach

.NET clients use a delegate-based approach for OAuth token retrieval. The client
calls your token refresh handler when authentication is needed, passing OAuth
configuration parameters for token requests.

### Key components

- OAuth configuration parameters (client ID, secret, endpoint, and so on)
- .NET delegate for token refresh
- Confluent Cloud cluster and identity pool IDs

### Token refresh handler requirements

- Must accept a string parameter (OAuth configuration)
- Must return the access token as a string
- Should handle errors and implement retry logic

### Token refresh handler

The .NET client requires a token refresh handler with the following signature:

```csharp
public delegate string OAuthTokenRefreshHandler(string oauth_config);
```

#### Complete implementation example

Here’s a complete implementation of the OAuth token refresh handler:

```csharp
using Confluent.Kafka;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

// Define a class to hold the logic
public class OauthHandler
{
    private static readonly HttpClient client = new HttpClient();

    // The actual token refresh handler implementation
    public static async Task SetOAuthToken(IClient client, string oauthCfg)
    {
        try
        {
            var config = JsonSerializer.Deserialize<Dictionary<string, string>>(oauthCfg);

            var requestData = new FormUrlEncodedContent(new[]
            {
                new KeyValuePair<string, string>("grant_type", "client_credentials"),
                new KeyValuePair<string, string>("client_id", config["sasl.oauthbearer.client.id"]),
                new KeyValuePair<string, string>("client_secret", config["sasl.oauthbearer.client.secret"]),
                new KeyValuePair<string, string>("scope", config.GetValueOrDefault("sasl.oauthbearer.scope"))
            });

            var response = await client.PostAsync(config["sasl.oauthbearer.token.endpoint.url"], requestData);
            response.EnsureSuccessStatusCode();

            var responseBody = await response.Content.ReadAsStringAsync();
            var tokenData = JsonSerializer.Deserialize<JsonElement>(responseBody);
            var accessToken = tokenData.GetProperty("access_token").GetString();
            var lifetimeMs = tokenData.GetProperty("expires_in").GetInt64() * 1000;

            // Set the token on the client
            client.OAuthBearerSetToken(accessToken, lifetimeMs, config["sasl.oauthbearer.client.id"]);
        }
        catch (Exception ex)
        {
            // Set a token failure error on the client
            client.OAuthBearerSetTokenFailure(ex.ToString());
        }
    }
}
```

#### Configuration example

All settings should be placed in a single configuration object:

```csharp
// All settings should be in a single configuration object.
var producerConfig = new ProducerConfig
{
    BootstrapServers = "your-bootstrap-server:9092",
    SecurityProtocol = SecurityProtocol.SaslSsl,

    // OAuth settings
    SaslMechanism = SaslMechanism.OAuthBearer,
    SaslOauthbearerTokenEndpointUrl = "https://your-oauth-provider.com/oauth2/token",
    SaslOauthbearerClientId = "your-client-id",
    SaslOauthbearerClientSecret = "your-client-secret",
    SaslOauthbearerScope = "kafka:read kafka:write",
    SaslOauthbearerExtensions = "logical_cluster=lkc-xxxxx,identity_pool_id=pool-yyyyy" // Confluent Cloud specific
};

// The .NET client uses an Action for the handler, not a delegate property.
var producer = new ProducerBuilder<string, string>(producerConfig)
    .SetOAuthBearerTokenRefreshHandler(OauthHandler.SetOAuthToken) // Assign the handler here
    .Build();

// For a consumer, the process is the same
var consumerConfig = new ConsumerConfig(producerConfig) // Reuse producer config
{
    GroupId = "your-consumer-group",
    AutoOffsetReset = AutoOffsetReset.Earliest,
    // Note: Consumer-specific properties override producer ones.
};

var consumer = new ConsumerBuilder<string, string>(consumerConfig)
    .SetOAuthBearerTokenRefreshHandler(OauthHandler.SetOAuthToken)
    .Build();
```

### Client assertion configuration

For client assertions (KIP-1258), configure the .NET client
to use JWT-based authentication:

```csharp
using Confluent.Kafka;

// Producer configuration with client assertions (KIP-1258)
var producerConfig = new ProducerConfig
{
    BootstrapServers = "your-bootstrap-server:9092",
    SecurityProtocol = SecurityProtocol.SaslSsl,

    // OAuth settings with client assertions
    SaslMechanism = SaslMechanism.OAuthBearer,
    SaslOauthbearerMethod = SaslOauthbearerMethod.Oidc,
    SaslOauthbearerTokenEndpointUrl = "https://your-oauth-provider.com/oauth/token",
    SaslOauthbearerClientId = "your-client-id",

    // Client assertion configuration - points to the private key used to sign the assertion
    SaslOauthbearerAssertionPrivateKeyFile = "/path/to/private_key.pem",
    SaslOauthbearerAssertionClaimIss = "your-client-id",
    SaslOauthbearerAssertionClaimAud = "https://your-oauth-provider.com/oauth/token",
    SaslOauthbearerExtensions = "logicalCluster=lkc-xxxxx,identityPoolId=pool-yyyyy"
};

producerConfig.Set("sasl.oauthbearer.grant.type", "urn:ietf:params:oauth:grant-type:jwt-bearer");

var producer = new ProducerBuilder<string, string>(producerConfig)
    .Build();

// Consumer configuration with client assertions (KIP-1258)
var consumerConfig = new ConsumerConfig
{
    BootstrapServers = "your-bootstrap-server:9092",
    SecurityProtocol = SecurityProtocol.SaslSsl,
    GroupId = "your-consumer-group",
    AutoOffsetReset = AutoOffsetReset.Earliest,

    // OAuth settings with client assertions
    SaslMechanism = SaslMechanism.OAuthBearer,
    SaslOauthbearerMethod = SaslOauthbearerMethod.Oidc,
    SaslOauthbearerTokenEndpointUrl = "https://your-oauth-provider.com/oauth/token",
    SaslOauthbearerClientId = "your-client-id",

    // Client assertion configuration
    SaslOauthbearerAssertionPrivateKeyFile = "/path/to/private_key.pem",
    SaslOauthbearerAssertionClaimIss = "your-client-id",
    SaslOauthbearerAssertionClaimAud = "https://your-oauth-provider.com/oauth/token",
    SaslOauthbearerExtensions = "logicalCluster=lkc-xxxxx,identityPoolId=pool-yyyyy"
};

consumerConfig.Set("sasl.oauthbearer.grant.type", "urn:ietf:params:oauth:grant-type:jwt-bearer");

var consumer = new ConsumerBuilder<string, string>(consumerConfig)
    .Build();
```

### Test your configuration

1. **Enable debug logging** to troubleshoot OAuth issues:
   ```csharp
   // Add to your configuration
   var debugConfig = new ProducerConfig
   {
       // ... your OAuth config
       Debug = "security,protocol,broker"
   };
   ```
2. **Test with a simple producer**:
   ```csharp
   // Test producer
   using var producer = new ProducerBuilder<string, string>(producerConfig).Build();

   var message = new Message<string, string>
   {
       Key = "test-key",
       Value = "test-message"
   };

   var deliveryResult = await producer.ProduceAsync("test-topic", message);
   Console.WriteLine($"Message delivered to {deliveryResult.Topic} [{deliveryResult.Partition}] - OAuth is working!");
   ```
3. **Check for common errors**:
   - **“SASL authentication failed”** - Check your OAuth credentials and endpoint
   - **“Invalid token”** - Verify your token refresh handler is returning a valid token
   - **“Connection timeout”** - Check your bootstrap servers and network connectivity
4. **Verify token refresh** - The client should automatically refresh tokens when they expire

## Troubleshoot .NET OAuth clients

Common issues and solutions for .NET OAuth clients:

### Authentication failures

- Verify client ID and secret are correct
- Check token endpoint URL is accessible
- Ensure logical cluster ID is valid
- Validate identity pool ID if used

### Network issues

- Confirm network connectivity to OAuth provider
- Check firewall rules allow OAuth traffic
- Verify SSL certificate validation

### Configuration issues

- Ensure all required parameters are provided
- Validate token refresh handler signature
- Check timeout values are reasonable

### Debug logging

Enable detailed client logs from the underlying librdkafka library by setting
the `Debug` property in your configuration. This is the most effective
way to troubleshoot authentication and network issues.

```csharp
var producerConfig = new ProducerConfig
{
    // ... your other settings
    Debug = "security,protocol,broker"
};
```

### Common .NET-specific issues

- **Assembly references**: Ensure all required NuGet packages are installed
- **Token refresh handler**: Verify the handler signature matches requirements
- **Async/await**: Use proper async patterns for token refresh operations
- **Memory management**: Dispose of HttpClient and other resources properly
- **Thread safety**: Ensure OAuth token provider is thread-safe for multi-threaded applications

## Related content

- [Configure Kafka Clients for OAuth 2.0 Authentication in Confluent Cloud](overview.md#oauth-client-configuration-overview)
- [Use OAuth/OIDC to Authenticate to Confluent Cloud](../overview.md#oauth-overview)
- [OAuth Configuration Reference for Confluent Cloud Clients](configuration-reference.md#oauth-configuration-reference)
- [Troubleshoot OAuth/OIDC Issues on Confluent Cloud](../troubleshooting.md#oauth-troubleshooting)
