Skip to content

Commit c386190

Browse files
[Backport 6.x] ApiKey should take precedence over basic auth (#1116)
* ApiKey should take precedence over basic auth * Updated docs * Updated test Co-authored-by: Tomas Della Vedova <[email protected]>
1 parent 6839df0 commit c386190

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

docs/authentication.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const client = new Client({
3333

3434
You can provide your credentials by passing the `username` and `password` parameters via the `auth` option.
3535

36+
NOTE: If you provide both basic authentication credentials and the Api Key configuration, the Api Key will take precedence.
37+
3638
[source,js]
3739
----
3840
const { Client } = require('@elastic/elasticsearch')
@@ -60,6 +62,8 @@ const client = new Client({
6062
You can use the https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-create-api-key.html[ApiKey] authentication by passing the `apiKey` parameter via the `auth` option. +
6163
The `apiKey` parameter can be either a base64 encoded string or an object with the values that you can obtain from the https://www.elastic.co/guide/en/elasticsearch/reference/7.x/security-api-create-api-key.html[create api key endpoint].
6264

65+
NOTE: If you provide both basic authentication credentials and the Api Key configuration, the Api Key will take precedence.
66+
6367
[source,js]
6468
----
6569
const { Client } = require('@elastic/elasticsearch')

lib/Connection.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,16 +294,14 @@ function resolve (host, path) {
294294

295295
function prepareHeaders (headers = {}, auth) {
296296
if (auth != null && headers.authorization == null) {
297-
if (auth.username && auth.password) {
298-
headers.authorization = 'Basic ' + Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
299-
}
300-
301297
if (auth.apiKey) {
302298
if (typeof auth.apiKey === 'object') {
303299
headers.authorization = 'ApiKey ' + Buffer.from(`${auth.apiKey.id}:${auth.apiKey.api_key}`).toString('base64')
304300
} else {
305301
headers.authorization = `ApiKey ${auth.apiKey}`
306302
}
303+
} else if (auth.username && auth.password) {
304+
headers.authorization = 'Basic ' + Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
307305
}
308306
}
309307
return headers

lib/pool/BaseConnectionPool.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ class BaseConnectionPool {
4242
opts = this.urlToHost(opts)
4343
}
4444

45-
if (opts.url.username !== '' && opts.url.password !== '') {
45+
if (this.auth !== null) {
46+
opts.auth = this.auth
47+
} else if (opts.url.username !== '' && opts.url.password !== '') {
4648
opts.auth = {
4749
username: decodeURIComponent(opts.url.username),
4850
password: decodeURIComponent(opts.url.password)
4951
}
50-
} else if (this.auth !== null) {
51-
opts.auth = this.auth
5252
}
5353

5454
if (opts.ssl == null) opts.ssl = this._ssl

test/unit/client.test.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,62 @@ test('Authentication', t => {
467467
})
468468
})
469469

470+
t.test('ApiKey should take precedence over basic auth (in url)', t => {
471+
t.plan(3)
472+
473+
function handler (req, res) {
474+
t.match(req.headers, {
475+
authorization: 'ApiKey Zm9vOmJhcg=='
476+
})
477+
res.setHeader('Content-Type', 'application/json;utf=8')
478+
res.end(JSON.stringify({ hello: 'world' }))
479+
}
480+
481+
buildServer(handler, ({ port }, server) => {
482+
const client = new Client({
483+
node: `http://user:pwd@localhost:${port}`,
484+
auth: {
485+
apiKey: 'Zm9vOmJhcg=='
486+
}
487+
})
488+
489+
client.info((err, { body }) => {
490+
t.error(err)
491+
t.deepEqual(body, { hello: 'world' })
492+
server.stop()
493+
})
494+
})
495+
})
496+
497+
t.test('ApiKey should take precedence over basic auth (in opts)', t => {
498+
t.plan(3)
499+
500+
function handler (req, res) {
501+
t.match(req.headers, {
502+
authorization: 'ApiKey Zm9vOmJhcg=='
503+
})
504+
res.setHeader('Content-Type', 'application/json;utf=8')
505+
res.end(JSON.stringify({ hello: 'world' }))
506+
}
507+
508+
buildServer(handler, ({ port }, server) => {
509+
const client = new Client({
510+
node: `http://localhost:${port}`,
511+
auth: {
512+
apiKey: 'Zm9vOmJhcg==',
513+
username: 'user',
514+
password: 'pwd'
515+
}
516+
})
517+
518+
client.info((err, { body }) => {
519+
t.error(err)
520+
t.deepEqual(body, { hello: 'world' })
521+
server.stop()
522+
})
523+
})
524+
})
525+
470526
t.end()
471527
})
472528

@@ -827,6 +883,44 @@ test('Elastic cloud config', t => {
827883
t.deepEqual(pool._ssl, { secureProtocol: 'TLSv1_2_method' })
828884
})
829885

886+
t.test('ApiKey should take precedence over basic auth', t => {
887+
t.plan(5)
888+
const client = new Client({
889+
cloud: {
890+
// 'localhost$abcd$efgh'
891+
id: 'name:bG9jYWxob3N0JGFiY2QkZWZnaA=='
892+
},
893+
auth: {
894+
username: 'elastic',
895+
password: 'changeme',
896+
apiKey: 'Zm9vOmJhcg=='
897+
}
898+
})
899+
900+
const pool = client.connectionPool
901+
t.ok(pool instanceof CloudConnectionPool)
902+
t.match(pool.connections.find(c => c.id === 'https://abcd.localhost/'), {
903+
url: new URL('https://elastic:[email protected]'),
904+
id: 'https://abcd.localhost/',
905+
headers: {
906+
authorization: 'ApiKey Zm9vOmJhcg=='
907+
},
908+
ssl: { secureProtocol: 'TLSv1_2_method' },
909+
deadCount: 0,
910+
resurrectTimeout: 0,
911+
roles: {
912+
master: true,
913+
data: true,
914+
ingest: true,
915+
ml: false
916+
}
917+
})
918+
919+
t.strictEqual(client.transport.compression, 'gzip')
920+
t.strictEqual(client.transport.suggestCompression, true)
921+
t.deepEqual(pool._ssl, { secureProtocol: 'TLSv1_2_method' })
922+
})
923+
830924
t.test('Override default options', t => {
831925
t.plan(4)
832926
const client = new Client({

0 commit comments

Comments
 (0)