Skip to content

KurrentDB Client NodeJS - readStream and readAll bypass createCredentialsMetadataGenerator - no Bearer token support for Rust bridge reads #470

@kochai

Description

@kochai

🐛 Current behavior

Starting in v1.0.0, readStream and readAll were moved to the Rust native bridge (@kurrent/bridge) for performance. However, the bridge's credential interface (RustReadStreamOptions.credentials / RustReadAllOptions.credentials) only accepts { username: string; password: string }:

// @kurrent/bridge v0.1.3 — lib/index.d.ts
export type RustReadStreamOptions = {
  // ...
  credentials?: {
    username: string;
    password: string;
  };
};

All other operations (append, delete, subscribe, persistent subscriptions) go through the gRPC path via callArguments(), which invokes createCredentialsMetadataGenerator. This allows consumers to override the credential generator for custom auth schemes (e.g., Bearer tokens via Azure RBAC / DefaultAzureCredential).

Read operations bypass this entirely, because they go through the Rust bridge which has no concept of Bearer tokens or custom metadata generators.

Any consumer using token-based auth (OAuth2 Bearer, Azure RBAC, custom metadata generators) cannot use readStream or readAll without workarounds. This affects:

  • Azure-hosted KurrentDB deployments using Managed Identity / DefaultAzureCredential
  • Any integration using createCredentialsMetadataGenerator override for custom auth

🔍 Steps to reproduce

import { KurrentDBClient } from '@kurrent/kurrentdb-client';
import { Metadata } from '@grpc/grpc-js';
import { DefaultAzureCredential } from '@azure/identity';

const client = KurrentDBClient.connectionString`kurrentdb+discover://host:2113?tls=true`;

const credential = new DefaultAzureCredential();

// Override credential generator for Bearer token auth
(client as any).createCredentialsMetadataGenerator = () => (_, cb) => {
  credential.getToken('api://<app-id>/.default').then(({ token }) => {
    const metadata = new Metadata();
    metadata.add('authorization', `Bearer ${token}`);
    cb(null, metadata);
  });
};

// ✅ This works — goes through gRPC callArguments()
await client.appendToStream('my-stream', [event]);

// ❌ This FAILS with auth error — goes through Rust bridge, ignores Bearer token
for await (const event of client.readStream('my-stream')) {
  console.log(event);
}

Reproducible link

https://gist.github.com/kochai/a9d13566e2c03d1452023bbe65e6718f

💭 Expected behavior

One of the following (in order of preference):

  1. Add Bearer/token credential support to @kurrent/bridge — extend RustReadStreamOptions.credentials and RustReadAllOptions.credentials to accept a token or bearer field, and pass it as an Authorization: Bearer <token> header in the Rust gRPC calls.

  2. Route reads through callArguments() when custom credentials are detected — if createCredentialsMetadataGenerator has been overridden on the client instance, fall back to the gRPC path for reads instead of the Rust bridge, so the custom metadata generator is invoked.

  3. Expose a public API for custom credential providers — e.g., a credentialProvider option on the client that is respected by both the gRPC and Rust bridge paths.

Package version

@kurrent/kurrentdb-client: 1.1.0, @kurrent/bridge: 0.1.3

KurrentDB Version

KurrentDB 25.1.4

Connection string

kurrentdb+discover://:2113?tls=true

☁️ Deployment Environment

Managed KurrentDB Cloud

Other Deployment Details

3-node StatefulSet on AKS, managed via KurrentDB Operator

Operating system

Linux (RHEL 8 for server, Debian container for client), Node.js 22.x

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions