Eigene Route für Userlogin / Authentication über JSON
Ich scheitere aktuell daran einen User in einem eigenen Bundle anzumelden. Leider funktioniert das ganze nicht. Der User wird zwar gefunden und auch angemeldet. Aber beim nächsten Request die Anmeldung wieder weg. Scheinbar funktoniert das Speichern der Anmeldung in der Session nicht so.
Kann mir jemand einen Hinweis geben was ich falsch mache, hab eigentlich erwartet das es relativ einfach gehen sollte einen User anzumelden, aber scheinbar ist dem nicht so.
PHP-Code:
public function __construct(
RequestStack $requestStack,
ContaoFramework $framework,
TranslatorInterface $translator,
$authProvider,
UserProviderInterface $userProvider,
TokenStorageInterface $tokenStorage,
LoggerInterface $logger,
EventDispatcherInterface $eventDispatcher,
UserCheckerInterface $userChecker,
AuthenticationSuccessHandlerInterface $authenticationSuccessHandler
) {
parent::__construct($requestStack, $framework, $translator);
$this->tokenStorage = $tokenStorage;
$this->authProvider = $authProvider;
$this->userProvider = $userProvider;
$this->tokenStorage = $tokenStorage;
$this->logger = $logger;
$this->eventDispatcher = $eventDispatcher;
$this->requestStack = $requestStack;
$this->userChecker = $userChecker;
$this->authenticationSuccessHandler = $authenticationSuccessHandler;
}
public function login(Request $request)
{
$container = System::getContainer();
$user = FrontendUser::loadUserByUsername($_POST['username']);
$token = new UsernamePasswordToken($_POST['username'], $_POST['password'],
'contao_frontend',$user->getRoles());
try {
$this->userChecker->checkPreAuth($user);
$this->userChecker->checkPostAuth($user);
} catch (AccountStatusException $e) {
return ApiResponse::error("Authentication failed");
}
$token = $this->authProvider->authenticate($token);
$this->tokenStorage->setToken($token);
$event = new InteractiveLoginEvent($request, $token);
$this->eventDispatcher->dispatch($event, 'onSuccessfulLogin');
$this->authenticationSuccessHandler->onAuthenticationSuccess($request, $token);
return ApiResponse::success();
}
Login mit code, z.B. QR-Code per Post verschickt
Wir hatten einen ähnlichen Fall (Contao 4.13), dass wir einen QR-Code per Post verschickt haben und die Nutzer beim Scan (oder manueller Eingabe) direkt angemeldet werden sollten. Der entscheidende Hinweis war tatsächlich in dem Posting des OP hier. Es ist nur ein minimaler Unterschied, durch den es letztendlich funktioniert hat. Beim UsernamePasswordToken wird kein Password angegeben. Den Password Parameter gibt es nämlich im c'tor von UsernamePasswordToken in symfony 6 nicht:
https://github.com/symfony/symfony/b...dToken.php#L25
Der Code sieht dann so aus:
PHP-Code:
// create the user session
$token = new UsernamePasswordToken($user, 'contao_frontend', $user->getRoles());
$authenticatedToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authenticatedToken);
Die Authentifizierung läuft vorher ab, indem der Nutzer mit dem Code aus der DB geladen wird. Der Code ist eine praktisch nicht erratbare Buchstaben/Zahlen Kombination, die einen eindeutigen index in der DB darstellt. Hier die komplette Login Methode:
PHP-Code:
public function loginWithCode(string $code): ?FrontendUser
{
// try to load the usr via the secret unique personal code, e.g. in QR as https://site.com/code-login?webCode=123456
$memberModel = MemberModel::findBy('webCode', $code);
if (!$memberModel) {
// user not found, code is invalid
return null;
}
// code is valid, load the user by its username
$user = FrontendUser::loadUserByIdentifier($memberModel->username);
if (!$user) {
// something bad happened, no login
return null;
}
try {
// check pre and post auth hooks
$this->userChecker->checkPreAuth($user);
$this->userChecker->checkPostAuth($user);
} catch (Exception $ex) {
return null;
}
// update the lastLogin and currentLogin field
$user->lastLogin = $user->currentLogin;
$user->currentLogin = time();
$user->save();
// create the user session
$token = new UsernamePasswordToken($user, 'contao_frontend', $user->getRoles());
$authenticatedToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authenticatedToken);
// log the login event
static::getContainer()->get('monolog.logger.contao')->log(
LogLevel::INFO,
sprintf('User "%s" has logged in with code', $user->username),
['contao' => new ContaoContext(__METHOD__, TL_ACCESS)]
);
return $user;
}
Der Vollständigkeit halber hier der c'tor der Klasse:
PHP-Code:
class CodeAuthenticator extends System
{
private AuthenticationManagerInterface $authenticationManager;
private TokenStorageInterface $tokenStorage;
private UserChecker $userChecker;
public function __construct(
AuthenticationManagerInterface $authenticationManager,
TokenStorageInterface $tokenStorage,
UserChecker $userChecker
)
{
$this->authenticationManager = $authenticationManager;
$this->tokenStorage = $tokenStorage;
$this->userChecker = $userChecker;
}
public function loginWithCode(string $code): ?FrontendUser{...}
}
Bei einer User/Password Authentifizierung muss natürlich die User/Password Kombi vorher selbst geprüft werden, vermutlich gibt es hier schon was Passendes in Symfony selbst. Das habe ich jetzt nicht recherchiert...