Skip to content

Commit

Permalink
Support async authentication - onAuthenticate may now return Promise. C…
Browse files Browse the repository at this point in the history
…loses #31.
  • Loading branch information
sv2 committed May 1, 2018
1 parent d834631 commit 1bb0833
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 7 deletions.
16 changes: 15 additions & 1 deletion examples/authtest/authtest.js
Expand Up @@ -80,7 +80,16 @@ app.use(swStats.getMiddleware({
sessionMaxAge: maxAge,
onAuthenticate: function(req,username,password){
// simple check for username and password
return((username==='swagger-stats') && (password==='swagger-stats') );
if(username==='swagger-stats') {
return ((username === 'swagger-stats') && (password === 'swagger-stats'));
} else if(username==='swagger-promise'){
return new Promise(function(resolve) {
setTimeout(function(){
resolve((username === 'swagger-promise') && (password === 'swagger-promise'));
}, 1000);
});
}
return false;
}
}));

Expand Down Expand Up @@ -143,4 +152,9 @@ function mockApiSendResponse(res,code,message,payloadsize){
}
}

process.on('unhandledRejection', function(error) {
debug('unhandledRejection', error.message, error.stack);
});


module.exports.app = app;
96 changes: 92 additions & 4 deletions lib/swsInterface.js
Expand Up @@ -120,6 +120,72 @@ function processOptions(options){

function processAuth(req,res,useWWWAuth) {

return new Promise( function (resolve, reject) {
if( !swsOptions.authentication ){
return resolve(true);
}

var cookies = new Cookies( req, res );

// Check session cookie
var sessionIdCookie = cookies.get('sws-session-id');
if( (sessionIdCookie !== undefined) && (sessionIdCookie !== null) ){

if( sessionIdCookie in sessionIDs ){
// renew it
//sessionIDs[sessionIdCookie] = Date.now();
storeSessionID(sessionIdCookie);
cookies.set('sws-session-id',sessionIdCookie,{path:swsOptions.uriPath,maxAge:swsOptions.sessionMaxAge*1000});
// Ok
req['sws-auth'] = true;
return resolve(true);
}
}

var authInfo = basicAuth(req);

var authenticated = false;
var msg = 'Authentication required';

if( (authInfo !== undefined) && (authInfo!==null) && ('name' in authInfo) && ('pass' in authInfo)){
if(typeof swsOptions.onAuthenticate === 'function'){

Promise.resolve(swsOptions.onAuthenticate(req, authInfo.name, authInfo.pass)).then(function(onAuthResult) {
if( onAuthResult ){

authenticated = true;

// Session is only for stats requests
if(req.url.startsWith(pathStats)){
// Generate session id
var sessid = uuidv1();
storeSessionID(sessid);
// Set session cookie with expiration in 15 min
cookies.set('sws-session-id',sessid,{path:swsOptions.uriPath,maxAge:swsOptions.sessionMaxAge*1000});
}

req['sws-auth'] = true;
return resolve(true);

}else{
msg = 'Invalid credentials';
res.status(403).end(msg);
return resolve(false);
}
});

}else{
res.status(403).end(msg);
return resolve(false);
}
}else{
res.status(403).end(msg);
return resolve(false);
}

});

/*
if( !swsOptions.authentication ){
return true;
}
Expand Down Expand Up @@ -176,6 +242,7 @@ function processAuth(req,res,useWWWAuth) {
req['sws-auth'] = true;
return true;
*/
}

function processLogout(req,res){
Expand All @@ -200,6 +267,18 @@ function processLogout(req,res){
// Query parameters (fields, path, method) defines which stat fields to return
function processGetStats(req,res){

processAuth(req,res).then(function (authResult){
if(!authResult){
return;
}
res.status(200);
if(('sws-auth' in req) && req['sws-auth']){
res.setHeader('x-sws-authenticated','true');
}
res.json( processor.getStats( req.query ) );
});

This comment has been minimized.

Copy link
@chadxz

chadxz May 7, 2018

@sv2 I think that without an error handler here (the second parameter to .then(successCallback, errorCallback) errors in the promise will be swallowed and an "UnhandledRejection" event will be emitted on the process object. This is probably not desirable

This comment has been minimized.

Copy link
@sv2

sv2 May 8, 2018

Author Collaborator

@chadxz The idea was that processAuth does not reject promise - it is always resolved with result true or false. But you are right, the implementation in the application (implementation of onAuthenticate) may reject. Thanks, I'll add handling of rejection


/*
if(!processAuth(req,res)) {
return;
}
Expand All @@ -209,19 +288,30 @@ function processGetStats(req,res){
}
res.status(200).json( processor.getStats( req.query ) );
*/
}


// Process /swagger-stats/metrics request
// Return all metrics for Prometheus
function processGetMetrics(req,res){

processAuth(req,res).then(function (authResult){
if(!authResult){
return;
}
res.status(200).set('Content-Type', 'text/plain');
res.end(promClient.register.metrics());
});

/*
if(!processAuth(req,res)) {
return;
}
res.status(200).set('Content-Type', 'text/plain');
res.end(promClient.register.metrics());
*/
}

module.exports = {
Expand All @@ -240,11 +330,9 @@ module.exports = {
// Respond to requests handled by swagger-stats
// swagger-stats requests will not be counted in statistics
if(req.url.startsWith(pathStats)) {
processGetStats(req, res);
return;
return processGetStats(req, res);
}else if(req.url.startsWith(pathMetrics)){
processGetMetrics(req,res);
return;
return processGetMetrics(req,res);
}else if(req.url.startsWith(pathLogout)){
processLogout(req,res);
return;
Expand Down
14 changes: 12 additions & 2 deletions test/400_auth.js
Expand Up @@ -178,9 +178,19 @@ setImmediate(function() {
});
});

it('should login again', function (done) {
it('should not login with wrong credentials using promise based auth method', function (done) {
apiAuthTest.get(swsTestFixture.SWS_TEST_STATS_API)
.auth('swagger-stats', 'swagger-stats')
.auth('swagger-promise', 'wrong')
.expect(403)
.end(function (err, res) {
if (err) return done(err);
done();
});
});

it('should login again using promise based auth method', function (done) {
apiAuthTest.get(swsTestFixture.SWS_TEST_STATS_API)
.auth('swagger-promise', 'swagger-promise')
.expect(200)
.expect('set-cookie', /sws-session-id/)
.expect('x-sws-authenticated', /true/)
Expand Down

0 comments on commit 1bb0833

Please sign in to comment.