@platformatic/kafka 1.21.0 → 1.22.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ export declare const SASLMechanisms: {
3
3
  readonly SCRAM_SHA_256: "SCRAM-SHA-256";
4
4
  readonly SCRAM_SHA_512: "SCRAM-SHA-512";
5
5
  readonly OAUTHBEARER: "OAUTHBEARER";
6
+ readonly GSSAPI: "GSSAPI";
6
7
  };
7
8
  export declare const allowedSASLMechanisms: SASLMechanismValue[];
8
9
  export type SASLMechanism = keyof typeof SASLMechanisms;
@@ -3,7 +3,8 @@ export const SASLMechanisms = {
3
3
  PLAIN: 'PLAIN',
4
4
  SCRAM_SHA_256: 'SCRAM-SHA-256',
5
5
  SCRAM_SHA_512: 'SCRAM-SHA-512',
6
- OAUTHBEARER: 'OAUTHBEARER'
6
+ OAUTHBEARER: 'OAUTHBEARER',
7
+ GSSAPI: 'GSSAPI'
7
8
  };
8
9
  export const allowedSASLMechanisms = Object.values(SASLMechanisms);
9
10
  // Metadata API
@@ -124,6 +124,9 @@ export declare const baseOptionsSchema: {
124
124
  authBytesValidator: {
125
125
  function: boolean;
126
126
  };
127
+ authenticate: {
128
+ function: boolean;
129
+ };
127
130
  };
128
131
  required: string[];
129
132
  additionalProperties: boolean;
@@ -41,7 +41,8 @@ export const baseOptionsSchema = {
41
41
  username: { oneOf: [{ type: 'string' }, { function: true }] },
42
42
  password: { oneOf: [{ type: 'string' }, { function: true }] },
43
43
  token: { oneOf: [{ type: 'string' }, { function: true }] },
44
- authBytesValidator: { function: true }
44
+ authBytesValidator: { function: true },
45
+ authenticate: { function: true }
45
46
  },
46
47
  required: ['mechanism'],
47
48
  additionalProperties: false
@@ -4,18 +4,21 @@ import { type ConnectionOptions as TLSConnectionOptions } from 'node:tls';
4
4
  import { type CallbackWithPromise } from '../apis/callbacks.ts';
5
5
  import { type Callback, type ResponseParser } from '../apis/definitions.ts';
6
6
  import { type SASLMechanismValue } from '../apis/enumerations.ts';
7
+ import { type SaslAuthenticateResponse, type SASLAuthenticationAPI } from '../apis/security/sasl-authenticate-v2.ts';
7
8
  import { Writer } from '../protocol/writer.ts';
8
9
  export type SASLCredentialProvider = () => string | Promise<string>;
9
10
  export interface Broker {
10
11
  host: string;
11
12
  port: number;
12
13
  }
14
+ export type SASLCustomAuthenticator = (mechanism: SASLMechanismValue, connection: Connection, authenticate: SASLAuthenticationAPI, usernameProvider: string | SASLCredentialProvider | undefined, passwordProvider: string | SASLCredentialProvider | undefined, tokenProvider: string | SASLCredentialProvider | undefined, callback: CallbackWithPromise<SaslAuthenticateResponse>) => void;
13
15
  export interface SASLOptions {
14
16
  mechanism: SASLMechanismValue;
15
17
  username?: string | SASLCredentialProvider;
16
18
  password?: string | SASLCredentialProvider;
17
19
  token?: string | SASLCredentialProvider;
18
20
  authBytesValidator?: (authBytes: Buffer, callback: CallbackWithPromise<Buffer>) => void;
21
+ authenticate?: SASLCustomAuthenticator;
19
22
  }
20
23
  export interface ConnectionOptions {
21
24
  connectTimeout?: number;
@@ -69,10 +69,10 @@ export class Connection extends EventEmitter {
69
69
  notifyCreation('connection', this);
70
70
  }
71
71
  get host() {
72
- return this.#status === ConnectionStatuses.CONNECTED ? this.#host : undefined;
72
+ return this.#status === ConnectionStatuses.CONNECTING || ConnectionStatuses.CONNECTED ? this.#host : undefined;
73
73
  }
74
74
  get port() {
75
- return this.#status === ConnectionStatuses.CONNECTED ? this.#port : undefined;
75
+ return this.#status === ConnectionStatuses.CONNECTING || ConnectionStatuses.CONNECTED ? this.#port : undefined;
76
76
  }
77
77
  get instanceId() {
78
78
  return this.#instanceId;
@@ -257,7 +257,7 @@ export class Connection extends EventEmitter {
257
257
  if (this.#status === ConnectionStatuses.CONNECTING) {
258
258
  this.#status = ConnectionStatuses.AUTHENTICATING;
259
259
  }
260
- const { mechanism, username, password, token } = this.#options.sasl;
260
+ const { mechanism, username, password, token, authenticate } = this.#options.sasl;
261
261
  if (!allowedSASLMechanisms.includes(mechanism)) {
262
262
  this.#onConnectionError(host, port, diagnosticContext, new UserError(`SASL mechanism ${mechanism} not supported.`));
263
263
  return;
@@ -269,12 +269,18 @@ export class Connection extends EventEmitter {
269
269
  }
270
270
  this.emit('sasl:handshake', response.mechanisms);
271
271
  const callback = this.#onSaslAuthenticate.bind(this, host, port, diagnosticContext);
272
- if (mechanism === SASLMechanisms.PLAIN) {
272
+ if (authenticate) {
273
+ authenticate(mechanism, this, saslAuthenticateV2.api, username, password, token, callback);
274
+ }
275
+ else if (mechanism === SASLMechanisms.PLAIN) {
273
276
  saslPlain.authenticate(saslAuthenticateV2.api, this, username, password, callback);
274
277
  }
275
278
  else if (mechanism === SASLMechanisms.OAUTHBEARER) {
276
279
  saslOAuthBearer.authenticate(saslAuthenticateV2.api, this, token, callback);
277
280
  }
281
+ else if (mechanism === SASLMechanisms.GSSAPI) {
282
+ callback(new UserError('No custom SASL/GSSAPI authenticator provided.'), undefined);
283
+ }
278
284
  else {
279
285
  saslScramSha.authenticate(saslAuthenticateV2.api, this, mechanism.substring(6), username, password, defaultCrypto, callback);
280
286
  }
@@ -11,5 +11,6 @@ export * from './records.ts';
11
11
  export * as saslOAuthBearer from './sasl/oauth-bearer.ts';
12
12
  export * as saslPlain from './sasl/plain.ts';
13
13
  export * as saslScramSha from './sasl/scram-sha.ts';
14
+ export * as saslUtils from './sasl/utils.ts';
14
15
  export * from './varint.ts';
15
16
  export * from './writer.ts';
@@ -11,5 +11,6 @@ export * from "./records.js";
11
11
  export * as saslOAuthBearer from "./sasl/oauth-bearer.js";
12
12
  export * as saslPlain from "./sasl/plain.js";
13
13
  export * as saslScramSha from "./sasl/scram-sha.js";
14
+ export * as saslUtils from "./sasl/utils.js";
14
15
  export * from "./varint.js";
15
16
  export * from "./writer.js";
@@ -1,6 +1,6 @@
1
1
  import { createPromisifiedCallback, kCallbackPromise } from "../../apis/callbacks.js";
2
2
  import { AuthenticationError } from "../../errors.js";
3
- import { getCredential } from "./credential-provider.js";
3
+ import { getCredential } from "./utils.js";
4
4
  export function jwtValidateAuthenticationBytes(authBytes, callback) {
5
5
  let authData = {};
6
6
  try {
@@ -24,7 +24,8 @@ export function authenticate(authenticateAPI, connection, tokenOrProvider, callb
24
24
  }
25
25
  getCredential('SASL/OAUTHBEARER token', tokenOrProvider, (error, token) => {
26
26
  if (error) {
27
- return callback(error, undefined);
27
+ callback(error, undefined);
28
+ return;
28
29
  }
29
30
  authenticateAPI(connection, Buffer.from(`n,,\x01auth=Bearer ${token}\x01\x01`), callback);
30
31
  });
@@ -1,16 +1,18 @@
1
1
  import { createPromisifiedCallback, kCallbackPromise } from "../../apis/callbacks.js";
2
- import { getCredential } from "./credential-provider.js";
2
+ import { getCredential } from "./utils.js";
3
3
  export function authenticate(authenticateAPI, connection, usernameProvider, passwordProvider, callback) {
4
4
  if (!callback) {
5
5
  callback = createPromisifiedCallback();
6
6
  }
7
7
  getCredential('SASL/PLAIN username', usernameProvider, (error, username) => {
8
8
  if (error) {
9
- return callback(error, undefined);
9
+ callback(error, undefined);
10
+ return;
10
11
  }
11
12
  getCredential('SASL/PLAIN password', passwordProvider, (error, password) => {
12
13
  if (error) {
13
- return callback(error, undefined);
14
+ callback(error, undefined);
15
+ return;
14
16
  }
15
17
  authenticateAPI(connection, Buffer.from(['', username, password].join('\0')), callback);
16
18
  });
@@ -14,11 +14,13 @@ export interface ScramCryptoModule {
14
14
  }
15
15
  export declare const ScramAlgorithms: {
16
16
  readonly 'SHA-256': {
17
+ readonly id: "SHA-256";
17
18
  readonly keyLength: 32;
18
19
  readonly algorithm: "sha256";
19
20
  readonly minIterations: 4096;
20
21
  };
21
22
  readonly 'SHA-512': {
23
+ readonly id: "SHA-512";
22
24
  readonly keyLength: 64;
23
25
  readonly algorithm: "sha512";
24
26
  readonly minIterations: 4096;
@@ -1,7 +1,7 @@
1
1
  import { createHash, createHmac, pbkdf2Sync, randomBytes } from 'node:crypto';
2
2
  import { createPromisifiedCallback, kCallbackPromise } from "../../apis/callbacks.js";
3
3
  import { AuthenticationError } from "../../errors.js";
4
- import { getCredential } from "./credential-provider.js";
4
+ import { getCredential } from "./utils.js";
5
5
  const GS2_HEADER = 'n,,';
6
6
  const GS2_HEADER_BASE64 = Buffer.from(GS2_HEADER).toString('base64');
7
7
  const HMAC_CLIENT_KEY = 'Client Key';
@@ -9,11 +9,13 @@ const HMAC_SERVER_KEY = 'Server Key';
9
9
  const PARAMETERS_PARSER = /([^=]+)=(.+)/;
10
10
  export const ScramAlgorithms = {
11
11
  'SHA-256': {
12
+ id: 'SHA-256',
12
13
  keyLength: 32,
13
14
  algorithm: 'sha256',
14
15
  minIterations: 4096
15
16
  },
16
17
  'SHA-512': {
18
+ id: 'SHA-512',
17
19
  keyLength: 64,
18
20
  algorithm: 'sha512',
19
21
  minIterations: 4096
@@ -129,11 +131,13 @@ export function authenticate(authenticateAPI, connection, algorithm, usernamePro
129
131
  }
130
132
  getCredential(`SASL/SCRAM-${algorithm} username`, usernameProvider, (error, username) => {
131
133
  if (error) {
132
- return callback(error, undefined);
134
+ callback(error, undefined);
135
+ return;
133
136
  }
134
137
  getCredential(`SASL/SCRAM-${algorithm} password`, passwordProvider, (error, password) => {
135
138
  if (error) {
136
- return callback(error, undefined);
139
+ callback(error, undefined);
140
+ return;
137
141
  }
138
142
  performAuthentication(connection, algorithm, definition, authenticateAPI, crypto, username, password, callback);
139
143
  });
@@ -0,0 +1,4 @@
1
+ import { type CallbackWithPromise } from '../../apis/index.ts';
2
+ import { type SASLCredentialProvider } from '../../network/connection.ts';
3
+ export declare function getCredential(label: string, credentialOrProvider: string | SASLCredentialProvider, callback: CallbackWithPromise<string>): void;
4
+ export declare function getCredential(label: string, credentialOrProvider: string | SASLCredentialProvider): Promise<string>;
@@ -1,28 +1,32 @@
1
+ import { createPromisifiedCallback, kCallbackPromise } from "../../apis/index.js";
1
2
  import { AuthenticationError } from "../../errors.js";
2
3
  export function getCredential(label, credentialOrProvider, callback) {
4
+ if (!callback) {
5
+ callback = createPromisifiedCallback();
6
+ }
3
7
  if (typeof credentialOrProvider === 'string') {
4
8
  callback(null, credentialOrProvider);
5
- return;
9
+ return callback[kCallbackPromise];
6
10
  }
7
11
  else if (typeof credentialOrProvider !== 'function') {
8
12
  callback(new AuthenticationError(`The ${label} should be a string or a function.`), undefined);
9
- return;
13
+ return callback[kCallbackPromise];
10
14
  }
11
15
  try {
12
16
  const credential = credentialOrProvider();
13
17
  if (typeof credential === 'string') {
14
18
  callback(null, credential);
15
- return;
19
+ return callback[kCallbackPromise];
16
20
  }
17
21
  else if (typeof credential?.then !== 'function') {
18
22
  callback(new AuthenticationError(`The ${label} provider should return a string or a promise that resolves to a string.`), undefined);
19
- return;
23
+ return callback[kCallbackPromise];
20
24
  }
21
25
  credential
22
26
  .then(token => {
23
27
  if (typeof token !== 'string') {
24
28
  process.nextTick(callback, new AuthenticationError(`The ${label} provider should resolve to a string.`), undefined);
25
- return;
29
+ return callback[kCallbackPromise];
26
30
  }
27
31
  process.nextTick(callback, null, token);
28
32
  })
@@ -33,4 +37,5 @@ export function getCredential(label, credentialOrProvider, callback) {
33
37
  catch (error) {
34
38
  callback(new AuthenticationError(`The ${label} provider threw an error.`, { cause: error }), undefined);
35
39
  }
40
+ return callback[kCallbackPromise];
36
41
  }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  export const name = "@platformatic/kafka";
2
- export const version = "1.21.0";
2
+ export const version = "1.22.0-alpha.2";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platformatic/kafka",
3
- "version": "1.21.0",
3
+ "version": "1.22.0-alpha.2",
4
4
  "description": "Modern and performant client for Apache Kafka",
5
5
  "homepage": "https://github.com/platformatic/kafka",
6
6
  "author": "Platformatic Inc. <oss@platformatic.dev> (https://platformatic.dev)",
@@ -40,6 +40,7 @@
40
40
  "@confluentinc/kafka-javascript": "^1.5.0",
41
41
  "@platformatic/rdkafka": "^4.0.0",
42
42
  "@types/debug": "^4.1.12",
43
+ "@types/kerberos": "^1.1.5",
43
44
  "@types/node": "^22.18.5",
44
45
  "@types/semver": "^7.7.1",
45
46
  "@watchable/unpromise": "^1.0.2",
@@ -50,8 +51,9 @@
50
51
  "eslint": "^9.35.0",
51
52
  "fast-jwt": "^6.0.2",
52
53
  "hwp": "^0.4.1",
53
- "kafkajs": "^2.2.4",
54
54
  "json5": "^2.2.3",
55
+ "kafkajs": "^2.2.4",
56
+ "kerberos": "^2.2.2",
55
57
  "neostandard": "^0.12.2",
56
58
  "parse5": "^7.3.0",
57
59
  "prettier": "^3.6.2",
@@ -1,3 +0,0 @@
1
- import { type Callback } from '../../apis/index.ts';
2
- import { type SASLCredentialProvider } from '../../network/connection.ts';
3
- export declare function getCredential(label: string, credentialOrProvider: string | SASLCredentialProvider, callback: Callback<string>): void;