Portal Community

Credential Pattern

Every MCP server definition carries a credentialId integer. At call time, IMCPClientFactory calls ICredentialResolver.GetPasswordAsync(credentialId) to retrieve the secret and injects it into the transport before the tool call is made. Workflow designers configure a credentialId — never a token or password.

Supported Authentication Modes

ModeTransportCredential FormatHeader / Mechanism
API KeyHTTP / SSEPlain stringAuthorization: Bearer <key> or X-Api-Key: <key>
OAuth 2.0 Client CredentialsHTTP / SSEJSON: {"clientId":"...","clientSecret":"...","tokenUrl":"..."}Token fetched and cached; Authorization: Bearer <token>
mTLSHTTP / SSEJSON: {"cert":"---PEM---","key":"---PEM---"}Client certificate presented on TLS handshake
NoneStdIoNone (credentialId: 0)Process-level isolation; no network auth needed

IMCPClientFactory — Credential Injection

public class MCPClientFactory : IMCPClientFactory
{
    private readonly ICredentialResolver _credentials;

    public MCPClientFactory(ICredentialResolver credentials)
    {
        _credentials = credentials;
    }

    public async Task<IMCPClient> CreateClientAsync(
        IMCPServerDefinition server,
        CancellationToken ct)
    {
        if (server.TransportType == MCPTransportType.StdIo)
            return new StdIoMCPClient(server);

        // Resolve secret — never stored in server definition
        string secret = await _credentials.GetPasswordAsync(server.CredentialId, ct);

        var authMode = DetectAuthMode(secret);
        return authMode switch
        {
            AuthMode.OAuthClientCredentials => await BuildOAuthClientAsync(server, secret, ct),
            AuthMode.mTLS                   => BuildMtlsClient(server, secret),
            _                               => new HttpMCPClient(server, bearerToken: secret)
        };
    }

    private static AuthMode DetectAuthMode(string secret)
    {
        if (!secret.TrimStart().StartsWith("{")) return AuthMode.ApiKey;
        using var doc = JsonDocument.Parse(secret);
        if (doc.RootElement.TryGetProperty("clientSecret", out _)) return AuthMode.OAuthClientCredentials;
        if (doc.RootElement.TryGetProperty("cert", out _)) return AuthMode.mTLS;
        return AuthMode.ApiKey;
    }
}

Registering Credentials

// API Key:
POST /api/credentials
{
  "name": "bfai-document-ai-key",
  "type": "ApiKey",
  "value": "sk-bfai-doc-xxxxxxxxxxxxxxxx"
}
// → returns credentialId: 201

// OAuth 2.0:
POST /api/credentials
{
  "name": "bfai-risk-ai-oauth",
  "type": "ApiKey",
  "value": "{\"clientId\":\"risk-client\",\"clientSecret\":\"...\",\"tokenUrl\":\"https://auth.bfai.internal/token\"}"
}
// → returns credentialId: 202

Credential Rotation

To rotate a credential, update the secret value via PUT /api/credentials/{id}. No workflow changes are needed — all server definitions reference the credential by ID. The next tool call after rotation automatically uses the new secret.

OAuth token caching: OAuth access tokens are cached in-process for their declared expires_in duration minus a 60-second safety margin. If a token is rotated on the identity provider before it expires, use POST /api/mcp/servers/{serverId}/flush-token to invalidate the in-process cache immediately.