I need to disable redirection after login check, because I need to get only that the login was success or not. After submission /login_check url give me the right data, but keep redirecting to /login (on failure).
/login is blank after that.
I am trying to set up login form using extjs 4 so I need to validate trough an ajax post request.
login_check should authenticate, create user session and return whether it was success or failure, but no forwarding anywhere.
my login.html.twig looks like:
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
{ success:true }
{% else %}
{ success: false }
{% endif %}
and in security.yml:
firewalls:
main:
form_login:
provider: fos_userbundle
failure_path: null
failure_forward: false
Create an authentication handler:
namespace YourVendor\UserBundle\Handler;
// "use" statements here
class AuthenticationHandler
implements AuthenticationSuccessHandlerInterface,
AuthenticationFailureHandlerInterface
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => true);
return new Response(json_encode($result));
} else {
// Handle non XmlHttp request here
}
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => false);
return new Response(json_encode($result));
} else {
// Handle non XmlHttp request here
}
}
}
Register the handler as a service:
services:
authentication_handler:
class: YourVendor\UserBundle\Handler\AuthenticationHandler
Register the service in the firewall:
firewalls:
main:
form_login:
success_handler: authentication_handler
failure_handler: authentication_handler
This is a rough example to give you the general idea — you'll need to figure out the details by yourself. If you're stuck and need further clarifications, put your questions in the comments and I'll try to elaborate the example.
The normal symfony flow is to redirect you to a login page if you are not logged in, which works fine for humans. But you seems to be looking for a programmatic solution.
Have you tried setting _target_path in your form, to declare what the "next page" should be? Symfony is always going to forward you somewhere, but you can set that somewhere to wherever you want.
I found these two pages useful for describing the inner workings of the login form:
How to customize your form login (search for _target_path)
Security page in the handbook
Related
I have config (security.yaml):
firewalls:
login:
pattern: ^/api/login
json_login:
username_path: email
password_path: password
check_path: api_login
main:
login_throttling:
max_attempts: 3
lazy: true
provider: app_user_provider
custom_authenticator: App\Security\CustomAuthenticator
<?php
namespace App\Controller;
use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
#[Route('/api', name: 'api_')]
class ApiController extends AbstractController
{
#[Route('/login', name: 'login', methods: ['POST'])]
public function login(#[CurrentUser] ?User $user): Response
{
if (null === $user) {
return $this->json([
'message' => 'missing credentials',
], Response::HTTP_UNAUTHORIZED);
}
return $this->json(['user' => $user->getUserIdentifier()]);
}
}
In debug-panel I see Authenticated and i see real getUserIdentifier(). But if I reload the page, then I'm not logged in again. If you move json_login to the main block, then everything works. What is missing?
I tried different custom authorizers, but it didn't help. I also looked at the open repositories in the github but all examples are the same
You don't have to create separate firewall only for login path - add entries in your main firewall.
I cannot find it now, but I can bet that somewhere in Symfony Security docs there is information that login should not have separate firewall. But from docs:
Each firewall is like a separate security system, being authenticated in one firewall doesn't make you authenticated in another one.
So, you are authenticating in login firewall, but then navigating to any endpoint protected by main will not work.
I am implementing user registration process on Symfony 4.4 (without bundle) and I am stuck at the last step.
So far, this is what I have done:
Registration form is created
When user submit registration form he is well added to the database. A field activation token in user entity is fullfill.
An email with the activation token as parameter is automatically send to the user to activate his account
If the user click on the link, activation token field is set to 'null' in the user entity
In a user checker I check if the activation token is null
If activation token is not null I need to refuse the login and redirect the user to homepage with a flash message :I am stuck on this part, my User checker is not triggered. User can login with token not null.
Here is my user checker:
namespace App\Security;
use App\Exception\AccountDeletedException;
use App\Security\User as AppUser;
use Symfony\Component\Security\Core\Exception\AccountExpiredException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\DisabledException;
class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
// User account is not validated
if ($user->getValidationToken() !== null) {
throw new DisabledException('User account is not activated');
}
}
public function checkPostAuth(UserInterface $user)
{
if (!$user instanceof AppUser) {
return;
}
// user account is expired, the user may be notified
if ($user->isExpired()) {
throw new AccountExpiredException('...');
}
}
}
Security.yalm:
security:
encoders:
App\Entity\User:
algorithm: auto
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
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
main:
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\AppCustomAuthenticator
logout:
path: app_logout
user_checker: App\Security\UserChecker
Change :
use App\Security\User as AppUser
To
use App\Entity\User as AppUser;
(or whatever namespace you have on your User class)
Side note : on preAuth, you can check for the presence of the token and not if valid or not since the auth is not passed... this should be done on real Authentification, or post it...
I'm trying to use auto login feature in symfony something like firing the login event and setting the user object. The user object is available in that controller but when I try to use other controller the user object says annon instead of showing the logged in user info
Controller A
private function autoLogin($request, $username)
{
$em = $this->getDoctrine()->getManager();
if (!$usr = $em->getRepository('AppBundle:User')->findOneBy(['username' => $username])) {
throw $this->createNotFoundException('User does not exist');
}
$token = new UsernamePasswordToken($usr, $usr->getPassword(), "secured_area", $usr->getRoles());
$this->get('security.token_storage')->setToken($token);
$loginEvent = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $loginEvent);
$user = $this->get('security.token_storage')->getToken()->getUser();
dump($user); // can see user object without any issue
if (!$this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
throw new AccessDeniedException();
}
return $usr;
}
Controller B
public function editAction(Request $request)
{
$user = $this->get('security.token_storage')->getToken()->getUser();
print_r($user); // result is annon.
}
security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
providers:
doctrine_provider:
entity:
class: AppBundle:User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
secured_area:
anonymous: ~
provider: doctrine_provider
pattern: ^/
form_login:
login_path: security_login
check_path: security_login
csrf_token_generator: security.csrf.token_manager
logout:
path: /logout
target: /login
access_control:
- { path: ^/.*, roles: IS_AUTHENTICATED_ANONYMOUSLY }
I'd assume that you're not using any security setup and the second controller is called after user refreshes the page.
The most likely problem is that your user is not persisted into the session. Symfony\Component\Security\Http\Firewall\ContextListener is responsible for that. If you have a look at onKernelResponse() method you can find out how it does it. Basically it gets token from token storage, serialize it and stores to the session. On the request it does opposite: gets token from session and puts it to token storage.
I'd suggest that you play with the configuration of firewall and set up something like this:
firewalls:
autologin:
pattern: /autologinUrl/
context: autologing
In this case context listener will be called doing session-related stuff and your code should work.
I'm trying to setup the FOSRestBundle to catch authentication exceptions. My config:
fos_rest:
param_fetcher_listener: true
body_listener: true
format_listener: true
view:
default_engine: php
format_listener:
default_priorities: ['json']
fallback_format: json
prefer_extension: false
access_denied_listener:
json: true
exception:
codes:
Symfony\Component\Security\Core\Exception\AccessDeniedException: 403
twig:
exception_controller: FOS\RestBundle\Controller\ExceptionController::showAction
This config will catch exceptions when they are thrown in a controller, but not when they are thrown from within the security component (i.e. if a user auth fails). Is something wrong with my config, or is the FOSRestBundle simply not designed to intercept exceptions at that point in the stack?
It's worth mentioning that I'm using a custom auth provider based on the WSSE tutorial here:
http://symfony.com/doc/current/cookbook/security/custom_authentication_provider.html
authentication exceptions should normally be handled by the authentication failure handler service.
The authentication handlers can be found in your security.yml:
security:
firewalls:
firewall_name:
form_login:
# ...
failure_path: /foo
failure_forward: false
failure_path_parameter: _failure_path
failure_handler: some.service.id
success_handler: some.service.id
see the configuration reference.
See this question for information on how to implement your own failure handler.
I was having this exact same issue. My issue was that WsseListener wasnt actually throwing the exception. Their tutorial has these code:
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->securityContext->setToken($authToken);
return;
} catch (AuthenticationException $failed) {
// ... you might log something here
// To deny the authentication clear the token. This will redirect to the login page.
// Make sure to only clear your token, not those of other authentication listeners.
// $token = $this->securityContext->getToken();
// if ($token instanceof WsseUserToken && $this->providerKey === $token->getProviderKey()) {
// $this->securityContext->setToken(null);
// }
// return;
// Deny authentication with a '403 Forbidden' HTTP response
$response = new Response();
$response->setStatusCode(403);
$event->setResponse($response);
}
// By default deny authorization
$response = new Response();
$response->setStatusCode(403);
$event->setResponse($response);
}
Note how the AuthenticationException is getting caught and then its returning a HTTP Response.
I fixed mine by just throwing $failed:
catch (AuthenticationException $failed) {
throw $failed
}
I have gone through lots of stackoveflow question and articles, but can't find a suitable answer.
I'm using fosuserbundle, hwiouthbundle and lexikjwt bundle.
I'm developing an api based on symfony which will be consumed by an android app and angular app.
Now I need the register and login system with fosuserbundle facebook login with hwiouthbundle and api protection with lexikjwt bundle.
I have implemented fosuserbundle and hwiouthbundke and both working without even writing user controller. But I need this with rest not with form. But I can't out type : rest in router.
Now how can I login, register user with fosuserbundle with rest? I don't want to use fosouth server. Just need registration and login with api not rest from web.
So, if you want register user manually using FOSUserBundle, create a controller and add a register method :
// Acme/AppBundle/Controller/SecurityController
public function registerAction(Request $request)
{
$userManager = $this->get('fos_user.user_manager');
$entityManager = $this->get('doctrine')->getManager();
$data = $request->request->all();
// Do a check for existing user with userManager->findByUsername
$user = $userManager->createUser();
$user->setUsername($data['username']);
// ...
$user->setPlainPassword($data['password']);
$user->setEnabled(true);
$userManager->updateUser($user);
return $this->generateToken($user, 201);
}
And, the generateToken method
protected function generateToken($user, $statusCode = 200)
{
// Generate the token
$token = $this->get('lexik_jwt_authentication.jwt_manager')->create($user)
$response = array(
'token' => $token,
'user' => $user // Assuming $user is serialized, else you can call getters manually
);
return new JsonResponse($response, $statusCode); // Return a 201 Created with the JWT.
}
And the route
security_register:
path: /api/register
defaults: { _controller: AcmeAppBundle:Security:registerAction }
methods: POST
Configure the firewall same as login
// app/config/security.yml
firewalls:
// ...
register:
pattern: ^/api/register
anonymous: true
stateless: true
// ...
access_control:
// ...
- { path: ^/api/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
For login, juste use the check_path of your FOSUser login firewall.
For more information about the token generation, see JWTManager.
Hope this help you.
EDIT
If you want a full example of LexikJWTAuthenticationBundle + FOSUserBundle + FOSRestBundle implementation see my symfony-rest-api