melib/smtp: implement gmail XOAUTH2 authentication method
parent
4914f29e20
commit
453bb0b2b2
|
@ -1132,7 +1132,7 @@ subsection
|
||||||
.El
|
.El
|
||||||
.Ss SmtpAuth
|
.Ss SmtpAuth
|
||||||
.Bl -tag -width 36n
|
.Bl -tag -width 36n
|
||||||
.It Ic type Ar "none" | "auto"
|
.It Ic type Ar "none" | "auto" | "xoauth2"
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
For type "auto":
|
For type "auto":
|
||||||
|
@ -1146,6 +1146,17 @@ require authentication in every case
|
||||||
.Pq Em true
|
.Pq Em true
|
||||||
.El
|
.El
|
||||||
.sp
|
.sp
|
||||||
|
For type "xoauth2":
|
||||||
|
.Bl -tag -width 36n
|
||||||
|
.It Ic token_command Ar String
|
||||||
|
Command to evaluate that returns an XOAUTH2 token.
|
||||||
|
.It Ic require_auth Ar bool
|
||||||
|
.Pq Em optional
|
||||||
|
require authentication in every case
|
||||||
|
.\" default value
|
||||||
|
.Pq Em true
|
||||||
|
.El
|
||||||
|
.sp
|
||||||
Examples:
|
Examples:
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
auth = { type = "auto", username = "user", password = { type = "raw", value = "hunter2" } }
|
auth = { type = "auto", username = "user", password = { type = "raw", value = "hunter2" } }
|
||||||
|
@ -1156,6 +1167,13 @@ auth = { type = "auto", username = "user", password = "hunter2" }
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
auth = { type = "none" }
|
auth = { type = "none" }
|
||||||
.Ed
|
.Ed
|
||||||
|
.sp
|
||||||
|
For Gmail (see
|
||||||
|
.Sx Gmail OAUTH2
|
||||||
|
for details on the authentication token command):
|
||||||
|
.Bd -literal
|
||||||
|
auth = { type = "xoauth2", token_command = "TOKEN=$(python3 oauth2.py --user=xxx@gmail.com --quiet --client_id=1038[...].apps.googleusercontent.com --client_secret=[..] --refresh_token=[..] && python3 oauth2.py --user=xxx@gmail.com --generate_oauth2_string --quiet --access_token=$TOKEN" }
|
||||||
|
.Ed
|
||||||
.Ss SmtpPassword
|
.Ss SmtpPassword
|
||||||
.Bl -tag -width 36n
|
.Bl -tag -width 36n
|
||||||
.It Ic type Ar "raw" | "command_evaluation"
|
.It Ic type Ar "raw" | "command_evaluation"
|
||||||
|
|
|
@ -140,6 +140,12 @@ pub enum SmtpAuth {
|
||||||
#[serde(skip_serializing, skip_deserializing, default)]
|
#[serde(skip_serializing, skip_deserializing, default)]
|
||||||
auth_type: SmtpAuthType,
|
auth_type: SmtpAuthType,
|
||||||
},
|
},
|
||||||
|
#[serde(alias = "xoauth2")]
|
||||||
|
XOAuth2 {
|
||||||
|
token_command: String,
|
||||||
|
#[serde(default = "true_val")]
|
||||||
|
require_auth: bool,
|
||||||
|
},
|
||||||
// md5, sasl, etc
|
// md5, sasl, etc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,7 +168,7 @@ impl SmtpAuth {
|
||||||
use SmtpAuth::*;
|
use SmtpAuth::*;
|
||||||
match self {
|
match self {
|
||||||
None => false,
|
None => false,
|
||||||
Auto { require_auth, .. } => *require_auth,
|
Auto { require_auth, .. } | XOAuth2 { require_auth, .. } => *require_auth,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,6 +511,39 @@ impl SmtpConnection {
|
||||||
.chain_err_kind(crate::error::ErrorKind::Authentication)?;
|
.chain_err_kind(crate::error::ErrorKind::Authentication)?;
|
||||||
ret.send_command(&[b"EHLO meli.delivery"]).await?;
|
ret.send_command(&[b"EHLO meli.delivery"]).await?;
|
||||||
}
|
}
|
||||||
|
SmtpAuth::XOAuth2 { token_command, .. } => {
|
||||||
|
let password_token = {
|
||||||
|
let _token_command = token_command.clone();
|
||||||
|
let mut output = unblock(move || {
|
||||||
|
Command::new("sh")
|
||||||
|
.args(&["-c", &_token_command])
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.output()
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
if !output.status.success() {
|
||||||
|
return Err(MeliError::new(format!(
|
||||||
|
"SMTP XOAUTH2 token evaluation command `{}` returned {}: {}",
|
||||||
|
&token_command,
|
||||||
|
output.status,
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if output.stdout.ends_with(b"\n") {
|
||||||
|
output.stdout.pop();
|
||||||
|
}
|
||||||
|
output.stdout
|
||||||
|
};
|
||||||
|
// https://developers.google.com/gmail/imap/xoauth2-protocol#smtp_protocol_exchange
|
||||||
|
ret.send_command(&[b"AUTH XOAUTH2 ", &password_token])
|
||||||
|
.await?;
|
||||||
|
ret.read_lines(&mut res, Some((ReplyCode::_235, &[])))
|
||||||
|
.await
|
||||||
|
.chain_err_kind(crate::error::ErrorKind::Authentication)?;
|
||||||
|
ret.send_command(&[b"EHLO meli.delivery"]).await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
let extensions_reply = ret
|
let extensions_reply = ret
|
||||||
|
@ -966,6 +1005,9 @@ async fn read_lines<'r>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ret.len() < 3 {
|
||||||
|
return Err(MeliError::new(format!("Invalid SMTP reply: {}", ret)));
|
||||||
|
}
|
||||||
let code = ReplyCode::try_from(&ret[..3])?;
|
let code = ReplyCode::try_from(&ret[..3])?;
|
||||||
let reply = Reply::new(ret, code);
|
let reply = Reply::new(ret, code);
|
||||||
//debug!(&reply);
|
//debug!(&reply);
|
||||||
|
|
Loading…
Reference in New Issue