-
Notifications
You must be signed in to change notification settings - Fork 77
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
Email Example with API-Plattform #264
Comments
Thanks, I found that too. But no mail is sent. Elsewhere it is sent with the Symfony mailer. But not with this instruction. |
The default mailer is used. If you have symfony/mailer installed, that one will be used. It reads like you want to use 2fa from an API, so please have a look at the instructions here. This not specifically about API Platform, but it should give you some hints what you need to address: Especially the two configuration options
would be important. If you haven't set those, this might be the reason why you're not receiving an email. |
Ok. I have two logins. <?php
namespace App\Entity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Scheb\TwoFactorBundle\Model\Email\TwoFactorInterface as EmailTwoFactorInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface,EmailTwoFactorInterface
{
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
/**
* @var ?string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $authCode;
/*....*/
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function is2FAEnabled(): ?bool
{
return $this->is2FA_Enabled;
}
public function setIs2FAEnabled(bool $is2FA_Enabled): static
{
$this->is2FA_Enabled = $is2FA_Enabled;
return $this;
}
public function isEmailAuthEnabled(): bool
{
return $this->is2FAEnabled();
}
public function getEmailAuthRecipient(): string
{
return $this->email;
}
public function getEmailAuthCode(): string
{
return (string)$this->authCode;
}
public function setEmailAuthCode(string $authCode): void
{
$this->authCode = $authCode;
}
} SecurityController <?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
#[Route(path: '/login', name: 'app_login')]
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
#[Route(path: '/logout', name: 'app_logout')]
public function logout(): void
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
} AppCustomAuthenticator <?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class AppCustomAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
public function __construct(private UrlGeneratorInterface $urlGenerator)
{
}
public function authenticate(Request $request): Passport
{
$email = $request->request->get('email', '');
$request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
$user = $token->getUser();
if (in_array('ROLE_ADMIN', $user->getRoles(), true)) {
return new RedirectResponse($this->urlGenerator->generate('admin'));
} else {
return new RedirectResponse($this->urlGenerator->generate('app_dashboard'));
}
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// For example:
return new RedirectResponse($this->urlGenerator->generate('app_dashboard'));
//throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
} The login is used by Easyadmin and the API. |
Would you please post your bundle and security configuration? For some troubleshooting on your own, you can follow this guide: https://symfony.com/bundles/SchebTwoFactorBundle/7.x/troubleshooting.html |
Sure. scheb_two_factor:
security_tokens:
- Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken
email:
enabled: true
digits: 6
sender_email: no-reply@email.com
sender_name: Auth-Code
template: security/2fa.html.twig security.yaml security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api:
pattern: ^/api
stateless: false
jwt: ~
json_login:
check_path: api_login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
main:
lazy: true
jwt: ~
provider: app_user_provider
custom_authenticator: App\Security\AppCustomAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
entry_point: 'App\Security\AppCustomAuthenticator'
two_factor:
auth_form_path: 2fa_login
check_path: 2fa_login_check
prepare_on_login: true
prepare_on_access_denied: true
access_control:
- { path: ^/api/login_check, roles: PUBLIC_ACCESS }
- { path: ^/api/docs, roles: PUBLIC_ACCESS } # Allows accessing API documentations and Swagger
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^(/(de|en|fr|it|es))?/admin, roles: ROLE_ADMIN }
- { path: ^/$, roles: PUBLIC_ACCESS }
- { path: ^(/(de|en|fr|it|es))?/login, roles: PUBLIC_ACCESS }
- { path: ^/login, roles: PUBLIC_ACCESS }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
- { path: ^/2fa, role: IS_AUTHENTICATED_2FA_IN_PROGRESS }
- { path: ^/logout, role: PUBLIC_ACCESS }
|
You have two different firewalls and 2FA is not activated on the "api" firewall. So any login within the "api" firewall scope, will not trigger a 2FA process. |
Yes, that is correct. But that's why I wanted to get it right with easyadmin first. But it doesn't work. |
Your custom authenticator extends from |
Thank you for your support. That was the problem. Now I get a mail and redirected to 2fa. The login at the backend works now. I'll keep trying to get it on the API. |
Now it also works with the API. Thanks for the help. |
Bundle version: 7.6.0
Symfony version: 7.1.6
PHP version: 8.3.19
Description
Can you please explain the implementation of Email-2fa? I can't find a explanation anywhere. For the others I can find it in the docs.
The implementation of the API platform would also be cool
Additional Context
API-Plattfrom:4.0.17
The text was updated successfully, but these errors were encountered: