Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support of OAUTHBEARER #79

Merged
merged 2 commits into from
Feb 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 63 additions & 9 deletions Net/SMTP.php
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ public function __construct($host = null, $port = null, $localhost = null,
$this->setAuthMethod('LOGIN', array($this, 'authLogin'), false);
$this->setAuthMethod('PLAIN', array($this, 'authPlain'), false);
$this->setAuthMethod('XOAUTH2', array($this, 'authXOAuth2'), false);
$this->setAuthMethod('OAUTHBEARER', array($this, 'authOAuthBearer'), false);
}

/**
Expand Down Expand Up @@ -708,14 +709,17 @@ public function starttls()

return true;
}

/**
* Attempt to do SMTP authentication.
*
* @param string $uid The userid to authenticate as.
* @param string $pwd The password to authenticate with.
* @param string $method The requested authentication method. If none is
* @param string $method The requested authentication method. If none is
* specified, the best supported method will be used.
* If you use the special method `OAUTH`, library
* will choose between OAUTHBEARER or XOAUTH2
* according the server's capabilities.
* @param bool $tls Flag indicating whether or not TLS should be attempted.
* @param string $authz An optional authorization identifier. If specified, this
* identifier will be used as the authorization proxy.
Expand Down Expand Up @@ -749,6 +753,19 @@ public function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
/* Return the PEAR_Error object from _getBestAuthMethod(). */
return $method;
}
} elseif ($method === 'OAUTH') {
// special case of OAUTH, use the supported method
$found = false;
$available_methods = explode(' ', $this->esmtp['AUTH']);
foreach (['OAUTHBEARER', 'XOAUTH2'] as $method) {
if (in_array($method, $available_methods)) {
$found = true;
break;
}
}
EdouardVanbelle marked this conversation as resolved.
Show resolved Hide resolved
if (!$found) {
return PEAR::raiseError("neither OAUTHBEARER nor XOAUTH2 is a supported authentication method");
}
} else {
$method = strtoupper($method);
if (!array_key_exists($method, $this->auth_methods)) {
Expand Down Expand Up @@ -1101,28 +1118,65 @@ protected function authGSSAPI($uid, $pwd, $authz = '')
* Authenticates the user using the XOAUTH2 method.
*
* @param string $uid The userid to authenticate as.
* @param string $token The access token to authenticate with.
* @param string $token The access token prefixed by it's type
* example: "Bearer $access_token".
* @param string $authz The optional authorization proxy identifier.
* @param object $conn The current object
*
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
* @since 1.9.0
*/
//FIXME: to switch into protected method on next major release
public function authXOAuth2($uid, $token, $authz, $conn)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All auth* methods are protected, so this one should too, I suppose.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not touch on previous methods already in place, I can correct it to move it into protected, no clue if it is already used outside of this library, I don't think so

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I corrected it in favor of a protected method
@schengawegga let me know your preferences

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My preference is like @alecpl, to protect all auth* methods, to be straight on this kind of methods.
But, i as you said @EdouardVanbelle, if anybody uses this method from outside, it will break backwards compatibility, and have to be released on a new major version. I also do not think, that anybody uses this from outside, but it is possible.

@alecpl @jparise what do you think about that? can we do a bugfix to change authXOAuth2 method to protected without releasing this under a new major version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While waiting any feedback I kept the original signature and added a FIXME in comment

{
$auth = base64_encode("user=$uid\1auth=$token\1\1");
return $this->authenticateOAuth('XOAUTH2', $auth, $authz, $conn);
}

// Maximum length of the base64-encoded token to be sent in the initial response is 497 bytes, according to
// RFC 4954 (https://datatracker.ietf.org/doc/html/rfc4954); for longer tokens an empty initial
/**
* Authenticates the user using the OAUTHBEARER method.
*
* @param string $uid The userid to authenticate as.
* @param string $token The access token prefixed by it's type
* example: "Bearer $access_token".
* @param string $authz The optional authorization proxy identifier.
* @param object $conn The current object
*
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
* @since 1.9.3
* @see https://www.rfc-editor.org/rfc/rfc7628.html
*/
protected function authOAuthBearer($uid, $token, $authz, $conn)
{
$auth = base64_encode("n,a=$uid\1auth=$token\1\1");
EdouardVanbelle marked this conversation as resolved.
Show resolved Hide resolved
return $this->authenticateOAuth('OAUTHBEARER', $auth, $authz, $conn);
}

/**
* Authenticates the user using the OAUTHBEARER or XOAUTH2 method.
*
* @param string $method The method (OAUTHBEARER or XOAUTH2)
* @param string $auth The authentication string (base64 coded)
* @param string $authz The optional authorization proxy identifier.
* @param object $conn The current object
*
* @return mixed Returns a PEAR_Error with an error message on any
* kind of failure, or true on success.
*/
protected function authenticateOAuth( $method, $auth, $authz, $conn)
{
// Maximum length of the base64-encoded token to be sent in the initial response is 504 - strlen($method) bytes,
// according to RFC 4954 (https://datatracker.ietf.org/doc/html/rfc4954); for longer tokens an empty initial
// response MUST be sent and the token must be sent separately
// (497 bytes = 512 bytes /SMTP command length limit/ - 13 bytes /"AUTH XOAUTH2 "/ - 2 bytes /CRLF/)
if (strlen($auth) <= 497) {
if (PEAR::isError($error = $this->put('AUTH', 'XOAUTH2 ' . $auth))) {
// (504 bytes = /SMTP command length limit/ - 6 bytes /"AUTH "/ -strlen($method) - 1 byte /" "/ - 2 bytes /CRLF/)
if (strlen($auth) <= (504-strlen($method))) {
if (PEAR::isError($error = $this->put('AUTH', $method . ' ' . $auth))) {
return $error;
}
} else {
if (PEAR::isError($error = $this->put('AUTH', 'XOAUTH2'))) {
if (PEAR::isError($error = $this->put('AUTH', $method))) {
return $error;
}

Expand Down
Loading