FOSFacebookBundle works, but FOSUserBundle does not seem to work - facebook

I can login with FOSFacebookBundle and everything works. But, FOSUserBundle does not seem to work because profiler shows Username: anon and Roles: {}. And, there is no user data about logged in user in database. Maybe, I didn't understand how it works. Please, help.
This is my config.yml
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: public
user_class: Trade\TradeBundle\Entity\User
fos_facebook:
file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
alias: facebook
app_id: my_app_id
secret: app_secret_key
cookie: true
permissions: [user_about_me]
services:
fos_facebook.auth:
class: Trade\TradeBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "#fos_facebook.api"
userManager: "#fos_user.user_manager"
validator: "#validator"
container: "#service_container"
This is my security.yml
security:
factories:
- "%kernel.root_dir%/../vendor/bundles/FOS/FacebookBundle/Resources/config/security_factories.xml"
role_hierarchy:
ROLE_ADMIN: ROLE_FACEBOOK
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
my_fos_facebook:
id: fos_facebook.auth
firewalls:
public:
pattern: ^/.*
fos_facebook:
app_url: "app_url"
server_url: "server_url"
login_path: /user/login
check_path: /user/login_check
default_target_path: /
provider: my_fos_facebook
anonymous: true
logout:
handlers: ["fos_facebook.logout_handler"]
The code below does not seem to work because when I log in with facebook setTimeout(goLogIn, 500) function inside if is not called.
function goLogIn(){
window.location.href = "{{ path('user_login_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.statusChange', function(response) {
if (response.session || response.authResponse) {
setTimeout(goLogIn, 500);
} else {
window.location.href = "{{ path('_security_logout') }}";
}
});
}
}
These are my controller actions:
/**
* #Route("/user/login", name = "user_login")
*/
public function loginAction()
{
}
/**
* #Route("/user/login_check", name = "user_login_check")
*/
public function loginCheckAction()
{
}

In order for FOSUserBundle to work together with FOSFacebookBundle, you need to specify a specific login route just for the facebook login at security.yml:
public:
fos_facebook:
check_path: /loginFacebook
Of course you are going to need to point that route correctly at routing.yml:
_security_check:
pattern: /loginFacebook
Then you need to change the check URL on the facebook javascript:
function goLogIn(){
window.location.href = "{{ path('_security_check') }}";
}
The last thing is to create the controller and the action for the new route (this is very important, otherwise it's not going to work) and leave it empty:
public function loginFacebookAction()
{
return array();
}
Of course you are going to need to adapt this to your needs, like where I use the DefaultController, you, apperantly, use the UserController.
Hope it helps.

I would guess you are storing empty data because of your permissions request:
permissions: [user_about_me]
I suggest changing to the tutorial recommended:
permissions: [email, user_birthday, user_location]

Related

Symfony 4 - authentification 1form 2tables

I would like to know if from the same login form we could connect from two tables: User and Player
Here is my attempt.
security:
encoders:
App\Entity\User:
algorithm: auto
App\Entity\Player:
algorithm: auto
providers:
chain_provider:
chain:
providers: [mon_provider_player, mon_provider_user]
mon_provider_player :
entity:
class: App\Entity\Player
property: email
mon_provider_user :
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
player:
anonymous: lazy
provider: mon_provider_player
form_login:
login_path: login
check_path: login
success_handler: redirect.after.login
logout:
path: logout
target: login
main:
anonymous: lazy
provider: mon_provider_user
form_login:
login_path: login
check_path: login
success_handler: redirect.after.login
logout:
path: logout
target: login
remember_me:
secret: '%kernel.secret%'
lifetime: 604800 # 1 an par défaut
path: /
domain: ~ # Defaults to the current domain from $_SERVER
access_control:
- { path: ^/pol, roles: ["ROLE_POLISTE"] }
- { path: ^/user, roles: ["ROLE_USER"] }
I found the solution. Here to help if needed
Everything happens in the file: loginFormAuthenticator.php which extends AbstractFormLoginAuthenticator in symfony.
There are 5 methods, one of which allows the $user in several tables.
public function getUser($credentials, UserProviderInterface $userProvider)
{
$userFound = $this->userRepository->findOneBy(['email' => $credentials['email']]);
if ($userFound !== NULL) {
return $userFound;
} else {
return $this->playerRepository->findOneBy(['emailJoueur' =>
$credentials['email']]);
}
}

HWIOAuthBundle - FOSUserBundle - Symfony 2 - Redirect to custom path after login with facebook

Reflected the following problem after the user login with the Facebook account: that is redirected to the following route /#_=_
How can I redirect it to this route instead: / Or to more to this /# ?
On the client side, I use the backbone.
Taking #Prynz's idea, we can get further and create a "redirect to page user comes from" this way:
1) In your firewall, take care to remove the following lines:
# security.yml
# ...
logout: true
logout:
path: /logout
target: /
As we will implment the logout ourselves to avoid redirecting to the specified target.
2) Add #Prynz's solution to your security.yml (or config.yml depending on your implementation)
oauth:
resource_owners:
google: "/login/check-google"
facebook: "/login/check-facebook"
twitter: "/login/check-twitter"
sensio_connect: "/login/check-sensio-connect"
login_path: /login
failure_path: /login
default_target_path: /welcome # THIS LINE CONTRIBUTES TO THE MAGIC
oauth_user_provider:
service: app.oauth_user_provider
3) In your routing, add a new controller (here, LoginController) before HWIO's imports:
fuz_app_login:
resource: "#FuzAppBundle/Controller/LoginController.php"
type: annotation
prefix: /
4) Create the corresponding controller:
<?php
namespace Fuz\AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
class LoginController
{
/**
* #Route("/login", name="login")
* #Method({"GET"})
*/
public function loginAction(Request $request)
{
if ($this->getUser())
{
// already-logged user accessed /login
return $this->redirect($request->headers->get('referer'));
}
else
{
// redirect to the login page
return $this->forward('HWIOAuthBundle:Connect:connect');
}
}
/**
* #Route("/logout", name="logout")
* #Method({"GET"})
*/
public function logoutAction(Request $request)
{
// we do a manual logout just to redirect the user to where he comes from
$this->container->get('security.context')->setToken(null);
return $this->redirect($request->headers->get('referer'));
}
/**
* #Route("/connect/{service}", name="connect")
* #Method({"GET"})
*/
public function connectAction(Request $request, $service)
{
// we overwrite this route to store user's referer in the session
$this->get('session')->set('referer', $request->headers->get('referer'));
return $this->forward('HWIOAuthBundle:Connect:redirectToService', array('service' => $service));
}
/**
* #Route("/welcome", name="welcome")
* #Method({"GET"})
*/
public function welcomeAction()
{
// on login success, we're redirected to this route...
// time to use the referer we previously stored.
$referer = $this->get('session')->get('referer');
if (is_null($referer))
{
return new RedirectResponse($this->generateUrl('home'));
}
return new RedirectResponse($referer);
}
}
5) relax.
You simply add default_target_path: /whatever/path/you/want to the oauth section under the firewall setup
oauth:
resource_owners:
facebook: '/login/check-facebook'
google: '/login/check-google'
windows: '/login/check-windows'
twitter: '/login/check-twitter'
login_path: /login
failure_path: /login
default_target_path: /whatever/path/you/want
Have a look at https://github.com/hwi/HWIOAuthBundle/issues/89
Redirect using javascript. Add following to the page.
<script>
// Handle facebook callback
if (window.location.hash && window.location.hash == '#_=_') {
window.location.hash = '';
}
</script>
If you want, you can redirect the users to the current page doing this way:
Add in your config.yml
hwi_oauth.target_path_parameter: "target_path"
In your view append the urls with:
&target_path=...
remember you can take the current route name with
app.request.get('_route')

Service not triggered : The controller must return a response (null given)

Hi everyone and huge thanks for the help,
I am trying to get FOSFacebook work, but it seems to be impossible until now.
I tried a lot of configuration, and a lot of fix but nothing worked for me.
Here is my configuration :
security.yml
chain_provider:
chain:
providers: [fos_userbundle, fos_facebook_provider]
fos_userbundle:
id: fos_user.user_provider.username
fos_facebook_provider:
id: fos.facebook.custom_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
login_path: fos_user_security_login
check_path: fos_user_security_check
provider: fos_userbundle
csrf_provider: form.csrf_provider
logout: true
anonymous: true
public:
pattern: ^/
fos_facebook:
app_url: "http://apps.facebook.com/myApp/"
server_url: "http://www.mywebsite.com"
login_path: /facebook/login
check_path: /facebook/check
default_target_path: /
provider: fos_facebook_provider
anonymous: false
logout:
handlers: ["fos_facebook.logout_handler"]
config.yml
fos.facebook.custom_provider:
class: Bundles\UserBundle\Security\User\Provider\FacebookProvider
arguments:
facebook: "#fos_facebook.api"
userManager: "#fos_user.user_manager"
validator: "#validator"
container: "#service_container"
fos_facebook:
file: %kernel.root_dir%/../vendor/facebook/php-sdk/src/base_facebook.php
alias: facebook
app_id: myid
secret: mysecret
cookie: true
permissions: [email]
routing.yml
_security_check:
pattern: /facebook/check
_security_logout:
pattern: /logout
Twig Template
<script type="text/javascript">
function goLogIn(){
window.location.href = "{{ path('_security_check') }}";
}
function onFbInit() {
if (typeof(FB) != 'undefined' && FB != null ) {
FB.Event.subscribe('auth.statusChange', function(response) {
if (response.session || response.authResponse) {
setTimeout(goLogIn, 500);
} else {
window.location.href = "{{ path('_security_logout') }}";
}
});
}
}
</script>
{{ facebook_initialize({'xfbml': true, 'fbAsyncInit': 'onFbInit();'}) }}
UserController.php
* #Route("/facebook/check")
*/
public function loginCheckFacebookAction(){
}
And the provider and the modification to User class (as in the Readme).
But the problem is that the service not seemed to be called. Indeed, the method in the UserController must be a dummy method, and it is not like that here.
The method is called and of course I get this error :
LogicException: The controller must return a response (null given). Did you forget to add a return statement somewhere in your controller? (uncaught exception).
What is my error here ?
Remove your UserController route/action loginCheckFacebookAction

How to go back to referer after login failure?

For login success there is a parameter use_referer: true. For login failure there is only failure_path, which isn't what I'm looking for.
Second thing: How to do that and pass error message?
Third thing: How to go back to referrer after logout?
I solved it.
There is solution: How to disable redirection after login_check in Symfony 2
and here is code which solves my problem:
<?php
namespace Acme\MainBundle\Handler;
use Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
class AuthenticationHandler implements AuthenticationFailureHandlerInterface, LogoutSuccessHandlerInterface
{
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$referer = $request->headers->get('referer');
$request->getSession()->setFlash('error', $exception->getMessage());
return new RedirectResponse($referer);
}
public function onLogoutSuccess(Request $request)
{
$referer = $request->headers->get('referer');
return new RedirectResponse($referer);
}
}
to handle events add to security.yml for example:
form_login:
check_path: /access/login_check
login_path: /
use_referer: true
failure_handler: authentication_handler
logout:
path: /access/logout
target: /
success_handler: authentication_handler
and to config.yml:
services:
authentication_handler:
class: Acme\MainBundle\Handler\AuthenticationHandler

FOSFacebookBundle does not call custom provider

I'm facing big issue while implementing FOSFacebookBundle.
I followed the docs and have following situation:
* when user clicks login a popup appears
* after user grants permission to the app, FB button is being changed (to Logout)
However, my custom provider is not called (only a constructor is called) - yes, I use a noobish debug method (creating empty files with the name of the class method :-)).
Anybody has any suggestion why? Any tips?
Edit
After some time of trying to solve that issue, I feel I'm lost.
Once again, here's my configuration:
app/config/config.yml:
fos_facebook:
file: %kernel.root_dir%/../vendor/facebook/src/base_facebook.php
alias: facebook
app_id: xxx
secret: xxx
cookie: true
permissions: [email, user_location]
app/config/routing.yml:
_security_login:
pattern: /login
defaults: { _controller: TestBundle:Main:login }
_security_check:
pattern: /login_check
defaults: { _controller: TestBundle:Main:loginCheck }
_security_logout:
pattern: /logout
defaults: { _controller: TestBundle:Main:logout }
app/config/security.yml
security:
factories:
-"%kernel.root_dir%/../vendor/bundles/FOS/FacebookBundle/Resources/config/security_factories.xml"
providers:
my_fos_facebook_provider:
id: my.facebook.user
fos_userbundle:
id: fos_user.user_manager
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
login_path: /login
check_path: /login_check
logout: true
anonymous: true
public:
pattern: ^/.*
fos_facebook:
app_url: "http://www.facebook.com/apps/application.php?id=xxx"
server_url: "http://symfonytest.com.dev/app_dev.php/"
login_path: /login
check_path: /login_check
provider: my_fos_facebook_provider
default_target_path: /
anonymous: true
logout: true
I'm also implementing code into twig template as shown in docs (also implemented snippet from #Matt).
I have the same workflow as you and my custom user provider is called correctly and everything is working fine.
The first thing that you need to check is: do you have a JavaScript script that redirects the user to the login_check route after it has successfully login into Facebook via the popup? This is important because calling the login_check route after a valid authentication will trigger the security mechanism of Symfony2 that will call the FOSFacebookBundle special security code that will then call your own custom user provider. I think you may be just missing this small piece.
Here the pieces of JavaScript code required to make it work (using jQuery):
$(document).ready(function() {
Core.facebookInitialize();
});
var Core = {
/**
* Initialize facebook related things. This function will subscribe to the auth.login
* facebook event. When the event is raised, the function will redirect the user to
* the login check path.
*/
facebookInitialize = function() {
FB.Event.subscribe('auth.login', function(response) {
Core.performLoginCheck();
});
};
/**
* Redirect user to the login check path.
*/
performLoginCheck = function() {
window.location = "http://localhost/app_dev.php/login_check";
}
}
I put here my security.yml just to help you check for differences with your own file:
security:
factories:
- "%kernel.root_dir%/../vendor/bundles/FOS/FacebookBundle/Resources/config/security_factories.xml"
providers:
acme.facebook_provider:
# This is our custom user provider service id. It is defined in config.yml under services
id: acme.user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
public:
pattern: ^/
fos_facebook:
app_url: "http://www.facebook.com/apps/application.php?id=FACEBOOK_APP_ID"
server_url: "http://localhost/app_dev.php/"
default_target_path: /
login_path: /login
check_path: /login_check
provider: acme.facebook_provider
anonymous: true
logout: true
And my service definition for the custom user provider we use:
services:
acme.user_provider:
class: Application\AcmeBundle\Security\User\Provider\UserProvider
arguments:
facebook: "#fos_facebook.api"
entityManager: "#doctrine.orm.entity_manager"
validator: "#validator"
You also need to create a new route for the /login_check, /login and /logout paths. Those route will be hooked by Symfony2 for the security process. Here an example of the implementation of the actions in a controller called MainController in my cases:
<?php
namespace Application\AcmeBundle\Controller;
use ...;
class MainController extends Controller
{
/**
* This action is responsible of displaying the necessary informations for
* a user to perform login. In our case, this will be a button to connect
* to the facebook API.
*
* Important notice: This will be called ONLY when there is a problem with
* the login_check or by providing the link directly to the user.
*
* #Route("/{_locale}/login", name = "_security_login", defaults = {"_locale" = "en"})
*/
public function loginAction()
{
if ($this->request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $this->request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $this->request->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render('AcmeBundle:Main:login.html.twig', array(
'error' => $error
));
}
/**
* This action is responsible of checking if the credentials of the user
* are valid. This will not be called because this will be intercepted by the
* security component of Symfony.
*
* #Route("/{_locale}/login_check", name = "_security_check", defaults = {"_locale" = "en"})
*/
public function loginCheckAction()
{
// Call intercepted by the Security Component of Symfony
}
/**
* This action is responsible of login out a user from the site. This will
* not be called because this will be intercepted by the security component
* of Symfony.
*
* #Route("/{_locale}/logout", name = "_security_logout", defaults = {"_locale" = "en"})
*/
public function logoutAction()
{
return $this->redirect('index');
}
}
Hope this help, if you have more questions or I misunderstand something from your problem, don't hesitate to leave a comment.
Regards,
Matt