-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Mailer] Improve oauth setup #52585
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
base: 7.4
Are you sure you want to change the base?
[Mailer] Improve oauth setup #52585
Conversation
Hey! Thanks for your PR. You are targeting branch "7.0" but it seems your PR description refers to branch "7.1". Cheers! Carsonbot |
foreach ($config['smtp']['authenticators'] ?? [] as $authenticator) { | ||
$authenticators[] = new Reference($authenticator); | ||
} | ||
$container->getDefinition('mailer.transport_factory.smtp')->setArgument(3, $authenticators); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to mailer_transport.php
this service is the EsmtpTransportFactory, but the CI tells that we somehow end up setting an argument to "Symfony\Component\Mailer\Mailer" - any idea what i am messing up here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the weird error message could be because of inlining the transport factory services. It might be inside one of the inlined services inside the mailer service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is that a bug of the DI container, or should i be doing something differently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not call it a bug. The hard thing is that inline definitions don't have an id. So that's hard to know how to mention them in error messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if it is not a bug, what do i need differently to set the parameter on the correct place?
src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
Outdated
Show resolved
Hide resolved
Did you consider adding the oauth token providen to core? Could this be done instead of the current extra interface? Is there an approach that wouldn't require adding this extra interface - or at least making |
oauth seems to be more of a concept than an actual standard. every provider is doing it differently. if symfony had a fully fledged oauth component with support for all the major players, we could maybe make this use that. but to me an interface is really the way to go here. oauth means sending a post with some json body to some url. but which field names to use, whats required or optional and what values to use e.g. for the scope is totally provider dependent. for office365/exchange, the values happen to be
i think the null check is required for BC. |
Sorry I wrote "instanceof" but that's inaccurate. I meant this line: |
@@ -32,6 +39,7 @@ public function getAuthKeyword(): string | |||
*/ | |||
public function authenticate(EsmtpTransport $client): void | |||
{ | |||
$client->executeCommand('AUTH XOAUTH2 '.base64_encode('user='.$client->getUsername()."\1auth=Bearer ".$client->getPassword()."\1\1")."\r\n", [235]); | |||
$token = $this->tokenProvider ? $this->tokenProvider->getToken() : $client->getPassword(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
$token = $this->tokenProvider ? $this->tokenProvider->getToken() : $client->getPassword(); | |
$token = $this->tokenProvider?->getToken() : $client->getPassword(); |
But I'm wondering about this line as now we give two ways to pass that secret - what if both are set for example? I feel like it'd be better to provide only one way to pass that secret - the existing one if possible :) Does that make sense? (it doesn't have to :) )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the issue with the existing one is that oauth tokens tend to be short-lived. In your worker process, you cannot just set the token as the password in the Transport as this will not properly refresh it when it expires.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changing the client to fetch the password dynamically would be a large refactor, and would mix responsibilities for the auth method.
the thing is that an oauth token can not be statically configured. and in a long running process it might expire before the process ends, so each time we need it we need to be ready to re-fetch it.
we could inject some TokenProviderInterface into the client and use that for getPassword(), but i don't think that would be better.
…ticator.php Co-authored-by: Nicolas Grekas <nicolas.grekas@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it for the geToken??getPassword logic, makes sense to me now.
Here are some suggestions to help move forward.
@@ -2091,6 +2091,15 @@ private function addMailerSection(ArrayNodeDefinition $rootNode, callable $enabl | |||
->end() | |||
->end() | |||
->end() | |||
->arrayNode('smtp') | |||
->fixXmlConfig('authenticator') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of adding a new config entry, I'd suggest passing a tagged_locator in the service definition
we should define the default authenticators as services with low priority so that then adding a new authenticator is just a matter of creating a new class that implements AuthenticatorInterface, thanks to registerForAutoconfiguration.
* Usually with OAuth, you will need to do some web request to fetch the token. | ||
* You also want to cache the token for as long as it is valid. | ||
*/ | ||
interface AuthTokenProviderInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TokenProviderInterface?
/** | ||
* @param AuthenticatorInterface[]|null $authenticators | ||
*/ | ||
public function __construct(EventDispatcherInterface $dispatcher = null, HttpClientInterface $client = null, LoggerInterface $logger = null, array $authenticators = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure we need the new argument: calling setAuthenticators on the resulting instance should be enough
Note that it'd be cool to contribute the office365 bridge while at it :) |
Oh and maybe also an AbstractOauthTokenProvider? |
thanks for the feedback. yeah that sounds good. i will try to find time, might be after christmas only 🙈 the office365 provider would go into a new Office365 namespace at https://github.com/symfony/symfony/tree/7.1/src/Symfony/Component/Mailer/Bridge and need a composer.json for the subtree split, right? i will see if an abstract base class makes sense that could be shared in the messenger package. |
yes! |
Hello, @dbu @nicolas-grekas, I stumbled here while doing some research, because I am since a few days working a new component proposal, that does exactly the job of the introduced here |
i do not have much time at the moment, so more than glad if you put some brain energy into proposing a nice architecture for the auth token system. i can try to give my input on the proposal when you have a draft ready, and will be happy to rebase this PR when/if your proposal gets merged. |
@dbu thanks, I will in few days I hope, open an issue for it. |
@nicolas-grekas I did ! Please see my PR #54013 - your insights would be invaluable ! |
Setting up the Symfony Mailer with XOAuth based authentication is possible but quite complicated. I hacked something together in https://gist.github.com/dbu/3094d7569aebfc94788b164bd7e59acc but with a few changes, I would only need to implement the token provider and configure it.
I guess this is too late for 7.0, so it should go to 7.1. I will adjust the target branch once 7.1 is created.