Projektstart
This commit is contained in:
174
backend/node_modules/imapflow/lib/commands/authenticate.js
generated
vendored
Normal file
174
backend/node_modules/imapflow/lib/commands/authenticate.js
generated
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
'use strict';
|
||||
|
||||
const { getStatusCode, getErrorText } = require('../tools.js');
|
||||
|
||||
async function authOauth(connection, username, accessToken) {
|
||||
let oauthbearer;
|
||||
let command;
|
||||
let breaker;
|
||||
|
||||
if (connection.capabilities.has('AUTH=OAUTHBEARER')) {
|
||||
oauthbearer = [`n,a=${username},`, `host=${connection.servername}`, `port=993`, `auth=Bearer ${accessToken}`, '', ''].join('\x01');
|
||||
command = 'OAUTHBEARER';
|
||||
breaker = 'AQ==';
|
||||
} else if (connection.capabilities.has('AUTH=XOAUTH') || connection.capabilities.has('AUTH=XOAUTH2')) {
|
||||
oauthbearer = [`user=${username}`, `auth=Bearer ${accessToken}`, '', ''].join('\x01');
|
||||
command = 'XOAUTH2';
|
||||
breaker = '';
|
||||
}
|
||||
|
||||
let errorResponse = false;
|
||||
try {
|
||||
let response = await connection.exec(
|
||||
'AUTHENTICATE',
|
||||
[
|
||||
{ type: 'ATOM', value: command },
|
||||
{ type: 'ATOM', value: Buffer.from(oauthbearer).toString('base64'), sensitive: true }
|
||||
],
|
||||
{
|
||||
onPlusTag: async resp => {
|
||||
if (resp.attributes && resp.attributes[0] && resp.attributes[0].type === 'TEXT') {
|
||||
try {
|
||||
errorResponse = JSON.parse(Buffer.from(resp.attributes[0].value, 'base64').toString());
|
||||
} catch (err) {
|
||||
connection.log.debug({ errorResponse: resp.attributes[0].value, err });
|
||||
}
|
||||
}
|
||||
|
||||
connection.log.debug({ src: 'c', msg: breaker, comment: `Error response for ${command}` });
|
||||
connection.write(breaker);
|
||||
}
|
||||
}
|
||||
);
|
||||
response.next();
|
||||
|
||||
connection.authCapabilities.set(`AUTH=${command}`, true);
|
||||
|
||||
return username;
|
||||
} catch (err) {
|
||||
let errorCode = getStatusCode(err.response);
|
||||
if (errorCode) {
|
||||
err.serverResponseCode = errorCode;
|
||||
}
|
||||
err.authenticationFailed = true;
|
||||
err.response = await getErrorText(err.response);
|
||||
if (errorResponse) {
|
||||
err.oauthError = errorResponse;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function authLogin(connection, username, password) {
|
||||
let errorResponse = false;
|
||||
try {
|
||||
let response = await connection.exec('AUTHENTICATE', [{ type: 'ATOM', value: 'LOGIN' }], {
|
||||
onPlusTag: async resp => {
|
||||
if (resp.attributes && resp.attributes[0] && resp.attributes[0].type === 'TEXT') {
|
||||
let question = Buffer.from(resp.attributes[0].value, 'base64').toString();
|
||||
switch (
|
||||
question.toLowerCase().replace(/[:\x00]*$/, '') // eslint-disable-line no-control-regex
|
||||
) {
|
||||
case 'username':
|
||||
case 'user name': {
|
||||
let encodedUsername = Buffer.from(username).toString('base64');
|
||||
connection.log.debug({ src: 'c', msg: encodedUsername, comment: `Encoded username for AUTH=LOGIN` });
|
||||
connection.write(encodedUsername);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'password':
|
||||
connection.log.debug({ src: 'c', msg: '(* value hidden *)', comment: `Encoded password for AUTH=LOGIN` });
|
||||
connection.write(Buffer.from(password).toString('base64'));
|
||||
break;
|
||||
|
||||
default: {
|
||||
let error = new Error(`Unknown LOGIN question "${question}"`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
response.next();
|
||||
|
||||
connection.authCapabilities.set(`AUTH=LOGIN`, true);
|
||||
|
||||
return username;
|
||||
} catch (err) {
|
||||
let errorCode = getStatusCode(err.response);
|
||||
if (errorCode) {
|
||||
err.serverResponseCode = errorCode;
|
||||
}
|
||||
err.authenticationFailed = true;
|
||||
err.response = await getErrorText(err.response);
|
||||
if (errorResponse) {
|
||||
err.oauthError = errorResponse;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function authPlain(connection, username, password, authzid) {
|
||||
let errorResponse = false;
|
||||
try {
|
||||
let response = await connection.exec('AUTHENTICATE', [{ type: 'ATOM', value: 'PLAIN' }], {
|
||||
onPlusTag: async () => {
|
||||
// SASL PLAIN format: [authzid]\x00authcid\x00password
|
||||
// authzid: authorization identity (who to impersonate)
|
||||
// authcid: authentication identity (who is authenticating)
|
||||
let authzidValue = authzid || '';
|
||||
let encodedResponse = Buffer.from([authzidValue, username, password].join('\x00')).toString('base64');
|
||||
let loggedResponse = Buffer.from([authzidValue, username, '(* value hidden *)'].join('\x00')).toString('base64');
|
||||
connection.log.debug({ src: 'c', msg: loggedResponse, comment: `Encoded response for AUTH=PLAIN${authzid ? ' with authzid' : ''}` });
|
||||
connection.write(encodedResponse);
|
||||
}
|
||||
});
|
||||
|
||||
response.next();
|
||||
|
||||
connection.authCapabilities.set(`AUTH=PLAIN`, true);
|
||||
|
||||
// Return the identity we're authorized as (authzid if provided, otherwise username)
|
||||
return authzid || username;
|
||||
} catch (err) {
|
||||
let errorCode = getStatusCode(err.response);
|
||||
if (errorCode) {
|
||||
err.serverResponseCode = errorCode;
|
||||
}
|
||||
err.authenticationFailed = true;
|
||||
err.response = await getErrorText(err.response);
|
||||
if (errorResponse) {
|
||||
err.oauthError = errorResponse;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Authenticates user using LOGIN
|
||||
module.exports = async (connection, username, { accessToken, password, loginMethod, authzid }) => {
|
||||
if (connection.state !== connection.states.NOT_AUTHENTICATED) {
|
||||
// nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
if (accessToken) {
|
||||
// AUTH=OAUTHBEARER and AUTH=XOAUTH in the context of OAuth2 or very similar so we can handle these together
|
||||
if (connection.capabilities.has('AUTH=OAUTHBEARER') || connection.capabilities.has('AUTH=XOAUTH') || connection.capabilities.has('AUTH=XOAUTH2')) {
|
||||
return await authOauth(connection, username, accessToken);
|
||||
}
|
||||
}
|
||||
|
||||
if (password) {
|
||||
if ((!loginMethod && connection.capabilities.has('AUTH=PLAIN')) || loginMethod === 'AUTH=PLAIN') {
|
||||
return await authPlain(connection, username, password, authzid);
|
||||
}
|
||||
|
||||
if ((!loginMethod && connection.capabilities.has('AUTH=LOGIN')) || loginMethod === 'AUTH=LOGIN') {
|
||||
return await authLogin(connection, username, password);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Unsupported authentication mechanism');
|
||||
};
|
||||
Reference in New Issue
Block a user