Expected response code "250/251/252" but got code "530", with message "530 SMTP authentication is required." - symfony

###> symfony/mailer ###
MAILER_DSN=smtp://localhost
###< symfony/mailer ###
this is a part from my .env i'm trying to send an email once the user is registred but i dont know wht to put in MAILER DSN, and i am getting this error
THE ERROR
and last but not least here's my mailer service
<?php
namespace App\Service;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
class Mailer{
/**
* #var MailerInterface
*/
private $mailer;
public function __construct(MailerInterface $mailer)
{
$this->mailer = $mailer;
}
public function sendMail($email, $token){
$email = (new TemplatedEmail())
->from('Lost-found#foundonly.com')
->to(new Address($email))
->subject('Thanks for signing up! Just one more thing to do')
// path of the Twig template to render
->htmlTemplate('emails/signup.html.twig')
// pass variables (name => value) to the template
->context([
'token' => $token,
])
;
$this->mailer->send($email);
}
}
and finally the register controller
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\RegisterType;
use App\Service\Mailer;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class RegisterController extends AbstractController
{
/**
* #var UserPasswordEncoderInterface
*/
private $passwordEncoder;
/**
* #var Mailer
*/
private $mailer;
public function __construct(UserPasswordEncoderInterface $passwordEncoder, Mailer $mailer)
{
$this->passwordEncoder = $passwordEncoder;
$this->mailer = $mailer;
}
/**
* #Route("/signup", name="signup")
* #throws \Exception
*/
public function register(Request $request): Response
{
$user = new User();
$form = $this->createForm(RegisterType::class,$user);
$form->handleRequest($request);
if($form->isSubmitted()&&$form->isValid()){
$user->setPassword(
$this->passwordEncoder->encodePassword($user,$form->get("password")->getData())
);
$user->setToken($this->generateToken());
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->mailer->sendMail($user->getEmail(),$user->getToken());
$this->addFlash("success", "you are more than welcome into our community, just one more step | Check your mail please");
}//37.12
return $this->render('register/register.html.twig',[
'form' => $form->createView()
]);
}
/**
* #throws \Exception
*/
private function generateToken(): string
{
return rtrim(strtr(base64_encode(random_bytes(32)),'+/','-_'),'=');
}
}
?>
so please can anyone help me here? i really dont know wht to put in the mailer_dsn

You need to properly configure your Mailer transport. It can either be an SMTP server or local sendmail binary.
If you don't want to bother with sendmail and prefer using SMTP transport, the easiest solution would be using gmail smtp server with symfony/google-mailer component
More info:
Sending Emails with Mailer

Related

Symfony/ Api platorm/JWT get the current user after login

Good morning to all
Please i need help. I am using JWT Authentication and all works well.But my problem is to retreive the current user after the login. I saw in the documentation that i can create a controller to do so, but after doing that i get the error of id parameter not given.
Here is my controller related to the user entity
// api/src/Controller/GetMeAction.php
namespace App\Controller;
use App\Entity\User;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Security;
class GetMeAction
{
/**
* #param Security
*/
private $_security;
public function __construct(Security $security)
{
$this->_security = $security;
}
/**
* #Route(
* name="get_me",
* path="get/me",
* methods={"GET"},
* defaults={
* "_api_resource_class"=User::class,
* "_api_item_operation_name"="get_me"
* }
* )
*/
public function __invoke(Request $request): User
{
return $this->_security->getUser();
}
}
Im using symfony 5.3, i wanted to use the api platform normalization and the item operation "get" to keep all the custom config, security, services, ...
So I used the forward() method in a controller :
/**
* #Route("/api/user/me", name="get_me")
*/
public function getMe(): Response
{
$router = $this->get('router')->getRouteCollection()->get('api_users_get_item');
$defaults = $router->getDefaults();
return $this->forward($router->getDefault('_controller'), array_merge($defaults, [ 'id' => $this->getUser()->getId()]));
}
Previous answer is right, but you forgot to Extend you controller from abstract one:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class AdminController extends AbstractController
{
}
If you want to get User in the service, you can Inject Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface in your __construct()
and you can get user like:
public function getUser(): ?User
{
$token = $this->tokenStorage->getToken();
if (!$token) {
return null;
}
$user = $token->getUser();
if (!$user instanceof User) {
return null;
}
return $user;
}

Symfony 4 How to autowire strings when making a general class?

I would first like to say that I saw the other questions on here relating to this error I'm having and none solved my problems.
I have the following code for a controller to check an APIkey before sending data from the backend to the frontend.
file1Controller.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class file1Controller extends AbstractController
{
/**
* #Route("/Some/URI", methods={"GET"}) // "/Some/URI" here
* #param Request $request
* #return JsonResponse
*/
public function list(Request $request)
{
if (empty($request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:Something')->findAll()); //Something here
}
}
Which works exactly as intended (tested it with Postman and with my browser) for my simple learning example. I would like to generalize it so that I can use it in other places. Almost everything should stay the same except the parts where there are comments. This is what it becomes when making it general:
General.php
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class General extends AbstractController
{
private $route;
private $entity;
/**
* General constructor.
* #param String $route
* #param String $entity
*/
function __construct(String $route, String $entity)
{
$this->route = $route;
$this->entity = $entity;
}
/**
* #Route({$this->route}, methods={"GET"})
* #param Request $request
* #return JsonResponse
*/
public function list(Request $request)
{
if (empty($request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:{$this->entity}')->findAll());
}
}
And the file file1Controller.php changes to:
<?php
namespace App\Controller;
use App\General;
use Symfony\Component\HttpFoundation\Request;
class SubscriptionController
{
/**
* #return General
*/
public function AuthenticateAPI()
{
$generalObject = new General("/Some/URI", 'Something');
return $generalObject;
}
}
This new setup gives no compiler errors but of course, do give the following error (when testing it):
Cannot autowire service "App\General": argument "$route" of method "__construct()" is type-hinted "string", you should configure its value explicitly.
I understand that this error occurs because Symfony doesn't know which String to inject. But there must be a way to get around this? Because I can't specify the value explicitly in my case because I'll be making another file file2Controller.php which will be the exact same but with different $route and $entity.

How can I generalize an ApiKeyAuthenticator in Symfony 4?

I have the following code that checks whether the API-key is the correct one before sending data to the front end.
file1Controller.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class file1Controller extends AbstractController
{
/**
* #Route("/Some/URI", methods={"GET"}) // "/Some/URI" here
* #param Request $request
* #return JsonResponse
*/
public function list(Request $request)
{
if (empty($request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:Something')->findAll()); //Something here
}
}
Which works excatly as intended (tested it with Postman) for my simple learning example. I would like to generalize it so that I can use it in other places. Almost everything should stay the same except the parts where there are comments. I have tried the following:
General.php
<?php
namespace App;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
class General extends AbstractController
{
private $request;
private $route;
private $entity;
/**
* ApiKeyAuthenticator constructor.
* #param Request $request
* #param String $route
* #param String $entity
*/
function __construct(Request $request, String $route, String $entity)
{
$this->request = $request;
$this->route = $route;
$this->entity = $entity;
}
/**
* #Route({$route}, methods={"GET"}) //notice here
* #return JsonResponse
*/
public function list()
{
if (empty($this->request->headers->get('api-key'))) {
return new JsonResponse(['error' => 'Please provide an API_key'], 401);
}
if ($this->request->headers->get('api-key') !== $_ENV['API_KEY']) {
return new JsonResponse(['error' => 'Invalid API key'], 401);
}
return new JsonResponse($this->getDoctrine()->getRepository('App:{$this->entity}')->findAll()); //notice here
}
}
Then I change the code of file1Controller.php to:
<?php
namespace App\Controller;
require(__DIR__.'/../General.php'); //note that there's no error accessing the file here
use Symfony\Component\HttpFoundation\Request;
class file1Controller
{
/**
* #param Request $request
*/
public function AuthenticateAPI(Request $request)
{
$AuthenticatorObject = new ApiKeyAuthenticator($request, "/Some/URI", 'Something'); //getting undefiend class
return $AuthenticatorObject;
}
}
This is unfortunately not working when testing it with Postman and I'm getting an undefiend class error on this line $AuthenticatorObject = new ApiKeyAuthenticator($request, "/Some/URI", 'Something'); in file1Controller.php
What did I do wrong and how could I fix it?
You shouldn't call your controllers like this in Symfony:
require(__DIR__.'/../General.php'); //note that there's no error accessing the file here
Please check out defining and accessing controllers as service in Symfony documentation:
How to Define Controllers as Services
How to Forward Requests to another Controller

Symfony: How to save each successful login to database table

i am developing a symfony restful api with tools like
FosRestBundle, FosUserBundle and Lexik JWT for api authentication.
I need to save each successful login in my app. So i created a Login entity
(user_id,loginDate) , but i don't know how to use it because the login in handled from Lexik.
Does anyone know how can i do this?
Thanks
You can use security.interactive_login event for that. More information can be found from the official documentation:
https://symfony.com/doc/current/components/security/authentication.html#authentication-events
Create the listener and register it:
namespace App\EventListener;
use App\Component\EntityManagerAwareTrait;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
/**
* #package App\EventListener
*/
class SecuritySubscriber implements EventSubscriberInterface
{
/**
* #param EntityManagerInterface $em
*/
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
/**
* #return array
*/
public static function getSubscribedEvents(): array
{
return [
SecurityEvents::INTERACTIVE_LOGIN => 'onSecurityInteractiveLogin',
];
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event): void
{
$user = $event->getAuthenticationToken()->getUser();
if ($user instanceof User) {
$user->setLoginDate(new \DateTime());
$this->em->persist($user);
$this->em->flush();
}
}
}

Authentication Failure Handler did not return a Response

I have extended the DefaultAuthenticationFailureHandler in order to redirect users to register page if the username is not in the database. it works fine for the first part.
If the username exists in the database I want from the Controller the default behavior i.e. to redirect to the login page with the login error message. Why it is not issuing a redirect?
namespace UserBundle\Redirection;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationFailureHandler;
use UserBundle\Controller\SecurityController;
class AfterLoginFailureRedirection extends DefaultAuthenticationFailureHandler
{
/**
* #var \Symfony\Component\Routing\RouterInterface
*/
private $router;
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
private $container;
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
public function setContainer(ContainerInterface $container)
{
$this->container=$container;
}
/**
* #param Request $request
* #param AuthenticationException $token
* #return RedirectResponse
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
//$request = $this->container->get('request');
$token=$exception->getToken();
$username=$token->getUsername();
//$username = $request->request->get('email');
$user = $this->container->get('fos_user.user_manager')->findUserByUsername($username);
if(!$user) {
$url=$this->container->get('router')->generate('fos_user_registration_register');
return new RedirectResponse($url);
}
else
{
parent::onAuthenticationFailure($request,$exception);
}
}}
You don't return anything in your else case
correct code should be :
else {
return parent::onAuthenticationFailure($request,$exception);
}

Resources