I've read documentations and examples from few sources but still can't get this HWIOAuthBundle works. I've login form using user ID and password which added manually in Admin section that already works fine.
I want to add Binding Facebook Button in User Area after they login successfully, so they can login with normal ID/Password either Facebook Login in future. I've searching about this HWIOAuthBundle but can't find any similar case like this.
My Security :
security:
encoders:
Sifo\UserBundle\Entity\Student: plaintext
providers:
user_area:
entity: { class: SifoUserBundle:Student, property: code }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
anonymous: true
login:
pattern: ^/user/login$
security: false
user_area:
pattern: ^/user
anonymous: false
provider: user_area
form_login:
check_path: /user/login_check
login_path: /user/login
logout:
path: /user/logout
target: /user
user_area_socials:
anonymous: false
oauth:
resource_owners:
facebook: "/login/check-facebook"
login_path: /login
use_forward: false
failure_path: /login
oauth_user_provider:
service: hwi_oauth.user.provider.entity
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/connect, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user/, roles: ROLE_USER }
Student Entity :
<?php
namespace Sifo\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Student
*/
class Student implements UserInterface, \Serializable
{
/**
* @var integer
*/
private $id;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $code;
/**
* @var string
*/
private $facebookId;
/**
* @var string
*/
private $facebookAccessToken;
app/config/routing.yml
hwi_oauth_redirect:
resource: "@HWIOAuthBundle/Resources/config/routing/redirect.xml"
prefix: /user
hwi_oauth_login:
resource: "@HWIOAuthBundle/Resources/config/routing/login.xml"
prefix: /login
facebook_login:
pattern: /login/check-facebook
app/config/config.yml
hwi_oauth:
# name of the firewall in which this bundle is active, this setting MUST be set
firewall_name: user_area_socials
resource_owners:
facebook:
type: facebook
client_id: XXXXX73105XXXXX
client_secret: XXXXX5534ce8d0c50893fbb9c45XXXXX
scope: "email"
services:
hwi_oauth.user.provider.entity:
class: HWI\Bundle\OAuthBundle\Security\Core\User\OAuthUserProvider
Login Form (login.html.twig) :
<form class="form-signin" action="{{ path('user_login_check') }}" method="post">
<h2 class="form-signin-heading">sign in now</h2>
<div class="login-wrap">
<input type="text" class="form-control" placeholder="User ID" autofocus id="username" name="_username" />
<input type="password" class="form-control" placeholder="Password" id="password" name="_password" />
<label class="checkbox">
<input type="checkbox" value="remember-me"> Remember me
<span class="pull-right">
<a href="{{ path('public_default') }}"><i class="icon-home"></i> Back to home</a>
</span>
</label>
<button class="btn btn-lg btn-login btn-block" type="submit">Sign in</button>
<p>or you can sign in via social network</p>
<div class="login-social-link">
<a href="{{ path('user_facebook_login') }}" class="facebook">
<i class="icon-facebook"></i>
Facebook
</a>
<a href="{{ path('user_twitter_login') }}" class="twitter">
<i class="icon-twitter"></i>
Twitter
</a>
</div>
</div>
</form>
Routing for user_facebook_login
:
user_facebook_login:
pattern: /login_facebook
defaults: { _controller: "SifoUserBundle:Default:facebook" }
My Controller :
<?php
namespace Sifo\UserBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Sifo\UserBundle\Form\DefaultType;
class DefaultController extends Controller
{
public function facebookAction()
{
return $this->render('SifoUserBundle:Default:facebook.html.twig');
}
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
return $this->render('SifoUserBundle:Default:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
}
facebook.html.twig :
<!DOCTYPE html>
<html lang="en">
<head>
</head>
<body class="login-body">
<script>
// This is called with the results from from FB.getLoginStatus().
function statusChangeCallback(response) {
console.log('statusChangeCallback');
console.log(response);
// The response object is returned with a status field that lets the
// app know the current login status of the person.
// Full docs on the response object can be found in the documentation
// for FB.getLoginStatus().
if (response.status === 'connected') {
// connected
document.location = "{{ url("hwi_oauth_service_redirect", {service: "facebook"}) }}";
} else if (response.status === 'not_authorized') {
// not_authorized
FB.login(function(response) {
if (response.authResponse) {
document.location = "{{ url("hwi_oauth_service_redirect", {service: "facebook"}) }}";
} else {
alert('Cancelled.');
}
}, {scope: 'email'});
} else {
// The person is not logged into Facebook, so we're not sure if
// they are logged into this app or not.
document.getElementById('status').innerHTML = 'Please log ' +
'into Facebook.';
}
}
// This function is called when someone finishes with the Login
// Button. See the onlogin handler attached to it in the sample
// code below.
function checkLoginState() {
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
}
window.fbAsyncInit = function() {
FB.init({
appId : 'XXXXX73105XXXXX',
cookie : true, // enable cookies to allow the server to access
// the session
xfbml : true, // parse social plugins on this page
version : 'v2.0' // use version 2.0
});
// Now that we've initialized the JavaScript SDK, we call
// FB.getLoginStatus(). This function gets the state of the
// person visiting this page and can return one of three states to
// the callback you provide. They can be:
//
// 1. Logged into your app ('connected')
// 2. Logged into Facebook, but not your app ('not_authorized')
// 3. Not logged into Facebook and can't tell if they are logged into
// your app or not.
//
// These three cases are handled in the callback function.
FB.getLoginStatus(function(response) {
statusChangeCallback(response);
});
};
// Load the SDK asynchronously
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
// Here we run a very simple test of the Graph API after login is
// successful. See statusChangeCallback() for when this call is made.
function testAPI() {
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Successful login for: ' + response.name);
document.getElementById('status').innerHTML =
'Thanks for logging in, ' + response.name + '!';
});
}
</script>
</body>
</html>
You need to create your own provider, which implements OAuthAwareUserProviderInterface
. In loadUserByOAuthUserResponse
method from $response
parameter you can get any info about Facebook-user. Example:
class MyOAuthProvider implements UserProviderInterface, OAuthAwareUserProviderInterface
{
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$token = $response->getAccessToken();
$facebookId = $response->getUsername(); // Facebook ID, e.g. 537091253102004
$username = $response->getRealName();
$email = $response->getEmail();
// search user in database
$result = $this->em->getRepository('AppUserBundle:User')->findOneBy(
array(
'facebookId' => $facebookId
)
);
if(!$result) {
$user = new User();
$user->setEmail($email);
$user->setUsername($username);
$user->setFacebookId($facebookId);
// ..
// save to database instructions
// ..
}
return $this->loadUserByUsername($username);
}
// other methods
}
Don't forget specify your custom provider in app/config/config.yml
services:
app.user.provider:
class: App\UserBundle\Entity\MyOAuthProvider
and in app/config/security.yml
firewalls:
...
user_area_socials:
oauth:
oauth_user_provider:
service: app.user.provider
Read more in docs: How to Create a custom User Provider