Skip to main content
Version: v1.1.4 (latest)

Authentication in Custom Connectors

This guide covers authentication implementation for custom connectors. For general authentication concepts, see Authentication.

Declaring Authentication in Schema​

Declare what authentication your connector supports in the schema:

Simple API Key​

new ChannelSchemaBuilder("MyProvider", "REST", "1.0.0")
.AddAuthenticationScheme(AuthenticationScheme.ApiKey)

This automatically accepts parameters like ApiKey, Key, or AccessKey.

Explicit Configuration​

For precise control over field names:

new ChannelSchemaBuilder("MyProvider", "REST", "1.0.0")
.AddAuthenticationConfiguration(
new AuthenticationConfiguration(AuthenticationScheme.ApiKey, "API Key")
.WithField("ApiKey", DataType.String, f =>
{
f.AuthenticationRole = "principal";
f.IsSensitive = true;
f.Description = "API key for authentication";
}))

Multiple Authentication Options​

Support multiple auth methods:

new ChannelSchemaBuilder("MyProvider", "REST", "1.0.0")
.AddAuthenticationConfiguration(
new AuthenticationConfiguration(AuthenticationScheme.ApiKey, "API Key")
.WithField("ApiKey", DataType.String, f =>
{
f.AuthenticationRole = "principal";
f.IsSensitive = true;
}))
.AddAuthenticationConfiguration(
new AuthenticationConfiguration(AuthenticationScheme.Basic, "Basic Auth")
.WithField("Username", DataType.String, f => f.AuthenticationRole = "principal")
.WithField("Password", DataType.String, f =>
{
f.AuthenticationRole = "credential";
f.IsSensitive = true;
}))

The framework selects the first configuration that matches the provided connection settings.

Using Authentication in Your Connector​

Authentication happens automatically during InitializeAsync(). Access credentials via the base class:

Access Credential Value​

protected override ValueTask InitializeConnectorAsync(CancellationToken ct)
{
// AuthenticationCredential is populated by base class
var token = AuthenticationCredential?.Value;
var scheme = AuthenticationCredential?.Scheme;

if (token != null)
{
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}");
}

SetState(ConnectorState.Ready);
return ValueTask.CompletedTask;
}

Use Helper Methods​

Base class provides helpers for common auth patterns:

protected override ValueTask InitializeConnectorAsync(CancellationToken ct)
{
// Get Authorization header value
var authHeader = GetAuthenticationHeader();
// Returns: "Bearer token" | "Basic base64" | null

// Get raw API key
var apiKey = GetApiKey();
// Returns: raw API key value | null

if (authHeader != null)
{
_httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);
}
else if (apiKey != null)
{
_httpClient.DefaultRequestHeaders.Add("X-API-Key", apiKey);
}

SetState(ConnectorState.Ready);
return ValueTask.CompletedTask;
}

Manual Authentication​

If auto-authentication fails or you need manual control:

protected override async ValueTask InitializeConnectorAsync(CancellationToken ct)
{
try
{
// Attempt manual authentication
var result = await AuthenticateAsync(ct);

if (!result.IsSuccess())
{
throw new ConnectorException(
"AUTH_FAILED",
"Unable to authenticate: " + result.Error?.Message);
}

// Credential is now available
var credential = AuthenticationCredential;

SetState(ConnectorState.Ready);
}
catch (Exception ex)
{
SetState(ConnectorState.Error);
throw;
}
}

Refresh Credentials​

Refresh expired credentials:

public async Task EnsureValidCredentialsAsync(CancellationToken ct)
{
if (AuthenticationCredential?.IsExpired == true)
{
var refreshResult = await RefreshAuthenticationAsync(ct);

if (!refreshResult.IsSuccess())
{
throw new ConnectorException(
"REFRESH_FAILED",
"Failed to refresh credentials");
}
}
}

Custom Authentication Providers​

For non-standard authentication, implement IAuthenticationProvider:

Create Custom Provider​

public class HmacSignatureProvider : AuthenticationProviderBase
{
public HmacSignatureProvider()
: base(AuthenticationScheme.Of("hmac-sha256"), "HMAC-SHA256 Signature")
{
}

public override Task<AuthenticationResult> ObtainCredentialAsync(
ConnectionSettings settings,
AuthenticationConfiguration configuration,
CancellationToken ct)
{
var apiKey = GetStringParameter(settings, "ApiKey");
var secretKey = GetStringParameter(settings, "SecretKey");

if (string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(secretKey))
return Task.FromResult(
AuthenticationResult.Failure("ApiKey and SecretKey required", "MISSING_CREDENTIALS"));

// Create credential with both values
var credential = new AuthenticationCredential(
AuthenticationScheme.Of("hmac-sha256"),
$"{apiKey}:{secretKey}");

credential.Properties["ApiKey"] = apiKey;
credential.Properties["SecretKey"] = secretKey;

return Task.FromResult(AuthenticationResult.Success(credential));
}
}

Register Provider​

var authManager = serviceProvider.GetRequiredService<IAuthenticationManager>();
authManager.RegisterProvider(new HmacSignatureProvider());

Declare in Schema​

new ChannelSchemaBuilder("MyProvider", "REST", "1.0.0")
.AddAuthenticationConfiguration(
new AuthenticationConfiguration(
AuthenticationScheme.Of("hmac-sha256"), "HMAC Signature")
.WithField("ApiKey", DataType.String, f =>
{
f.AuthenticationRole = "principal";
f.IsSensitive = true;
})
.WithField("SecretKey", DataType.String, f =>
{
f.AuthenticationRole = "credential";
f.IsSensitive = true;
}))

OAuth 2.0 Client Credentials​

For OAuth 2.0 flows, use the built-in provider:

Schema Configuration​

new ChannelSchemaBuilder("MyProvider", "REST", "1.0.0")
.AddAuthenticationConfiguration(
new AuthenticationConfiguration(
AuthenticationScheme.OAuthClientCredentials, "OAuth 2.0")
.WithField("ClientId", DataType.String, f => f.AuthenticationRole = "principal")
.WithField("ClientSecret", DataType.String, f =>
{
f.AuthenticationRole = "credential";
f.IsSensitive = true;
})
.WithField("TokenEndpoint", DataType.String, _ => { })
.WithField("Scope", DataType.String, _ => { }))

Connection Settings​

{
"MyProvider": {
"ClientId": "client-123",
"ClientSecret": "secret-456",
"TokenEndpoint": "https://provider.com/oauth/token",
"Scope": "read write"
}
}

The framework automatically:

  • Requests access tokens
  • Caches credentials
  • Refreshes before expiration

Authentication Field Roles​

Use roles to clarify field purposes:

RoleMeaningExample Fields
"principal"Primary identifierApiKey, Token, Username, AccountSid
"credential"Corresponding secretPassword, AuthToken, ClientSecret
(other)AuxiliaryProjectId, CertificatePassword

Example with Roles​

new AuthenticationConfiguration(AuthenticationScheme.Basic, "Basic Auth")
.WithField("AccountSid", DataType.String, f =>
{
f.AuthenticationRole = "principal";
f.Description = "Account SID";
})
.WithField("AuthToken", DataType.String, f =>
{
f.AuthenticationRole = "credential";
f.IsSensitive = true;
f.Description = "Authentication token";
})

Best Practices​

✅ DO: Mark Sensitive Fields​

.WithField("ApiKey", DataType.String, f =>
{
f.IsSensitive = true; // Value redacted in logs
})

✅ DO: Provide Clear Descriptions​

.WithField("TokenEndpoint", DataType.String, f =>
{
f.Description = "OAuth 2.0 token endpoint URL";
})

✅ DO: Use Standard Field Names​

Use common names for automatic matching:

  • ApiKey, Key, AccessKey for API key auth
  • Token, AccessToken, BearerToken for bearer auth
  • Username/Password or AccountSid/AuthToken for basic auth

❌ DON'T: Hardcode Credentials​

// ❌ Bad - hardcoded secret
.WithField("ApiKey", DataType.String)
.DefaultValue = "hardcoded-secret";

// ✅ Good - require configuration
.WithField("ApiKey", DataType.String, f =>
{
f.IsRequired = true;
})

Troubleshooting​

Authentication Failed​

Error: AUTHENTICATION_FAILED

Causes:

  • Missing required credentials
  • Invalid credentials
  • Expired credentials

Solution:

  1. Verify credentials in connection settings
  2. Check credential validity in provider console
  3. Enable credential refresh if applicable

No Authentication Configuration Found​

Error: No suitable authentication configuration found

Cause: Connection settings don't match any auth configuration

Solution:

  • Verify parameter names match schema field names
  • Ensure all required fields are provided
  • Check for typos in configuration

See Also​