Skip to content

Commit ee0f189

Browse files
kerhacgitster
authored andcommitted
send-email: implement SMTP bearer authentication
Manually send SMTP AUTH command for auth type OAUTHBEARER and XOAUTH2. This is necessary since they are currently not supported by the Perls Authen::SASL module. The bearer token needs to be passed in as the password. This can be done with git-credential-oauth[0] after minor modifications[1]. Which will allow using git send-email with Gmail and oauth2 authentication: [credential] helper = cache --timeout 7200 # two hours helper = oauth [sendemail] smtpEncryption = tls smtpServer = smtp.gmail.com smtpUser = [email protected] smtpServerPort = 587 smtpauth = OAUTHBEARER As well as Office 365 accounts: [credential] helper = cache --timeout 7200 # two hours helper = oauth [sendemail] smtpEncryption = tls smtpServer = smtp.office365.com smtpUser = [email protected] smtpServerPort = 587 smtpauth = XOAUTH2 [0] https://github.com/hickford/git-credential-oauth [1] hickford/git-credential-oauth#48 Tested-by: M Hickford <[email protected]> Signed-off-by: Julian Swagemakers <[email protected]> Signed-off-by: Aditya Garg <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 4bbb303 commit ee0f189

File tree

2 files changed

+67
-2
lines changed

2 files changed

+67
-2
lines changed

Documentation/git-send-email.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,10 @@ SMTP server and if it is supported by the utilized SASL library, the mechanism
213213
is used for authentication. If neither 'sendemail.smtpAuth' nor `--smtp-auth`
214214
is specified, all mechanisms supported by the SASL library can be used. The
215215
special value 'none' maybe specified to completely disable authentication
216-
independently of `--smtp-user`
216+
independently of `--smtp-user`. Specifying `OAUTHBEARER` or `XOAUTH2` will
217+
bypass SASL negotiation and force bearer authentication. In this case the
218+
bearer token must be provided with `--smtp-pass` or using a credential helper
219+
and `--smtp-encryption=tls` must be set.
217220

218221
--smtp-pass[=<password>]::
219222
Password for SMTP-AUTH. The argument is optional: If no

git-send-email.perl

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1398,6 +1398,63 @@ sub smtp_host_string {
13981398
}
13991399
}
14001400

1401+
sub generate_oauthbearer_string {
1402+
# This will generate the oauthbearer string used for authentication.
1403+
#
1404+
# "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
1405+
#
1406+
# The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
1407+
# * gs2-cb-flag `n` -> client does not support CB
1408+
# * gs2-authzid `a=" {User} "`
1409+
#
1410+
# The second part are key value pairs containing host, port and auth as
1411+
# described in RFC7628.
1412+
#
1413+
# https://datatracker.ietf.org/doc/html/rfc5801
1414+
# https://datatracker.ietf.org/doc/html/rfc7628
1415+
my $username = shift;
1416+
my $token = shift;
1417+
return "n,a=$username,\001port=$smtp_server_port\001auth=Bearer $token\001\001";
1418+
}
1419+
1420+
sub generate_xoauth2_string {
1421+
# "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
1422+
# https://developers.google.com/gmail/imap/xoauth2-protocol#initial_client_response
1423+
my $username = shift;
1424+
my $token = shift;
1425+
return "user=$username\001auth=Bearer $token\001\001";
1426+
}
1427+
1428+
sub smtp_bearer_auth {
1429+
my $username = shift;
1430+
my $token = shift;
1431+
my $auth_string;
1432+
if ($smtp_encryption ne "tls") {
1433+
# As described in RFC7628 TLS is required and will be enforced
1434+
# at this point.
1435+
#
1436+
# https://datatracker.ietf.org/doc/html/rfc7628#section-3
1437+
die sprintf(__("For %s TLS is required."), $smtp_auth);
1438+
}
1439+
if ($smtp_auth eq "OAUTHBEARER") {
1440+
$auth_string = generate_oauthbearer_string($username, $token);
1441+
} elsif ($smtp_auth eq "XOAUTH2") {
1442+
$auth_string = generate_xoauth2_string($username, $token);
1443+
}
1444+
my $encoded_auth_string = MIME::Base64::encode($auth_string, "");
1445+
$smtp->command("AUTH $smtp_auth $encoded_auth_string\r\n");
1446+
use Net::Cmd qw(CMD_OK);
1447+
if ($smtp->response() == CMD_OK){
1448+
return 1;
1449+
} else {
1450+
# Send dummy request on authentication failure according to rfc7628.
1451+
# https://datatracker.ietf.org/doc/html/rfc7628#section-3.2.3
1452+
$smtp->command(MIME::Base64::encode("\001"));
1453+
$smtp->response();
1454+
return 0;
1455+
}
1456+
}
1457+
14011458
# Returns 1 if authentication succeeded or was not necessary
14021459
# (smtp_user was not specified), and 0 otherwise.
14031460

@@ -1436,7 +1493,12 @@ sub smtp_auth_maybe {
14361493

14371494
# catch all SMTP auth error in a unified eval block
14381495
eval {
1439-
if ($smtp_auth) {
1496+
if (defined $smtp_auth && ($smtp_auth eq "OAUTHBEARER" || $smtp_auth eq "XOAUTH2")) {
1497+
# Since Authen:SASL does not support XOAUTH2 nor OAUTHBEARER we will
1498+
# manually authenticate for these types. The password field should
1499+
# contain the auth token at this point.
1500+
$result = smtp_bearer_auth($cred->{'username'}, $cred->{'password'});
1501+
} elsif ($smtp_auth) {
14401502
my $sasl = Authen::SASL->new(
14411503
mechanism => $smtp_auth,
14421504
callback => {

0 commit comments

Comments
 (0)