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 = example@xxxxxxxxx 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 = example@xxxxxxxxxxx smtpServerPort = 587 smtpauth = XOAUTH2 ``` [0] https://github.com/hickford/git-credential-oauth [1] https://github.com/hickford/git-credential-oauth/issues/48 Signed-off-by: Julian Swagemakers <julian@xxxxxxxxxxxxxxx> --- git-send-email.perl | 65 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/git-send-email.perl b/git-send-email.perl index 821b2b3a13..72d378f6fd 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -1359,6 +1359,63 @@ sub smtp_host_string { } } +sub generate_oauthbearer_string { + # This will generate the oauthbearer string used for authentication. + # + # "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A + # + # The first part `n,a=" {User} ",` is the gs2 header described in RFC5801. + # * gs2-cb-flag `n` -> client does not support CB + # * gs2-authzid `a=" {User} "` + # + # The second part are key value pairs containing host, port and auth as + # described in RFC7628. + # + # https://datatracker.ietf.org/doc/html/rfc5801 + # https://datatracker.ietf.org/doc/html/rfc7628 + my $username = shift; + my $token = shift; + return "n,a=$username,\001port=$smtp_server_port\001auth=Bearer $token\001\001"; +} + +sub generate_xoauth2_string { + # "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A" + # https://developers.google.com/gmail/imap/xoauth2-protocol#initial_client_response + my $username = shift; + my $token = shift; + return "user=$username\001auth=Bearer $token\001\001"; +} + +sub smtp_bearer_auth { + my $username = shift; + my $token = shift; + my $auth_string; + if ($smtp_encryption ne "tls") { + # As described in RFC7628 TLS is required and will be will + # be enforced at this point. + # + # https://datatracker.ietf.org/doc/html/rfc7628#section-3 + die __("For $smtp_auth TLS is required.") + } + if ($smtp_auth eq "OAUTHBEARER") { + $auth_string = generate_oauthbearer_string($username, $token); + } elsif ($smtp_auth eq "XOAUTH2") { + $auth_string = generate_xoauth2_string($username, $token); + } + my $encoded_auth_string = MIME::Base64::encode($auth_string, ""); + $smtp->command("AUTH $smtp_auth $encoded_auth_string\r\n"); + use Net::Cmd qw(CMD_OK); + if ($smtp->response() == CMD_OK){ + return 1; + } else { + # Send dummy request on authentication failure according to rfc7628. + # https://datatracker.ietf.org/doc/html/rfc7628#section-3.2.3 + $smtp->command(MIME::Base64::encode("\001")); + $smtp->response(); + return 0; + } +} + # Returns 1 if authentication succeeded or was not necessary # (smtp_user was not specified), and 0 otherwise. @@ -1392,8 +1449,12 @@ sub smtp_auth_maybe { 'password' => $smtp_authpass }, sub { my $cred = shift; - - if ($smtp_auth) { + if ($smtp_auth eq "OAUTHBEARER" or $smtp_auth eq "XOAUTH2") { + # Since Authen:SASL does not support XOAUTH2 nor OAUTHBEARER we will + # manuall authenticate for tese types. The password field should + # contain the auth token at this point. + return smtp_bearer_auth($cred->{'username'}, $cred->{'password'}); + } elsif ($smtp_auth) { my $sasl = Authen::SASL->new( mechanism => $smtp_auth, callback => { -- 2.43.2