diff --git a/src/Protocol/Imap/Negotiation/AuthNegotiation.php b/src/Protocol/Imap/Negotiation/AuthNegotiation.php index 9604f538..52134991 100644 --- a/src/Protocol/Imap/Negotiation/AuthNegotiation.php +++ b/src/Protocol/Imap/Negotiation/AuthNegotiation.php @@ -3,6 +3,7 @@ namespace Genkgo\Mail\Protocol\Imap\Negotiation; +use Genkgo\Mail\Exception\AssertionFailedException; use Genkgo\Mail\Exception\ImapAuthenticationException; use Genkgo\Mail\Protocol\Imap\Client; use Genkgo\Mail\Protocol\Imap\NegotiationInterface; @@ -88,7 +89,7 @@ public function negotiate(Client $client): void ->first() ->assertContinuation(); - $client + $taggedResponse = $client ->emit( new AuthPlainCredentialsRequest( $tag, @@ -97,16 +98,29 @@ public function negotiate(Client $client): void ) ) ->last() - ->assertCompletion(CompletionResult::ok()) ->assertTagged(); + + try { + $taggedResponse->assertCompletion(CompletionResult::ok()); + } catch (AssertionFailedException $e) { + throw new ImapAuthenticationException( + 'Failed to authenticate: ' . $taggedResponse->getBody() + ); + } break; case Client::AUTH_LOGIN: - $tag = $client->newTag(); - $client - ->emit(new LoginCommand($tag, $this->username, $this->password)) + $taggedResponse = $client + ->emit(new LoginCommand($client->newTag(), $this->username, $this->password)) ->last() - ->assertTagged() - ->assertCompletion(CompletionResult::ok()); + ->assertTagged(); + + try { + $taggedResponse->assertCompletion(CompletionResult::ok()); + } catch (AssertionFailedException $e) { + throw new ImapAuthenticationException( + 'Failed to authenticate: ' . $taggedResponse->getBody() + ); + } break; } } diff --git a/test/Unit/Protocol/Imap/Negotiation/AuthNegotiationTest.php b/test/Unit/Protocol/Imap/Negotiation/AuthNegotiationTest.php index c392a159..e2cf83b6 100644 --- a/test/Unit/Protocol/Imap/Negotiation/AuthNegotiationTest.php +++ b/test/Unit/Protocol/Imap/Negotiation/AuthNegotiationTest.php @@ -172,4 +172,92 @@ public function it_uses_capability_when_method_is_auto_and_not_advertised_and_lo $negotiation = new AuthNegotiation(Client::AUTH_AUTO, 'username', 'password'); $negotiation->negotiate($client); } + + /** + * @test + */ + public function it_throws_auth_exception_when_plain_auth_fails(): void + { + $this->expectException(ImapAuthenticationException::class); + $this->expectExceptionMessage('Failed to authenticate: NO [AUTHENTICATIONFAILED] Authentication failed.'); + + $connection = $this->createMock(ConnectionInterface::class); + $connection + ->expects($this->at(0)) + ->method('addListener'); + + $connection + ->expects($this->at(1)) + ->method('send') + ->with("TAG1 AUTHENTICATE PLAIN\r\n"); + + $connection + ->expects($this->at(2)) + ->method('receive') + ->willReturn('+ Send password'); + + $connection + ->expects($this->at(3)) + ->method('send') + ->with("AHVzZXJuYW1lAHBhc3N3b3Jk\r\n"); + + $connection + ->expects($this->at(4)) + ->method('receive') + ->willReturn('TAG1 NO [AUTHENTICATIONFAILED] Authentication failed.'); + + $client = new Client($connection, new GeneratorTagFactory(), []); + + $negotiation = new AuthNegotiation(Client::AUTH_PLAIN, 'username', 'password'); + $negotiation->negotiate($client); + } + + /** + * @test + */ + public function it_throws_auth_exception_when_login_fails(): void + { + $this->expectException(ImapAuthenticationException::class); + $this->expectExceptionMessage('Failed to authenticate: NO [AUTHENTICATIONFAILED] Authentication failed.'); + + $connection = $this->createMock(ConnectionInterface::class); + $connection + ->expects($this->at(0)) + ->method('addListener'); + + $connection + ->expects($this->at(1)) + ->method('send') + ->with("TAG1 CAPABILITY\r\n"); + + $connection + ->expects($this->at(2)) + ->method('receive') + ->willReturn('* CAPABILITY IMAP4rev1 STARTTLS'); + + $connection + ->expects($this->at(3)) + ->method('receive') + ->willReturn('TAG1 OK'); + + $connection + ->expects($this->at(4)) + ->method('send') + ->with("TAG2 LOGIN username password\r\n"); + + $connection + ->expects($this->at(5)) + ->method('receive') + ->willReturn('TAG2 NO [AUTHENTICATIONFAILED] Authentication failed.'); + + $client = new Client($connection, new GeneratorTagFactory(), []); + + $negotiation = new AuthNegotiation(Client::AUTH_AUTO, 'username', 'password'); + $negotiation->negotiate($client); + + $client = new Client($connection, new GeneratorTagFactory(), []); + + $negotiation = new AuthNegotiation(Client::AUTH_PLAIN, 'username', 'password'); + $negotiation->negotiate($client); + } }