Closed iamraffe closed 9 years ago
I haven't tried this yet, it's on my backlog for my current project so I'll hopefully get to have a look at it this week. Would be good if @HipsterJazzbo has any pointers!
I don't currently have any need for this so it might be a while till I get round to it. Happy to accept a PR though!
@nicklee1990 @HipsterJazzbo I think I have a working login/register function for Facebook.
I modified the create function in Registrar.php
public function create(array $data)
{
$userSubclass = ParseObject::getRegisteredSubclass('_User');
$user = new $userSubclass;
$user->username = $data['email'];
$user->email = $data['email'];
$user->password = $data['password'];
if(isset($data['name'])){
$fullName = explode(" ", $data['name']);
$user->name = $fullName[0];
$user->lastname = $fullName[1];
}
if(isset($data['id'])){
$user->facebookId = $data['id'];
$user->pictureURL = $data['avatar'];
$user->name = $data['first_name'];
$user->lastname = $data['last_name'];
}
$user->signUp();
return $user;
}
And added a function called fbLogin, in AuthController
public function fbLogin(Request $request){
if($request->has('code')){
$fbUser = \Socialize::with('facebook')->user();
$user = $this->userProvider->retrieveByCredentials($fbUser->user);
if($user == null){
$fbUser->user['password'] = Hash::make($user['name']);
$fbUser->user['avatar'] = $fbUser->avatar;
//return dd($fbUser->user);
$this->auth->login($this->registrar->create($fbUser->user));
return redirect($this->redirectPath());
}
if ($this->auth->loginUsingId($user->objectId, true))
{
return redirect()->intended($this->redirectPath());
}
}else{
return \Socialize::with('facebook')->redirect();
}
}
I am using Socialite, so it must be added with composer first, as well as as a service provider and facade.
Let me know what do you guys think.
This is amazing and is identical to my own needs. I'm going to build a twitter Login function similar to this.
I would've thought this would make use of the ParseUser logInWithFacebook and linkWithFacebook methods so that the authData is correctly populated in Parse and to ensure that the ParseUser is set in the Session?
@raffe90 @oflannabhra perhaps a slightly more Parse integrated implementation could be as follows (looks like a lot more code but i've just padded it out with comments):
/**
* Logs a user in using a social network
* @param $provider
* @param $request
* @return mixed
*/
public function handleReturn(Request $request, $provider)
{
try{
$user = Socialite::driver($provider)->user(); // I use Socialite Alias as per 5.1 docs
$expiration_date = new \DateTime();
$expiration_date->setTimestamp(time() + 86400 * 60); // Set token expiry for below
$existingUser = $this->userProvider->retrieveByCredentials($user->user); // is there a better way to do this??
if($existingUser == null){
$name = Collection::make(explode(' ',$user->getName()));
$data = [
"authData" => [
$provider => [
"id" => $user->getId(), "access_token" => $user->token,
"expiration_date" => ParseClient::getProperDateFormat($expiration_date)
]
], // you'll need to update Registrar to set the authData array
"firstName" => $name->first(), // use first and last in case of middle name
"lastName" => $name->last(),
"email" => $user->getEmail(),
"username" => $user->getEmail(), //this could be different dependent on your app
"profilePicture" => $user->getAvatar(),
"password" => \Hash::make($user->getId()),
"password_confirm" => \Hash::make($user->getId()), //app dependent
];
$this->auth->login($this->registrar->create($data));
}
else{
$data = ["authData" => [
$provider => [
"id" => $user->getId(), "access_token" => $user->token,
"expiration_date" => ParseClient::getProperDateFormat($expiration_date)
]
]];
// Login with parse to get session token
$result = ParseClient::_request("POST", "/1/users", "", json_encode($data));
// This is own subclass but the LaraParse one will do
User::become($result['sessionToken']);
// Login using the user object from Parse
$this->auth->login(User::getCurrentUser());
return redirect()->intended($this->redirectPath());
}
} catch (\Exception $e){
// If user rejects it throws an exception which we can handle here
}
}
/**
* @param $provider
* @return mixed
*/
public function redirectToProvider($provider){
return Socialite::driver($provider)->redirect();
}
Then I use this route:
Route::match(['get','post'],'/auth/login/{provider}','Auth\AuthController@redirectToProvider');
Route::match(['get','post'],'/auth/login/{provider}/return','Auth\AuthController@handleReturn');
I haven't tested this much, only with Facebook. This should work with Twitter however you'll have to ensure that you don't require an email address because annoyingly Twitter doesn't return an email!
@nicklee1990 You are totally right. While still developing I was going through the Parse PHP SDK and came accross the logInWithFacebook method and I also ended up copying the contents into my fbLogin method. I was looking into using that method instead of copying but I did not know what to place in the access_token field, what exactly should go there?
Also, I would like to get rid of is the retrieveByCredentials method and use something else. Any ideas on that?
Lastly, I think there is a typo on your code:
$existingUser = $this->userProvider->retrieveByCredentials($fbUser->user);
Should be
$existingUser = $this->userProvider->retrieveByCredentials($user->user);
If I'm not mistaken.
the access_token you use is just the token you get back from Socialite ($user->token). Using the loginWithFacebook method didn't seem to work for me but in all honesty I didn't spend too long trying to make it work (it throws some Parse Exception). Yeah you're right that's a type in my code, I've updated that now. I split mine into two methods and caught exceptions because otherwise you get stuck in a redirect loop if the user says no on the Social Media site.
As for the retrieveByCredentials method there's different options. I personally use Repositories and have my UserRepository injected into the controller that these methods are in so I just do
$this->user->findBy('username', $user->getId()); // or $user->getEmail() if your username = email
although to be honest using the retrieveByCredentials isn't bad. Also FYI here's an interesting package I've used which seems good http://socialiteproviders.github.io/
Do you guys have more example code? I've got something working, but I feel like it's pretty hacky.
in Auth/ParseUserProvider.php
, I added a method to check for existing user by FBId (I store Facebook Id as a separate column):
public function retrieveByFacebookId($id)
{
$query = new ParseQuery('_User');
$query->equalTo('facebookUserId', $id);
$user = $query->first(true);
return empty($user) ? null : $user;
}
and then use the following code to login an existing user (currently directly in AuthController.php
, but should probably be elsewhere)
$parse_user = User::logInWithFacebook($user->id, $user->token); // exp date set automatically in ParseUser
$current_user = User::become($parse_user->getSessionToken());
$current_user->setEmail($user->email);
$current_user->setUsername($user->email);
$current_user->set('facebookUserId', $user->id);
$current_user->set('firstName', head(explode(' ', $user->name)));
$current_user->set('lastName', last(explode(' ', $user->name)));
\Auth::login(User::getCurrentUser());
return redirect('user');
User
is my custom subclass of Laraparse\Subclasses\User
@nicklee1990 how are you setting the authData when you create the user? I can't get that to work correctly.
@raffe90 are you using the ParseSDK loginWithFacebook()
method? It handles setting all of that automatically.
@oflannabhra I am not using that method. I am using a code similar to what @nicklee1990 posted, but I can't get it to work correctly.
I'd recommend using loginWithFacebook() where possible. the only reason i wasn't using it was because there was a bug which has been fixed by the latest update of the parse sdk. In any case, in Registrar when you are setting the properties on the user, you just need to do:
isset($data['authData']) ? $user->setArray("authData", $data['authData']) : null;
This key is only available in the data array if you set it how i did in the code above i.e.
$data = [
"authData" => [
$provider => [
"id" => $user->getId(), "access_token" => $user->token,
"expiration_date" => ParseClient::getProperDateFormat($expiration_date)
]
],
// And the rest
The thing is, no matter what I try, I think I am having a problem with Socialite.
I keep getting an error "Invalid State Exception", and it's pretty random. Have you guys had any errors like this?
Nope never. Do you want to paste your login and registration code and I'll take a look?
When trying to use the logInWithFacebook() method I get this:
"You must specify a Parse class name or register the appropriate subclass when creating a new Object. Use ParseObject::create to create a subclass object."
I actually copy/pasted your code @oflannabhra and keep getting that message. Did you get that?
Nevermind. I figured it out with the parse php sdk update on line 180.
haha, yeah I did, but submitted a PR to fix it. Glad you got it working!!
hi! how do you solve the "You must specify a Parse class name or register the appropriate subclass when creating a new Object. Use ParseObject::create to create a subclass object." message?
@mnek84 what I did was took a look at the Parse PHP SDK and realized they had changed the ParseUser class, however my files didn't have that updated code.
So, I changed it manually. On line 180, I changed this:
$user = new ParseUser();
with this code:
$user = ParseObject::create('_User');
Just like they did here:
https://github.com/ParsePlatform/parse-php-sdk/commit/83fd79b0fbcca2060916c1b3c3eed03b62ecd8d2
On their latest commit.
Hope that helps
great! change that and it work! thank you @raffe90
Hi, interested to see you have a working login - would someone be able to put together a working PR or even a gist of this code?
Has anyone got it working with Facebook Login/Registration?
That being with Socialite or any of the other alternatives available.