wildduck/imap-core/lib/commands/authenticate-plain.js

146 lines
4.6 KiB
JavaScript
Raw Normal View History

2017-03-06 05:45:50 +08:00
'use strict';
const imapTools = require('../imap-tools');
2017-03-06 05:45:50 +08:00
module.exports = {
state: 'Not Authenticated',
2017-06-03 14:51:58 +08:00
schema: [
{
name: 'token',
type: 'string',
optional: true
}
],
2017-03-06 05:45:50 +08:00
handler(command, callback, next) {
2017-06-03 14:51:58 +08:00
let token = ((command.attributes && command.attributes[0] && command.attributes[0].value) || '').toString().trim();
2017-03-06 05:45:50 +08:00
let requireClientToken = (command.command || '').toString().toUpperCase() === 'AUTHENTICATE PLAIN-CLIENTTOKEN' ? true : false;
2017-09-11 03:53:12 +08:00
if (!this.secure && !this._server.options.disableSTARTTLS && !this._server.options.ignoreSTARTTLS) {
2017-03-06 05:45:50 +08:00
// Only allow authentication using TLS
return callback(null, {
response: 'BAD',
message: 'Run STARTTLS first'
});
}
// Check if authentication method is set
if (typeof this._server.onAuth !== 'function') {
return callback(null, {
response: 'NO',
message: 'Authentication not implemented'
});
}
if (!token) {
this._nextHandler = (token, next) => {
this._nextHandler = false;
next(); // keep the parser flowing
authenticate(this, token, requireClientToken, callback);
2017-03-06 05:45:50 +08:00
};
this.send('+');
return next(); // resume input parser. Normally this is done by callback() but we need the next input sooner
}
authenticate(this, token, requireClientToken, callback);
2017-03-06 05:45:50 +08:00
}
};
function authenticate(connection, token, requireClientToken, callback) {
2018-05-11 19:39:23 +08:00
let data = Buffer.from(token, 'base64')
.toString()
.split('\x00');
2017-03-06 05:45:50 +08:00
if ((!requireClientToken && data.length !== 3) || (requireClientToken && data.length !== 4)) {
2017-03-06 05:45:50 +08:00
return callback(null, {
response: 'BAD',
message: 'Invalid SASL argument'
});
}
let username = (data[1] || '').toString().trim();
let password = (data[2] || '').toString().trim();
let clientToken = (data[3] || '').toString().trim() || false;
2017-03-06 05:45:50 +08:00
// Do auth
2017-06-03 14:51:58 +08:00
connection._server.onAuth(
{
method: 'PLAIN',
username,
2017-10-07 19:46:42 +08:00
password,
2018-10-31 16:04:32 +08:00
clientToken,
connection
2017-06-03 14:51:58 +08:00
},
connection.session,
(err, response) => {
if (err) {
connection._server.logger.info(
{
err,
tnx: 'auth',
username,
method: 'PLAIN',
action: 'fail',
cid: connection.id
},
'[%s] Authentication error for %s using %s\n%s',
connection.id,
username,
'PLAIN',
err.message
);
return callback(err);
}
if (!response || !response.user) {
connection._server.logger.info(
{
tnx: 'auth',
username,
method: 'PLAIN',
action: 'fail',
cid: connection.id
},
'[%s] Authentication failed for %s using %s',
connection.id,
username,
'PLAIN'
);
return callback(null, {
response: 'NO',
code: 'AUTHENTICATIONFAILED',
message: 'Invalid credentials'
});
}
connection._server.logger.info(
{
tnx: 'auth',
username,
method: 'PLAIN',
action: 'success',
cid: connection.id,
clientToken
2017-06-03 14:51:58 +08:00
},
'[%s] %s authenticated using %s%s',
2017-06-03 14:51:58 +08:00
connection.id,
2017-05-16 18:57:04 +08:00
username,
'PLAIN' + (requireClientToken ? '-CLIENTTOKEN' : ''),
clientToken ? ' with token "' + clientToken + '"' : ''
2017-06-03 14:51:58 +08:00
);
connection.setUser(response.user);
2017-06-03 14:51:58 +08:00
connection.state = 'Authenticated';
connection.setupNotificationListener();
imapTools.sendCapabilityResponse(connection);
2017-06-03 14:51:58 +08:00
callback(null, {
response: 'OK',
2018-05-11 19:39:23 +08:00
message: Buffer.from(username + ' authenticated').toString('binary')
2017-03-06 05:45:50 +08:00
});
}
2017-06-03 14:51:58 +08:00
);
2017-03-06 05:45:50 +08:00
}