I am trying to set up Socialite in Laravel for facebook logging. I got user logged in , but now I have a problem storing the details in the database.
Also I would like to know how to notice my app that User is logged in? For normal loggin I used native AuthController and everything goes smooth.
Now if user try to log in again, then Laravel automatically redirect it to the url without logging, with appended code like this ?code=AQBCM-KbFqB-VJepSJ-45nFURnsvPPdpdqOu...
So how can I logout the user completely and how can I store the details for the next logging??
This is what I am using but it is not working:
public function getSocialAuth($provider=null)
{
if(!config("services.$provider")) abort('404'); //just to handle providers that doesn't exist
return $this->socialite->with($provider)->redirect();
}
public function getSocialAuthCallback($provider=null)
{
if($user = $this->socialite->with($provider)->user()){
dd($user);
/* This is also not working:
$user1 = new User;
$user1->facebook_id = $user->getId();
$user1->email=$user->email();
$user1->fullname=$user->name();
$user1->provider="facebook";
$user1->save();*/
// not working:
User::create([
'facebook_id' => $user->getId(), 'email' => $user->email(),
'fullname' => $user->name()
]);
}else{
return 'something went wrong';
}
}
Related
Im making a gym app on laravel 9 as an api. The app will have a normal user and also an admin which has access to certain files that a normal user doesnt. For that i created a middleware but im facing a few problems there.
See even after i log in, when i try using a route protected by my isAdmin middleware, i get the error: Log in to gain access. This is the Login function:
public function login(Request $request)
{
$creds = $request->validate([
'email' => 'required|email|string|exists:users,email',
'password' => ['required']
]);
if (!Auth::attempt($creds)) {
return response([ 'message' => 'Provided email or password is incorrect' ], 422);
}
/** #var \App\Models\User $user */
$user = Auth::user();
Auth::login($user);
$token = $user->createToken('main')->plainTextToken;
return response(compact('user', 'token'))->header('Authorization', 'Bearer '.$token);
}
and then this is the middleware
public function handle(Request $request, Closure $next)
{
if (Auth::check()) {
$user = Auth::guard('auth')->user();
dd($user);
if ($user->role == 1) {
return $next($request);
} else{
return response(['msg'=>'You are not authorized for this route!']);
}
} else{
return response(['msg'=>'Log In to gain access!']);
}
return $next($request);
}
Im pretty sure the problem has to do with the token either not being sent or being stored properly, but i cant seem to find where the problem is. Also this is what im doing in Postman if that would help:
Login
Add Recipe (this is a route guarded by the middleware)
And also here are their headers respectively
Also i dont know if this is info that will help but i have to manually write the that bearer token for it to show up. (im a beginner so idk how it should be done)
I have a client which want to save his data encrypted in the database (email, name etc.). He wants to log in with the email too. I made the functionality to log in with encrypted email but the problem is that after log in I am redirected to a blank page with the url /login when I should be redirected to /business-accounts. If I delete manually the /login from the url I am redirected to the /business-accounts which I need to be redirected. Before doing the ecrypted email authentication everything worked fine.
AuthenticatedSessionController.php
public function store(LoginRequest $request)
{
//check user is validated
User::all()->filter(function ($user) use ($request) {
if($user->email == $request->email){
if($user && $user->status==0){
throw ValidationException::withMessages([
'validation' => 'Account not verified.'
]);
}
//get user email crypted for login
$request->merge(['email' => User::find($user->id)->get_email_crypted()]);
$request->authenticate();
$request->session()->regenerate();
//set user session
UserService::set_session($user);
return redirect()->intended(RouteServiceProvider::HOME);
}
});
}
I printed a dd() before the return and seems like everything is working fine till there.
LoginRequest.php
public function authenticate()
{
$this->ensureIsNotRateLimited();
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'email' => __('auth.failed'),
]);
}
RateLimiter::clear($this->throttleKey());
}
RouteServiceProvider.php
public const HOME = '/business-accounts';
Encryption and decryption is made in the User model with get/setEmailAttribute. In the authenticate() method I could see that it is not entering the if where Auth::attempt is located.
I tried to make it work in PasswordResetLinkController too but all I could get is the same blank page with the url /forgot-password and no email received in the inbox.
My Laravel version is 8.x.
L.E. I dumped something before the return redirect() and I saw that in the browser after the login submit I am redirected back to the /login form, so I am thinking that I am getting in a loop or something.
L.E.2.
I somehow resolved this. The thing that I done was changing the crypting method on the email column. I changed the Laravel cripting method with the openssl_crypt function.
User Model
public function setEmailAttribute($value)
{
if (!is_null($value)) {
$this->attributes['email'] = openssl_encrypt($value, "AES-128-ECB", env('EMAIL_ENCRYPT_KEY', false));
}
}
Where the crypting key is located in .env.
AuthenticatedSessionController.php
public function store(LoginRequest $request)
{
//check user is validated
$user = User::where('email', openssl_encrypt($request->email, "AES-128-ECB", env('EMAIL_ENCRYPT_KEY', false)))->first();
if($user && $user->status==0){
throw ValidationException::withMessages([
'validation' => 'Contul nu este verificat'
]);
}
//set email from request to encrypted email
$request->merge(['email' => User::find($user->id)->get_email_crypted()]);
$request->authenticate();
$request->session()->regenerate();
UserService::set_session($user);
return redirect()->intended(RouteServiceProvider::HOME);
}
you can take a look in the log file in /storage/logs/laravel.log for the error message
also, you could change the .env file to show_errors = true and it will show the error in the browser
Use return redirect()->route('your-route-name'); maybe can help you.
In a laravel 5.8 API project, I want users to login via their social accounts. So far I have been able to use Socialite to retrieve user info from the provider and use it to create a new user record. But when I try to have the user log in again, it throws up the following error
Call to undefined method Laravel\Socialite\Two\User::createToken()
Here's the code I am working with
<?php
namespace App\Http\Controllers;
use App\User;
use Socialite;
use App\SocialAccount;
use App\Http\Resources\UserResource;
class SocialAuthController extends Controller
{
...
public function handleProviderCallback($provider)
{
$socialUser = Socialite::driver($provider)->stateless()->user();
$userSocialAccount = SocialAccount::where('provider_id', $socialUser->id)->where('provider_name', $provider)->first();
/*
if account exist, return the social account user
else create the user account, then return the new user
*/
if ($userSocialAccount) {
// generate access token for use
$token = $socialUser->createToken('********')->accessToken;
// return access token & user data
return response()->json([
'token' => $token,
'user' => (new UserResource($userSocialAccount))
]);
} else {
$user = User::create([
'firstname' => $socialUser->name,
'lastname' => $socialUser->name,
'username' => $socialUser->email,
'email_verified_at' => now()
]);
if ($user) {
SocialAccount::create([
'provider_id' => $socialUser->id,
'provider_name' => $provider,
'user_id' => $user->id
]);
}
// assign passport token to user
$token = $user->createToken('********')->accessToken;
return response()->json(['token' => $token, 'user' => new UserResource($user)]);
}
}
}
I haven't been able to spot the reason why I am getting the error when the user attempts a second login but there is no error if it's the first time the user logs in with a social account.
Why does it complain about Laravel\Socialite\Two\User::createToken() method? If I try adding this line use Laravel\Socialite\Two\User vscode intelephsense flags it as a duplicate of App\User so what is really going on in my code?
I think your last sentence hits the problem: the Laravel\Socialite\Two\User and App\User are two fully separate entities.
The Socialite::driver($provider)->stateless()->user() provides you with a Socialite User whereas User::create creates an App\User.
The second $token = $user->createToken('********')->accessToken; works because App\User has the createToken function and the other does not.
First of all the problem I was having with having a token generated by passport for users authentication after the first social login was because I was calling the createToken method on the user returned by Socialite. As explained by #JorisJ1 Socialite does not have the createToken function so my initial code threw an error.
Here's how I fixed it
public function handleProviderCallback($provider)
{
// retrieve social user info
$socialUser = Socialite::driver($provider)->stateless()->user();
// check if social user provider record is stored
$userSocialAccount = SocialAccount::where('provider_id', $socialUser->id)->where('provider_name', $provider)->first();
if ($userSocialAccount) {
// retrieve the user from users store
$user = User::find($userSocialAccount->user_id);
// assign access token to user
$token = $user->createToken('Pramopro')->accessToken;
// return access token & user data
return response()->json([
'token' => $token,
'user' => (new UserResource($user))
]);
} else {
...
}
}
Comments are welcomed if there is a better way for adding social authentication to API.
My first question: is it possible to test this on a localhost? (Because I can imagine that this could be my problem)
I read the docs and did everything like it is described. I browser to /auth/facebook and I get redirected to Facebook where I "accept" the terms. I get redirected to this link: http://localhost:8000/auth/login#_=_
So I tried to debug and did a dd($user); in my Authcontroller, but there was no output given.
public function redirectToProvider()
{
return Socialite::driver('facebook')->redirect();
}
public function handleProviderCallback()
{
$user = Socialite::driver('facebook')->user();
dd($user);
// $user->token;
}
Anyone an idea what I did wrong?
Need to see your routes configuration for more info, but will give it a shot with the info you provided.
Note that you are getting redirected to http://localhost:8000/auth/login#_=_.
That looks like you are getting redirected to your app's login page after authorizing on facebook.
In config/services.php, make sure that, for your facebook login configuration, your redirect url points to a route which you have defined in your routing configuration to handle the callback.
e.g.
// config/services.php
...
'facebook' => [
'client_id' => 'my_facebook_client_id',
'client_secret' => 'my_facebook_client_secret',
'redirect' => 'http://localhost:8000/auth/facebook/callback',
],
...
The route http://localhost:8000/auth/facebook/callback should then be defined in the routing configuration:
// app/Http/routes.php
...
Route::get('auth/facebook/callback', 'Auth\AuthController#handleProviderCallback');
...
Note that the routing config has to allow GET requests since the user will be redirected.
Your dd($user) should work just fine with this setup.
How you save the user data depends on your model architecture.
For instance, in a simple case where you have no other registration method and only use facebook login, you could do this:
public function handleProviderCallback()
{
$facebookData = Socialite::driver('facebook')->user();
// check if already in DB
try{
$user = User::where('facebook_id', $data->id)->firstOrFail();
} catch (Illuminate\Database\Eloquent\ModelNotFoundException $e) {
// create a new user
$user = new User();
// set the properties you want
// $user->facebook_id = $data->id;
// ...
// then save
$user->save();
}
// login the user
Auth::login($user);
// perhaps return a redirect response
return redirect()->action('MyController#myAction');
}
I implement socialite together with manual registration where register using facebook is a user's option. but i noticed that if the user registered first with the manual registration and logout then came back and register using facebook it produce an error
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry"
here is my current UserRepository
namespace App\Repositories;
use App\User;
class UserRepository{
public function findByUsernameOrCreate($userData)
{
return User::firstOrCreate([
'first_name' => $userData->first_name,
'last_name' => $userData->last_name,
'email' => $userData->email
]);
}
}
I recently had this issue. I was testing a pre registered user's ability to login with facebook and have their avatar and email updated when they did.
Assuming you can check for existing (non facebook) user by their email address.
$user = User::where('email', $userData->email)->first();
if ($user){
// Update existing user with facebook data.
$user->email = $userData->email;
// Any other fields you want to update.
$user->avatar = $userData->avatar;
$user->save();
return $user;
}
Then your original function here.
Mark is right, however the data will be updated each time the user connect with Facebook. Here's some improvements :
$authUser = User::where('email', $existingUser->email)->first();
if ($authUser){
// Check and update if Facebook data doesn't exists
if(!($authUser->facebook_id)){
$authUser->facebook_id = $existingUser->id;
$authUser->avatar = $existingUser->avatar;
$authUser->save();
}
return $authUser;
}
// following by your function here...