mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-20 18:43:20 +00:00
933eaa2ba4
Adds "Microsoft 365 (OAuth)" to the Email settings provider dropdown. Selecting it swaps the SMTP form for an app registration (client id, secret, optional tenant) and a "Connect mailbox" flow: the admin signs into the mailbox the installation should send as, and outgoing email goes through Graph sendMail as that mailbox — no password, no app password, no SMTP AUTH, which Microsoft is winding down. Delegated flow on purpose: it needs no admin consent and works for work/school and personal accounts alike. Its one weakness — a grant can die silently behind a password reset or a Conditional Access change — is answered by a daily scheduled refresh that keeps the token alive and, on a dead grant, warns the settings admins once in-app and on the settings page instead of letting mail stop quietly. Tokens and the client secret live encrypted in their own row and are read fresh at send time, never through the boot-config cache. The stored SMTP transport survives a provider switch untouched.
48 lines
1.7 KiB
PHP
48 lines
1.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Modules\Platform\Mail;
|
|
|
|
/**
|
|
* One OAuth mail provider's token machinery: building the consent URL,
|
|
* turning the returned code into tokens, and keeping those tokens fresh.
|
|
*
|
|
* Deliberately not Socialite: a mail connection needs raw tokens with a
|
|
* send scope, not a user identity, and Socialite's user() call would
|
|
* drag in a userinfo permission (User.Read on Graph) that sending mail
|
|
* does not need. Implementations write their results straight onto the
|
|
* MailOAuthConnection row and save it.
|
|
*/
|
|
interface MailOAuthBroker
|
|
{
|
|
/** The provider consent URL the admin's browser is sent to. */
|
|
public function authorizeUrl(MailOAuthConnection $connection, string $state, string $redirectUri): string;
|
|
|
|
/**
|
|
* Exchange the callback's authorization code for tokens and record
|
|
* them, along with the connected mailbox's address, on the connection.
|
|
*
|
|
* @throws MailOAuthException
|
|
*/
|
|
public function exchange(MailOAuthConnection $connection, string $code, string $redirectUri): void;
|
|
|
|
/**
|
|
* Refresh the access token (rotating the refresh token when the
|
|
* provider hands back a new one) and record the outcome — including
|
|
* `last_error` on failure, so the settings page and the scheduled
|
|
* health check read one source of truth.
|
|
*
|
|
* @throws MailOAuthException
|
|
*/
|
|
public function refresh(MailOAuthConnection $connection): void;
|
|
|
|
/**
|
|
* An access token currently valid for at least a small safety margin,
|
|
* refreshing first when needed — what transports call at send time.
|
|
*
|
|
* @throws MailOAuthException
|
|
*/
|
|
public function freshAccessToken(MailOAuthConnection $connection): string;
|
|
}
|