Skip to content

Commit 57c969e

Browse files
add support of OAUTHBEARER (#79)
* add support of OAUTHBEARER Signed-off-by: Edouard Vanbelle <edouard@vanbelle.fr> * add OAUTH pseudo method * OAUTH pseudo method will elect either XOAUTH2 or OAUTHBEARER according to server's capabilities Signed-off-by: Edouard Vanbelle <edouard@vanbelle.fr> --------- Signed-off-by: Edouard Vanbelle <edouard@vanbelle.fr>
1 parent 0ae529d commit 57c969e

File tree

1 file changed

+63
-9
lines changed

1 file changed

+63
-9
lines changed

Net/SMTP.php

+63-9
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ public function __construct($host = null, $port = null, $localhost = null,
233233
$this->setAuthMethod('LOGIN', array($this, 'authLogin'), false);
234234
$this->setAuthMethod('PLAIN', array($this, 'authPlain'), false);
235235
$this->setAuthMethod('XOAUTH2', array($this, 'authXOAuth2'), false);
236+
$this->setAuthMethod('OAUTHBEARER', array($this, 'authOAuthBearer'), false);
236237
}
237238

238239
/**
@@ -708,14 +709,17 @@ public function starttls()
708709

709710
return true;
710711
}
711-
712+
712713
/**
713714
* Attempt to do SMTP authentication.
714715
*
715716
* @param string $uid The userid to authenticate as.
716717
* @param string $pwd The password to authenticate with.
717-
* @param string $method The requested authentication method. If none is
718+
* @param string $method The requested authentication method. If none is
718719
* specified, the best supported method will be used.
720+
* If you use the special method `OAUTH`, library
721+
* will choose between OAUTHBEARER or XOAUTH2
722+
* according the server's capabilities.
719723
* @param bool $tls Flag indicating whether or not TLS should be attempted.
720724
* @param string $authz An optional authorization identifier. If specified, this
721725
* identifier will be used as the authorization proxy.
@@ -749,6 +753,19 @@ public function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
749753
/* Return the PEAR_Error object from _getBestAuthMethod(). */
750754
return $method;
751755
}
756+
} elseif ($method === 'OAUTH') {
757+
// special case of OAUTH, use the supported method
758+
$found = false;
759+
$available_methods = explode(' ', $this->esmtp['AUTH']);
760+
foreach (['OAUTHBEARER', 'XOAUTH2'] as $method) {
761+
if (in_array($method, $available_methods)) {
762+
$found = true;
763+
break;
764+
}
765+
}
766+
if (!$found) {
767+
return PEAR::raiseError("neither OAUTHBEARER nor XOAUTH2 is a supported authentication method");
768+
}
752769
} else {
753770
$method = strtoupper($method);
754771
if (!array_key_exists($method, $this->auth_methods)) {
@@ -1101,28 +1118,65 @@ protected function authGSSAPI($uid, $pwd, $authz = '')
11011118
* Authenticates the user using the XOAUTH2 method.
11021119
*
11031120
* @param string $uid The userid to authenticate as.
1104-
* @param string $token The access token to authenticate with.
1121+
* @param string $token The access token prefixed by it's type
1122+
* example: "Bearer $access_token".
11051123
* @param string $authz The optional authorization proxy identifier.
11061124
* @param object $conn The current object
11071125
*
11081126
* @return mixed Returns a PEAR_Error with an error message on any
11091127
* kind of failure, or true on success.
11101128
* @since 1.9.0
11111129
*/
1130+
//FIXME: to switch into protected method on next major release
11121131
public function authXOAuth2($uid, $token, $authz, $conn)
11131132
{
11141133
$auth = base64_encode("user=$uid\1auth=$token\1\1");
1134+
return $this->authenticateOAuth('XOAUTH2', $auth, $authz, $conn);
1135+
}
11151136

1116-
// Maximum length of the base64-encoded token to be sent in the initial response is 497 bytes, according to
1117-
// RFC 4954 (https://datatracker.ietf.org/doc/html/rfc4954); for longer tokens an empty initial
1137+
/**
1138+
* Authenticates the user using the OAUTHBEARER method.
1139+
*
1140+
* @param string $uid The userid to authenticate as.
1141+
* @param string $token The access token prefixed by it's type
1142+
* example: "Bearer $access_token".
1143+
* @param string $authz The optional authorization proxy identifier.
1144+
* @param object $conn The current object
1145+
*
1146+
* @return mixed Returns a PEAR_Error with an error message on any
1147+
* kind of failure, or true on success.
1148+
* @since 1.9.3
1149+
* @see https://www.rfc-editor.org/rfc/rfc7628.html
1150+
*/
1151+
protected function authOAuthBearer($uid, $token, $authz, $conn)
1152+
{
1153+
$auth = base64_encode("n,a=$uid\1auth=$token\1\1");
1154+
return $this->authenticateOAuth('OAUTHBEARER', $auth, $authz, $conn);
1155+
}
1156+
1157+
/**
1158+
* Authenticates the user using the OAUTHBEARER or XOAUTH2 method.
1159+
*
1160+
* @param string $method The method (OAUTHBEARER or XOAUTH2)
1161+
* @param string $auth The authentication string (base64 coded)
1162+
* @param string $authz The optional authorization proxy identifier.
1163+
* @param object $conn The current object
1164+
*
1165+
* @return mixed Returns a PEAR_Error with an error message on any
1166+
* kind of failure, or true on success.
1167+
*/
1168+
protected function authenticateOAuth( $method, $auth, $authz, $conn)
1169+
{
1170+
// Maximum length of the base64-encoded token to be sent in the initial response is 504 - strlen($method) bytes,
1171+
// according to RFC 4954 (https://datatracker.ietf.org/doc/html/rfc4954); for longer tokens an empty initial
11181172
// response MUST be sent and the token must be sent separately
1119-
// (497 bytes = 512 bytes /SMTP command length limit/ - 13 bytes /"AUTH XOAUTH2 "/ - 2 bytes /CRLF/)
1120-
if (strlen($auth) <= 497) {
1121-
if (PEAR::isError($error = $this->put('AUTH', 'XOAUTH2 ' . $auth))) {
1173+
// (504 bytes = /SMTP command length limit/ - 6 bytes /"AUTH "/ -strlen($method) - 1 byte /" "/ - 2 bytes /CRLF/)
1174+
if (strlen($auth) <= (504-strlen($method))) {
1175+
if (PEAR::isError($error = $this->put('AUTH', $method . ' ' . $auth))) {
11221176
return $error;
11231177
}
11241178
} else {
1125-
if (PEAR::isError($error = $this->put('AUTH', 'XOAUTH2'))) {
1179+
if (PEAR::isError($error = $this->put('AUTH', $method))) {
11261180
return $error;
11271181
}
11281182

0 commit comments

Comments
 (0)