Auth::user & sessions with Socialite - php

FIXED THE ISSUE
I had <a href='{{ Auth::logout }}'>Logout</a> which caused every page to log the user out.
app/Http/Controllers/SocialAuthFacebookController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Socialite;
use App\Services\SocialFacebookAccountService;
use Auth;
class SocialAuthFacebookController extends Controller
{
/**
* Create a redirect method to facebook api.
*
* #return void
*/
public function redirect()
{
return Socialite::driver('facebook')->redirect();
}
/**
* Return a callback method from facebook api.
*
* #return callback URL from facebook
*/
public function callback(SocialFacebookAccountService $service)
{
$user = $service->createOrGetUser( Socialite::driver('facebook')->user() );
Auth::login($user,true);
return redirect()->to('/');
}
}
app/SocialFacebookAccount.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class SocialFacebookAccount extends Model
{
protected $fillable = ['user_id', 'provider_user_id', 'provider'];
public function user()
{
return $this->belongsTo(User::class);
}
}
app/Services/SocialFacebookAccountService.php
namespace App\Services;
use App\SocialFacebookAccount;
use App\User;
use Laravel\Socialite\Contracts\User as ProviderUser;
class SocialFacebookAccountService
{
public function createOrGetUser(ProviderUser $providerUser)
{
$account = SocialFacebookAccount::whereProvider('facebook')
->whereProviderUserId($providerUser->getId())
->first();
if ($account) {
return $account->user;
} else {
$account = new SocialFacebookAccount([
'provider_user_id' => $providerUser->getId(),
'provider' => 'facebook'
]);
$user = User::whereEmail($providerUser->getEmail())->first();
if (!$user) {
$user = User::create([
'email' => $providerUser->getEmail(),
'name' => $providerUser->getName(),
'password' => md5(rand(1,10000)),
'image' => $providerUser->getAvatar(),
]);
}
$account->user()->associate($user);
$account->save();
return $user;
}
}
}
I am currently using Facebook and that works out so this is not what I'm asking.
I have this snippet on my master.blade.php and it works when I first sign into Facebook however when I go onto another page, such as /about it logs me out. I've tried Auth::viaRemember but this does nothing.
How do I store the user's credential in a session so that users won't get logged out just for moving to another page?!

The facebook api will valid your request and give some basic user details only..you have to store the user details in your db first(after fb api request successfull) and then you have to start session with saved user id (Using AuthLoginUsingID function).

Related

Getting getting cURL error 6 when doing authentication using Socialite in Laravel

I'm using Socialite package in Laravel to do auth. I've uploaded the app to my hosting for so long time and auth always went success. but lately, it runs into some cURL error basically saying GuzzleHttp \ Exception \ ConnectException cURL error 6: getaddrinfo() thread failed to start (see http://curl.haxx.se/libcurl/c/libcurl-errors.html). I have no idea why this is happening. I've read some articles and nothing can solve my problem. Does anyone have an idea how to solve this problem?. Thank you.
Here's my code
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Laravel\Socialite\Facades\Socialite;
class AuthController extends Controller
{
public function redirectToProvider($provider)
{
return Socialite::driver($provider)->redirect();
}
/**
* Obtain the user information from provider. Check if the user already exists in our
* database by looking up their provider_id in the database.
* If the user exists, log them in. Otherwise, create a new user then log them in. After that
* redirect them to the authenticated users homepage.
*
* #return Response
*/
public function handleProviderCallback($provider)
{
$user = Socialite::driver($provider)->user();
$authUser = $this->findOrCreateUser($user, $provider);
Auth::login($authUser, true);
return redirect('/');
}
/**
* If a user has registered before using social auth, return the user
* else, create a new user object.
* #param $user Socialite user object
* #param $provider Social auth provider
* #return User
*/
public function findOrCreateUser($user, $provider)
{
$authUser = User::where('provider_id', $user->id)->first();
if ($authUser) {
return $authUser;
}
else{
$data = User::create([
'name' => $user->name,
'email' => !empty($user->email)? $user->email : '' ,
'provider' => $provider,
'provider_id' => $user->id
]);
return $data;
}
}
}

Laravel: Integrating Throttle in Custom Login

How to integrate laravel throttle if I did not use the default LoginController given by laravel?
here's my controller:
use AuthenticatesUsers;
//function for login
public function login(Request $requests){
$username = $requests->username;
$password = $requests->password;
/**to login using email or username**/
if(filter_var($username, FILTER_VALIDATE_EMAIL)) {
Auth::attempt(['email' => $username, 'password' => $password]);
} else {
Auth::attempt(['username' => $username, 'password' => $password]);
}
if(Auth::check()){
if(Auth::user()->type_user == 0){
return view('users.dashboard');
}
else{
return view('admin.dashboard');
}
}
else{
return Redirect::back()->withInput()->withErrors(['message'=>$login_error],'login');
}
}
I want to limit the failed logins but I can't seem to make it work using my own controller. Can you guys help me please?
add the following code inside your method. make it the first thing
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
now add the following code where log in fails. this will increment the failed attempt count.
$this->incrementLoginAttempts($request);
on successful login, add the following code so it resets.
$this->clearLoginAttempts($request);
Try adding throttling to your controller's constructor, like so:
/**
* Create a new login controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('throttle:3,1')->only('login');
}
Unfortunately, the Laravel docs don't say much about throttling:
https://laravel.com/docs/6.x/authentication#login-throttling
However, the 3,1 part of the string corresponds to a maximum of 3 tries with a decay time of 1 minute.
throttle could be defined in /project-root/laravel/app/Http/Kernel.php in the routeMiddleware array like so:
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,.
The Laravel documentation explains this method here: https://laravel.com/docs/6.x/middleware#assigning-middleware-to-routes
use Trait ThrottlesLogins present in Illuminate\Foundation\Auth and override the 2 functions as mentioned below. I have tested it on Laravel 5.6 and working fine.
public function maxAttempts()
{
//Lock out on 5th Login Attempt
return 5;
}
public function decayMinutes()
{
//Lock for 1 minute
return 1;
}
Although, this answer is very late, but here is , what i did, and it worked. I hope it helps you too. I am using laravel 5.2.
<?php
namespace App\Http\Controllers;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\MessageBag;
use Cookie;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class UserController extends Controller
{
/** Add This line on top */
use AuthenticatesAndRegistersUsers,ThrottlesLogins;
/** This way, you can control the throttling */
protected $maxLoginAttempts=3;
protected $lockoutTime=300;
public function postUserSignIn(Request $request)
{
/** This line should be in the start of method */
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
/** Validate the input */
$validation = $this->validate($request,[
'email' => 'required|email',
'password' => 'required|min:4'
]);
/** Validation is done, now login user */
//else to user profile
$check = Auth::attempt(['email' => $request['email'],'password' => $request['password']]);
if($check){
$user = Auth::user();
/** Since Authentication is done, Use it here */
$this->clearLoginAttempts($request);
if ($user->role == 1 || $user->role == 2){
if(Session::has('cart')){
return redirect()->route('cart');
}
return redirect()->intended();
}elseif($user->role == 99) {
return redirect()->route('dashboard');
}
}else{
/** Authentication Failed */
$this->incrementLoginAttempts($request);
$errors = new MessageBag(['password' => ['Email and/or Password is invalid']]);
return redirect()->back()->withErrors($errors);
}
}
}
Route::post('login', ['before' => 'throttle:2,60', 'uses' => 'YourLoginController#Login']);
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return redirect()->route('login')->with('alert-warning', 'Too many login attempts');
}
protected function hasTooManyLoginAttempts(Request $request)
{
$maxLoginAttempts = 3;
$lockoutTime = 1; // In minutes
return $this->limiter()->tooManyAttempts(
$this->throttleKey($request), $maxLoginAttempts, $lockoutTime
);
}
try my version:
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller{
use AuthenticatesUsers;
public function login(Request $request){
if($this->hasTooManyLoginAttempts($request)){
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}else{
if (Auth::attempt(['username' => $request->login_username, 'password' => $request->login_password])) {
session()->put(['username'=>Auth::user()->username,'userid'=>Auth::user()->id]);
return redirect()->intended('anydashboard');
}else{
$this->incrementLoginAttempts($request);
//my '/' path is the login page, with customized response msg...
return redirect('/')->with(['illegal'=>'Login failed, please try again!'])->withInput($request->except('password'));
}
}
}
}
in order to use Eloquent Model Auth (which is default), your AUTH_MODEL should implements AuthenticatableContract, so double check your model:
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract,CanResetPasswordContract
{
use Authenticatable, CanResetPassword;
//protected $fillable = [];
...
}

Laravel 5.2 send password reset after registration

I have altered my authController.php file to do a few things needed for my project. It works great, just need to make one more change. Right now the controller looks like this:
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use App\Role;
use Mail;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\ResetsPasswords;
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins, ResetsPasswords;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/add';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
/**
* Overwrite the Laravel 5.2 standard registration
* so user will not be logged in automatically.
*
* #param array $request
* #return Register
*/
public function register(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
$this->create($request->all());
return redirect($this->redirectPath());
}
/**
* Extend password reset email to user for when registering
*/
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$this->subject = "First Time User Setup";
$broker->emailView = "auth.emails.password";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::FIRST_TIME_SETUP:
return $this->getSendFirstTimeSetupEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
public function getSendFirstTimeSetupEmailSuccessResponse($response)
{
return redirect()->back()->with('status', trans($response));
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'first-name' => 'required|max:255',
'last-name' => 'required|max:255',
'phone' => 'required|max:255',
'form' => 'max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(Request $request, array $data )
{
//Create the user
$user = User::create([
'first_name' => $data['first-name'],
'last_name' => $data['last-name'],
'phone' => $data['phone'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
return $this->postEmail($request);
/*
Mail::send('auth.emails.registered', ['user' => $user], function ($m) use ($user)
{
$m->to($user->email, $user->first_name)->subject('You Have Been Added');
});*/
//Is it a User? Then give them that role
if ($data['user-role'] == 'user')
{
$role = Role::where('name', '=', 'user')->firstOrFail();
$user = User::find($user->id);
$user->roles()->attach($role->id);
}
//Is it an Admin? Then give them that role
if ($data['user-role'] == 'admin')
{
$role = Role::where('name', '=', 'owner')->firstOrFail();
$user = User::find($user->id);
$user->roles()->attach($role->id);
}
return $user;
}
}
It stops auto login on user creation, assigns a role to the user based on the form and sends a password reset email. The trouble is not I get the error Trait method guestMiddleware has not been applied, because there are collisions with other trait methods on App\Http\Controllers\Auth\AuthController
One of the tricks to this is understanding how the traits inside of the Auth factory work.
First, we'll need to use the use Illuminate\Foundation\Auth\ResetsPasswords; Trait as well as the AuthenticatesAndRegistersUsers and the ThrottlesLogins trait.
use AuthenticatesAndRegistersUsers, ThrottlesLogins, ResetsPassword;
Next, we need to make sure that we have a $request instance being passed to our create method:
protected function create(Illuminate\Http\Request $request, array $data)
Sidenote - I would recommend moving away from passing in a $data object as an argument and instead working with $request. You can get your data like this: $request->get('first-name'), etc.
Finally, we pass our $request to the postEmail() function:
return $this->postEmail($request);
This will pipe the $request to the ResetsPasswords\postEmail function:
public function postEmail(Request $request)
{
return $this->sendResetLinkEmail($request);
}
Which will inturn pipe it to the ResetsPasswords\sendResetLinkEmail function:
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$response = Password::broker($broker)->sendResetLink(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
Which will ultimately send out an email.
The key to making this entire thing work is that the instance of Illuminate\Http\Request always contains an email field.
Note
The response that will be returned from the sendResetLinkEmail function may not be the same response that you sent out. Perhaps Users are confused by a Password Reset request when they've just created their account. Instead, maybe you want to send a First Time Setup. In order to do this, you will need to create your own Password Broker and Password Facade.
Next, lets make 2 new directories:
App\Brokers
App\Facades
Next, make a new file inside of App\Facades and call it Password.php. Namespace the file accordingly and extend the existing Password Facade. Also, we'll add another const as an observable response type for our FIRST_TIME_SETUP.
<?php
namespace App\Facades;
class Password extends \Illuminate\Support\Facades\Password {
/**
* Constant representing a successfully sent reminder.
*
* #var string
*/
const FIRST_TIME_SETUP = 'passwords.first_time_setup';
}
Now we have added another response type that we can switch on which will help dictate how we send out our email.
Next, we need to make our Password Broker and establish our sendFirstTimeSetup functionality.
namespace App\Brokers;
class PasswordBroker extends Illuminate\Auth\Passwords\PasswordBroker {
public function sendFirstTimeSetup(array $credentials, Closure $callback = null)
{
// First we will check to see if we found a user at the given credentials and
// if we did not we will redirect back to this current URI with a piece of
// "flash" data in the session to indicate to the developers the errors.
$user = $this->getUser($credentials);
if (is_null($user)) {
return static::INVALID_USER;
}
// Once we have the reset token, we are ready to send the message out to this
// user with a link to reset their password. We will then redirect back to
// the current URI having nothing set in the session to indicate errors.
$token = $this->tokens->create($user);
$this->emailResetLink($user, $token, $callback);
return static::FIRST_TIME_SETUP;
}
}
Now we need to copy the sendResetLinkEmail function that we saw earlier, and move it into our AuthController. This will allow us to modify the Password Facade that we use, and modify our switch statement to support our First Time Setup
public function sendResetLinkEmail(Request $request)
{
$this->validateSendResetLinkEmail($request);
$broker = $this->getBroker();
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
switch ($response) {
case Password::RESET_LINK_SENT:
return $this->getSendResetLinkEmailSuccessResponse($response);
case Password::FIRST_TIME_SETUP:
return $this->getSendFirstTimeSetupEmailSuccessResponse($response);
case Password::INVALID_USER:
default:
return $this->getSendResetLinkEmailFailureResponse($response);
}
}
Then, also in the AuthController, we'll make another function to return our response:
public function getSendFirstTimeSetupEmailSuccessResponse($response)
{
return redirect()->back()->with('status', trans($response));
}
Finally, if you want to override the view that is used by the function when being sent out, simply override the $broker->emailView property before invoking the ->sendFirstTimeSetup() function:
//...
$broker->emailView = "emails.user.first_time_setup";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
//...
If you want to change the subject of the email, override the ->subject() property inside of your AuthController prior to firing your ->sendFirstTimeSetup() function:
//...
$this->subject = "First Time User Setup";
$broker->emailView = "emails.user.first_time_setup";
$response = Password::broker($broker)->sendFirstTimeSetup(
$this->getSendResetLinkEmailCredentials($request),
$this->resetEmailBuilder()
);
//...
I could go on and on, but I think this will get you going in the right direction. Hopefully it will help a few others as well.
You can fix your trait collision issue by changing the use block to the following:
use ThrottlesLogins,
ResetsPasswords,
AuthenticatesAndRegistersUsers {
AuthenticatesAndRegistersUsers::guestMiddleware insteadof ResetsPasswords;
AuthenticatesAndRegistersUsers::getGuard insteadof ResetsPasswords;
AuthenticatesAndRegistersUsers::redirectPath insteadof ResetsPasswords;
}
It's ugly, but it will fix all related collisions, thus allowing you to just use the pre-built Illuminate methods if you so choose.

laravel5.1 Manually Authenticating Users

In my app I use the default authentification to login my users to their dashboard.
Now I would like to create a backend for supporters and one for admins.
I use this code at the moment to login as admin:
<?php
namespace App\Http\Controllers\Admin;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App;
use Artisan;
use Validator;
use Auth;
class AdminLoginController extends Controller
{
public function index()
{
return view('admin.login');
}
public function authenticate(Request $request)
{
if (Auth::attempt(['username' => $request->username, 'password' => $request->password, 'id' => 1]))
{
// Authentication passed...
dd("correct");
}
else
{
dd("login data incorrect!");
}
}
}
How can I use now the well known redirects after login.
In my AuthController I use for the users I use this code:
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/dashboard';
protected $redirectAfterLogout = 'auth/login';
protected $redirectPath = '/dashboard';
1.) How can I use this in my code above? I would like to redirect admins logged into the app like users to specified urls using this laravel feature.
2.) What's the best way, to check more "ids" in the if statement? Because there will be more than one admin.
first of all you are checking username, password along with a ID. So only user with Id = 1 is going to be authenticated by your function. You dont need to put an id check there.
Now for the redirect you can make a function which returns back the url based on the user type. Here is a very basic example`
public function getRedirectUrl(){
if(auth()->user()->isAdmin()){
return url('/admin-dashboard');
}
elseif(auth()->user()->isUser()){
return url('/user-dashboard')
}
}
Make sure you have isAdmin() and isUser() function defined in your User model like
public function isAdmin(){
return $this->user_type == UserType::ADMIN;
}
So in your authentication function just do return redirect($this->getRedirectUrl())

Laravel 5 - why is input empty when returning with errors?

I have a form that submits to a controller, which validates the data. If the validation fails it redirects back with the input and the errors. This is the method that deals with the form submission:
<?php namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class UserController extends Controller {
/**
* Create a new user.
*
* #param Reqeust $request
*
* #return Void
*/
public function postCreate(Request $request)
{
$user = new User;
$rules = $user->rules();
$rules['password'] = 'required|confirmed|min:8';
$v = \Validator::make($request->except('_token', 'roles'), $rules);
if ($v->fails())
{
return redirect()->back()->withInput($request->except('_token', 'password', 'password_confirmation'))->withErrors($v);
}
$user->fill($request->except('_token', 'password', 'password_confirmation'));
$user->password = \Hash::make($request->input('password'));
$user->save();
return redirect()->route('webmanAccounts')->with('messages', [['text' => 'User account created', 'class' => 'alert-success']]);
}
On the page that displays the form I check to see if name, one of the fields, is present and if so populate a User object with the data. The problem is input is always empty.
<?php namespace BackEnd;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Request as RequestFacade;
use App\Http\Controllers\Controller;
use App\Models\Role;
use App\Models\User;
class UserController extends Controller {
public function __construct(Request $request)
{
if ( ! $request->user()->can('accounts'))
{
return abort(403, 'You do not have permission to access this page.');
}
}
/**
* Display the create new user form and process any error messages.
*
* #param Reqeust $request
*
* #return View
*/
public function create(Request $request)
{
$user = new User;
dump(RequestFacade::all());
if (RequestFacade::has('name'))
{
$user->fill(RequestFacade::except('_token', 'roles'));
foreach (RequestFacade::only('roles') as $role)
{
$user->roles()->add($role);
}
}
return view('backend.user.create', ['title' => 'Website Manager :: Create New Account', 'user' => $user, 'roles' => Role::all()]);
}
I have tried RequestFacade, $request and Input, all show as empty. Why isn't the data being passed back?
To add to the strangeness of this, I have another project that uses almost identical code and that works perfectly fine. Why would it work fine for one project but not for another!?
When you use the withInput() method, the data is flashed to the session as "old" data.
$request->old() should give you an array of all the "old" data.
$request->old('name') should give you the "old" name data.

Categories