Skip to content

Commit

Permalink
Merge pull request #201 from cplussharp/jwt-support
Browse files Browse the repository at this point in the history
Support JWT authentication method
  • Loading branch information
jeffalbion authored Aug 4, 2022
2 parents 838b0d7 + c8cad8b commit f12206f
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 3 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ Below is a major feature comparison chart between the two drivers:
| Active-Active Read Enabled |:heavy_check_mark:|:x:|
| Client-Side Data Encryption |:heavy_check_mark:|:x:|
| Statement Distribution |:heavy_check_mark:|:x:|
| Password/PBKDF2 Authentication |:heavy_check_mark:|:heavy_check_mark:|
| SAML Authentication |:heavy_check_mark:|:heavy_check_mark:|
| JWT Authentication |:heavy_check_mark:|:heavy_check_mark:|
| LDAP Authentication |:heavy_check_mark:|:x:|
| Kerberos Authentication |:heavy_check_mark:|:x:|
| X.509 Authentication |:heavy_check_mark:|:x:|
| Secure User Store Integration (hdbuserstore) |:heavy_check_mark:|:x:|
| Connections through HTTP proxy |:heavy_check_mark:|:x:|
| Connections through SOCKS proxy (SAP Cloud Connector) |:heavy_check_mark:|:x:|
Expand Down Expand Up @@ -212,6 +217,25 @@ client.connect({

After a successful SAML authentication, the server returns the database `user` and a `SessionCookie` which can be used for reconnecting.

#### JWT token
JWT tokens can also be used to authenticate users.

Instead of `user` and `password` you have to provide a JWT `token`:

```js
client.connect({
token: 'eyJhbGciOiJSUzI1NiJ9....'
},function (err) {
if (err) {
return console.error('Error:', err);
}
console.log('User:', client.get('user'));
console.log('SessionCookie:', client.get('SessionCookie'));
});
```

After a successful JWT authentication, the server returns the database `user` and a `SessionCookie` which can be used for reconnecting.

### Encrypted network communication
To establish an encrypted database connection just pass either `key`, `cert` and `ca` or a `pfx` to createClient.

Expand Down
5 changes: 5 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ Client.prototype.connect = function connect(options, cb) {
this._settings.assertion = undefined;
}

// JWT token can only be used once
if (this._settings.token) {
this._settings.token = undefined;
}

var self = this;

function done(err, reply) {
Expand Down
40 changes: 40 additions & 0 deletions lib/protocol/auth/JWT.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2022 SAP SE.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http: //www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific
// language governing permissions and limitations under the License.
'use strict';

module.exports = JWT;

function JWT(options) {
this.name = 'JWT';
this.token = options.token || options.password;
this.user = undefined;
this.sessionCookie = undefined;
}

JWT.prototype.initialData = function initialData() {
return this.token;
};

JWT.prototype.initialize = function initialize(buffer, cb) {
this.user = buffer.toString('utf8');
cb();
};

JWT.prototype.finalData = function finalData() {
return new Buffer(0);
};

JWT.prototype.finalize = function finalize(buffer) {
this.sessionCookie = buffer;
};
6 changes: 5 additions & 1 deletion lib/protocol/auth/Manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

var SCRAMSHA256 = require('./SCRAMSHA256');
var SAML = require('./SAML');
var JWT = require('./JWT');
var SessionCookie = require('./SessionCookie');

module.exports = Manager;
Expand All @@ -24,9 +25,12 @@ function Manager(options) {
this.user = options.user || '';
this._authMethod = undefined;
this._authMethods = [];
if (options.assertion || (!options.user && options.password)) {
if (options.assertion || (!options.user && options.password && options.password.toString().startsWith("<") )) {
this._authMethods.push(new SAML(options));
}
if (options.token || (!options.user && options.password && options.password.toString().startsWith("ey"))) {
this._authMethods.push(new JWT(options));
}
if (options.sessionCookie) {
this._authMethods.push(new SessionCookie(options));
}
Expand Down
49 changes: 48 additions & 1 deletion test/auth.Manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ describe('Auth', function () {
describe('#SAML', function () {

var method = 'SAML';
var assertion = new Buffer('3fca227d', 'hex');
var assertion = new Buffer('<saml:Assertion></saml:Assertion>', 'ascii');
var sessionCookie = new Buffer('fcac0f42', 'hex');

it('should get the corresponding authentication method instance', function () {
Expand Down Expand Up @@ -244,6 +244,53 @@ describe('Auth', function () {

});

describe('#JWT', function () {

var method = 'JWT';
var token = new Buffer('eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.eu3buOdtT84lHs90LfmC3MJ_17Qg0FfgBke2qnW5yE-wDlEdKWWEURFoneCzMmdGtJcnVqINmZD1X8XbvoAWeWq_tH75fSKcg_1RaooYaARdtpQGF_BtjXJ9jMJHoJ9kgjO8cv06GobNaoydu2v6C8fsSIBDVw9zEApGZIwNCJztkgmEGmkQKXHHxKRISi55DgCowVYk1Obgp55KMjRqmMkAvw8qoMsAU109n26NGQNI19wOaGiPrSGKpENkgq6lWFY6visswoA8X3pYn6EXdAqEGjuFH0ADuvqUoRyrrIaaem30JgVny8LQ-t2ms7gck8jPdxS7TUjiB2hHKjRwBw', 'ascii');
var sessionCookie = new Buffer('420facfc', 'hex');

it('should get the corresponding authentication method instance', function () {
var manager = auth.createManager({
user: null,
password: token
});
var authMethod = manager.getMethod(method);
authMethod.token.should.equal(token);
});

it('should authenticate and connect successfully', function () {
var manager = auth.createManager({
token: token
});
manager.user.should.equal('');
manager._authMethods.should.have.length(1);
var authMethod = manager._authMethods[0];
authMethod.name.should.equal(method);
authMethod.token.should.equal(token);
// initial data
var initialData = authMethod.initialData();
initialData.should.equal(token);
initialData = manager.initialData();
initialData.should.eql(['', method, token]);
// initialize manager
manager.initialize([method, new Buffer(user, 'utf8')], function(err) {
manager._authMethod.should.equal(authMethod);
// user
manager.userFromServer.should.equal(user);
// final data
var finalData = authMethod.finalData();
finalData.should.eql(emptyBuffer);
finalData = manager.finalData();
finalData.should.eql([user, method, emptyBuffer]);
// finalize manager
manager.finalize([method, sessionCookie]);
manager.sessionCookie.should.equal(sessionCookie);
});
});

});

describe('#SessionCookie', function () {

var method = 'SessionCookie';
Expand Down
13 changes: 12 additions & 1 deletion test/hdb.Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ describe('hdb', function () {

it('should connect with saml assertion', function (done) {
var client = new TestClient({
assertion: 'assertion'
assertion: '<saml:Assertion></saml:Assertion>'
});
client.connect(function (err) {
should(err === null).be.ok;
Expand All @@ -506,6 +506,17 @@ describe('hdb', function () {
});
});

it('should connect with jwt token', function (done) {
var client = new TestClient({
token: 'eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.eu3buOdtT84lHs90LfmC3MJ_17Qg0FfgBke2qnW5yE-wDlEdKWWEURFoneCzMmdGtJcnVqINmZD1X8XbvoAWeWq_tH75fSKcg_1RaooYaARdtpQGF_BtjXJ9jMJHoJ9kgjO8cv06GobNaoydu2v6C8fsSIBDVw9zEApGZIwNCJztkgmEGmkQKXHHxKRISi55DgCowVYk1Obgp55KMjRqmMkAvw8qoMsAU109n26NGQNI19wOaGiPrSGKpENkgq6lWFY6visswoA8X3pYn6EXdAqEGjuFH0ADuvqUoRyrrIaaem30JgVny8LQ-t2ms7gck8jPdxS7TUjiB2hHKjRwBw'
});
client.connect(function (err) {
should(err === null).be.ok;
should(client.get('token')).not.be.ok;
done();
});
});

it('should directly connect without constructor', function (done) {
var client = new TestClient();
var connection = client._connection;
Expand Down

0 comments on commit f12206f

Please sign in to comment.