Skip to content

Commit f594d11

Browse files
Add setToken API for OAuthBearer authentication flow (#1075)
1 parent a0648d3 commit f594d11

14 files changed

+328
-93
lines changed

examples/oauthbearer-default-flow.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
Producer, Consumer and HighLevelProducer:
2+
```js
3+
/*
4+
* node-rdkafka - Node.js wrapper for RdKafka C/C++ library
5+
*
6+
* Copyright (c) 2016 Blizzard Entertainment
7+
*
8+
* This software may be modified and distributed under the terms
9+
* of the MIT license. See the LICENSE.txt file for details.
10+
*/
11+
12+
var Kafka = require('../');
13+
14+
var token = "your_token";
15+
16+
var producer = new Kafka.Producer({
17+
//'debug' : 'all',
18+
'metadata.broker.list': 'localhost:9093',
19+
'security.protocol': 'SASL_SSL',
20+
'sasl.mechanisms': 'OAUTHBEARER',
21+
}).setOauthBearerToken(token);
22+
23+
//start the producer
24+
producer.connect();
25+
26+
//refresh the token
27+
producer.setOauthBearerToken(token);
28+
```
29+
30+
AdminClient:
31+
```js
32+
/*
33+
* node-rdkafka - Node.js wrapper for RdKafka C/C++ library
34+
*
35+
* Copyright (c) 2016 Blizzard Entertainment
36+
*
37+
* This software may be modified and distributed under the terms
38+
* of the MIT license. See the LICENSE.txt file for details.
39+
*/
40+
var Kafka = require('../');
41+
42+
var token = "your_token";
43+
44+
var admin = Kafka.AdminClient.create({
45+
'metadata.broker.list': 'localhost:9093',
46+
'security.protocol': 'SASL_SSL',
47+
'sasl.mechanisms': 'OAUTHBEARER',
48+
}, token);
49+
50+
//refresh the token
51+
admin.refreshOauthBearerToken(token);
52+
```
53+
54+
ConsumerStream:
55+
```js
56+
/*
57+
* node-rdkafka - Node.js wrapper for RdKafka C/C++ library
58+
*
59+
* Copyright (c) 2016 Blizzard Entertainment
60+
*
61+
* This software may be modified and distributed under the terms
62+
* of the MIT license. See the LICENSE.txt file for details.
63+
*/
64+
var Kafka = require('../');
65+
66+
var token = "your_token";
67+
68+
var stream = Kafka.KafkaConsumer.createReadStream({
69+
'metadata.broker.list': 'localhost:9093',
70+
'group.id': 'myGroup',
71+
'security.protocol': 'SASL_SSL',
72+
'sasl.mechanisms': 'OAUTHBEARER'
73+
}, {}, {
74+
topics: 'test1',
75+
initOauthBearerToken: token,
76+
});
77+
78+
//refresh the token
79+
stream.refreshOauthBearerToken(token.token);
80+
```

index.d.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ export interface ReadStreamOptions extends ReadableOptions {
117117
autoClose?: boolean;
118118
streamAsBatch?: boolean;
119119
connectOptions?: any;
120+
initOauthBearerToken?: string;
120121
}
121122

122123
export interface WriteStreamOptions extends WritableOptions {
@@ -137,6 +138,7 @@ export interface ProducerStream extends Writable {
137138
export interface ConsumerStream extends Readable {
138139
consumer: KafkaConsumer;
139140
connect(options: ConsumerGlobalConfig): void;
141+
refreshOauthBearerToken(tokenStr: string): void;
140142
close(cb?: () => void): void;
141143
}
142144

@@ -180,6 +182,8 @@ export abstract class Client<Events extends string> extends EventEmitter {
180182

181183
connect(metadataOptions?: MetadataOptions, cb?: (err: LibrdKafkaError, data: Metadata) => any): this;
182184

185+
setOauthBearerToken(tokenStr: string): this;
186+
183187
getClient(): any;
184188

185189
connectedTime(): number;
@@ -330,6 +334,8 @@ export interface NewTopic {
330334
}
331335

332336
export interface IAdminClient {
337+
refreshOauthBearerToken(tokenStr: string): void;
338+
333339
createTopic(topic: NewTopic, cb?: (err: LibrdKafkaError) => void): void;
334340
createTopic(topic: NewTopic, timeout?: number, cb?: (err: LibrdKafkaError) => void): void;
335341

@@ -343,5 +349,5 @@ export interface IAdminClient {
343349
}
344350

345351
export abstract class AdminClient {
346-
static create(conf: GlobalConfig): IAdminClient;
352+
static create(conf: GlobalConfig, initOauthBearerToken?: string): IAdminClient;
347353
}

lib/admin.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ var shallowCopy = require('./util').shallowCopy;
2525
* active handle with the brokers.
2626
*
2727
*/
28-
function createAdminClient(conf) {
28+
function createAdminClient(conf, initOauthBearerToken) {
2929
var client = new AdminClient(conf);
3030

31+
if (initOauthBearerToken) {
32+
client.refreshOauthBearerToken(initOauthBearerToken);
33+
}
34+
3135
// Wrap the error so we throw if it failed with some context
3236
LibrdKafkaError.wrap(client.connect(), true);
3337

@@ -105,6 +109,22 @@ AdminClient.prototype.disconnect = function() {
105109
this._isConnected = false;
106110
};
107111

112+
/**
113+
* Refresh OAuthBearer token, initially provided in factory method.
114+
* Expiry is always set to maximum value, as the callback of librdkafka
115+
* for token refresh is not used.
116+
*
117+
* @param {string} tokenStr - OAuthBearer token string
118+
* @see connection.cc
119+
*/
120+
AdminClient.prototype.refreshOauthBearerToken = function (tokenStr) {
121+
if (!tokenStr || typeof tokenStr !== 'string') {
122+
throw new Error("OAuthBearer token is undefined/empty or not a string");
123+
}
124+
125+
this._client.setToken(tokenStr);
126+
};
127+
108128
/**
109129
* Create a topic with a given config.
110130
*

lib/client.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,25 @@ Client.prototype.connect = function(metadataOptions, cb) {
229229

230230
};
231231

232+
/**
233+
* Set initial token before any connection is established for oauthbearer authentication flow.
234+
* Expiry is always set to maximum value, as the callback of librdkafka
235+
* for token refresh is not used.
236+
* Call this method again to refresh the token.
237+
*
238+
* @param {string} tokenStr - OAuthBearer token string
239+
* @see connection.cc
240+
* @return {Client} - Returns itself.
241+
*/
242+
Client.prototype.setOauthBearerToken = function (tokenStr) {
243+
if (!tokenStr || typeof tokenStr !== 'string') {
244+
throw new Error("OAuthBearer token is undefined/empty or not a string");
245+
}
246+
247+
this._client.setToken(tokenStr);
248+
return this;
249+
};
250+
232251
/**
233252
* Get the native Kafka client.
234253
*

lib/kafka-consumer-stream.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ function KafkaConsumerStream(consumer, options) {
112112
self.push(null);
113113
});
114114

115+
if (options.initOauthBearerToken) {
116+
this.consumer.setOauthBearerToken(options.initOauthBearerToken);
117+
}
118+
115119
// Call connect. Handles potentially being connected already
116120
this.connect(this.connectOptions);
117121

@@ -123,6 +127,18 @@ function KafkaConsumerStream(consumer, options) {
123127

124128
}
125129

130+
/**
131+
* Refresh OAuthBearer token, initially provided in factory method.
132+
* Expiry is always set to maximum value, as the callback of librdkafka
133+
* for token refresh is not used.
134+
*
135+
* @param {string} tokenStr - OAuthBearer token string
136+
* @see connection.cc
137+
*/
138+
KafkaConsumerStream.prototype.refreshOauthBearerToken = function (tokenStr) {
139+
this.consumer.setOauthBearerToken(tokenStr);
140+
};
141+
126142
/**
127143
* Internal stream read method. This method reads message objects.
128144
* @param {number} size - This parameter is ignored for our cases.

0 commit comments

Comments
 (0)