{"thread":{"id":"62791","subject":"Re: [PATCH] send-email: implement SMTP bearer authentication","startedAt":"2025-01-11T21:27:34Z","lastAt":"2025-01-11T21:27:34Z","messageCount":1,"participants":["M Hickford"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"510375","messageId":"20250111212701.723710-1-mirth.hickford@gmail.com","threadId":"62791","inReplyTo":null,"subject":"Re: [PATCH] send-email: implement SMTP bearer authentication","fromName":"M Hickford","fromEmail":"mirth.hickford@gmail.com","sentAt":"2025-01-11T21:27:01Z","receivedAt":"2025-01-11T21:27:34Z","isPatch":true,"sender":{"key":"mirth.hickford@gmail.com","avatar":"https://avatars.githubusercontent.com/u/105314?v=4"},"body":"> Manually send SMTP AUTH command for auth type OAUTHBEARER and XOAUTH2.\n> This is necessary since they are currently not supported by the Perls\n> Authen::SASL module.\n> \n> The bearer token needs to be passed in as the password. This can be done\n\nPlease add documentation to git-send-email.txt\n\n> with git-credential-oauth[0] after minor modifications[1]. Which will\n> allow using git send-email with Gmail and oauth2 authentication:\n> \n> ```\n> [credential]\n> \thelper = cache --timeout 7200\t# two hours\n> \thelper = oauth\n> [sendemail]\n>     smtpEncryption = tls\n>     smtpServer = smtp.gmail.com\n>     smtpUser = example@gmail.com\n>     smtpServerPort = 587\n>     smtpauth = OAUTHBEARER\n> ```\n> \n> As well as Office 365 accounts:\n> \n> ```\n> [credential]\n> \thelper = cache --timeout 7200\t# two hours\n> \thelper = oauth\n> [sendemail]\n>     smtpEncryption = tls\n>     smtpServer = smtp.office365.com\n>     smtpUser = example@example.com\n>     smtpServerPort = 587\n>     smtpauth = XOAUTH2\n> ```\n> \n> [0] https://github.com/hickford/git-credential-oauth\n> [1] https://github.com/hickford/git-credential-oauth/issues/48\n> \n> Signed-off-by: Julian Swagemakers <julian@swagemakers.org>\n> ---\n>  git-send-email.perl | 65 +++++++++++++++++++++++++++++++++++++++++++--\n>  1 file changed, 63 insertions(+), 2 deletions(-)\n> \n> diff --git a/git-send-email.perl b/git-send-email.perl\n> index 821b2b3a13..72d378f6fd 100755\n> --- a/git-send-email.perl\n> +++ b/git-send-email.perl\n> @@ -1359,6 +1359,63 @@ sub smtp_host_string {\n>  \t}\n>  }\n>  \n> +sub generate_oauthbearer_string {\n> +\t# This will generate the oauthbearer string used for authentication.\n> +\t#\n> +\t# \"n,a=\" {User} \",^Ahost=\" {Host} \"^Aport=\" {Port} \"^Aauth=Bearer \" {Access Token} \"^A^A\n> +\t#\n> +\t# The first part `n,a=\" {User} \",` is the gs2 header described in RFC5801.\n> +\t# * gs2-cb-flag `n` -> client does not support CB\n> +\t# * gs2-authzid `a=\" {User} \"`\n> +\t#\n> +\t# The second part are key value pairs containing host, port and auth as\n> +\t# described in RFC7628.\n> +\t#\n> +\t# https://datatracker.ietf.org/doc/html/rfc5801\n> +\t# https://datatracker.ietf.org/doc/html/rfc7628\n> +\tmy $username = shift;\n> +\tmy $token = shift;\n> +\treturn \"n,a=$username,\\001port=$smtp_server_port\\001auth=Bearer $token\\001\\001\";\n> +}\n> +\n> +sub generate_xoauth2_string {\n> +\t# \"user=\" {User} \"^Aauth=Bearer \" {Access Token} \"^A^A\"\n> +\t# https://developers.google.com/gmail/imap/xoauth2-protocol#initial_client_response\n> +\tmy $username = shift;\n> +\tmy $token = shift;\n> +\treturn \"user=$username\\001auth=Bearer $token\\001\\001\";\n> +}\n> +\n> +sub smtp_bearer_auth {\n> +\tmy $username = shift;\n> +\tmy $token = shift;\n> +\tmy $auth_string;\n> +\tif ($smtp_encryption ne \"tls\") {\n> +\t\t# As described in RFC7628 TLS is required and will be will\n> +\t\t# be enforced at this point.\n> +\t\t#\n> +\t\t# https://datatracker.ietf.org/doc/html/rfc7628#section-3\n> +\t\tdie __(\"For $smtp_auth TLS is required.\")\n> +\t}\n> +\tif ($smtp_auth eq \"OAUTHBEARER\") {\n> +\t\t$auth_string = generate_oauthbearer_string($username, $token);\n> +\t} elsif ($smtp_auth eq \"XOAUTH2\") {\n> +\t\t$auth_string = generate_xoauth2_string($username, $token);\n> +\t}\n> +\tmy $encoded_auth_string = MIME::Base64::encode($auth_string, \"\");\n> +\t$smtp->command(\"AUTH $smtp_auth $encoded_auth_string\\r\\n\");\n> +\tuse Net::Cmd qw(CMD_OK);\n> +\tif ($smtp->response() == CMD_OK){\n> +\t\treturn 1;\n> +\t} else {\n> +\t\t# Send dummy request on authentication failure according to rfc7628.\n> +\t\t# https://datatracker.ietf.org/doc/html/rfc7628#section-3.2.3\n> +\t\t$smtp->command(MIME::Base64::encode(\"\\001\"));\n> +\t\t$smtp->response();\n> +\t\treturn 0;\n> +\t}\n> +}\n> +\n>  # Returns 1 if authentication succeeded or was not necessary\n>  # (smtp_user was not specified), and 0 otherwise.\n>  \n> @@ -1392,8 +1449,12 @@ sub smtp_auth_maybe {\n>  \t\t'password' => $smtp_authpass\n>  \t}, sub {\n>  \t\tmy $cred = shift;\n> -\n> -\t\tif ($smtp_auth) {\n> +\t\tif ($smtp_auth eq \"OAUTHBEARER\" or $smtp_auth eq \"XOAUTH2\") {\n\nBe careful to check that $smtp_auth is initialized to avoid warnings:\n\nUse of uninitialized value $smtp_auth in string eq at /home/matt/libexec/git-core/git-send-email line 1493.\nUse of uninitialized value $smtp_auth in string eq at /home/matt/libexec/git-core/git-send-email line 1493.\n\n> +\t\t\t# Since Authen:SASL does not support XOAUTH2 nor OAUTHBEARER we will\n> +\t\t\t# manuall authenticate for tese types. The password field should\n> +\t\t\t# contain the auth token at this point.\n> +\t\t\treturn smtp_bearer_auth($cred->{'username'}, $cred->{'password'});\n> +\t\t} elsif ($smtp_auth) {\n>  \t\t\tmy $sasl = Authen::SASL->new(\n>  \t\t\t\tmechanism => $smtp_auth,\n>  \t\t\t\tcallback => {\n"}]}