My registration form additionally accepts the name of the user's company, which I want to insert in a separate table "Holdings". All data is successfully saved to the Holdings and Users table, but an error occurs at the last step when redirecting to the home page.
ResgisterController:
protected function create(array $data)
{
//Mail::to($data['email'])->send(new Welcome($data['name']));
$id = User::insertGetId([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'role' => 8
]);
$oHolding = new Holdings;
$oHolding->shortname = $data['orgname'];
$oHolding->creator = $id;
$oHolding->save();
DB::table('users')->where('id', $id)->update([
'holding_id' => $oHolding->id,
]);
$user = DB::table('users')->select('*')->where('id', $id)->get();
return $user;
}
Error Message:
Argument 1 passed to Illuminate\Auth\SessionGuard::login() must implement interface Illuminate\Contracts\Auth\Authenticatable, instance of Illuminate\Support\Collection given, called in /Users/admin/Sites/jetime/vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php on line 35
Login Function
public function login(AuthenticatableContract $user, $remember = false)
{
$this->updateSession($user->getAuthIdentifier());
// If the user should be permanently "remembered" by the application we will
// queue a permanent cookie that contains the encrypted copy of the user
// identifier. We will then decrypt this later to retrieve the users.
if ($remember) {
$this->ensureRememberTokenIsSet($user);
$this->queueRecallerCookie($user);
}
// If we have an event dispatcher instance set we will fire an event so that
// any listeners will hook into the authentication events and run actions
// based on the login and logout events fired from the guard instances.
$this->fireLoginEvent($user, $remember);
$this->setUser($user);
}
I will be glad of any help, how to fix the error?
You are returning a collection rather than an instance of a class that implements Authenticatable.
You can see this happening here:
$user = DB::table('users')->select('*')->where('id', $id)->get();
return $user;
If you have the User model that ships with Laravel, then you'll actually want to do:
$user = User::find($id);
return $user;
Although your whole create method could be cleaned up to streamline all of this, however that isn't the topic of your question.
Related
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.
I am working on an assignment in laravel where I've an Application form. I want to submit application form with email, mobileNo, customerId etc.
What I want is to insert form data into users table and then user will be logged in with auto generated password and redirect to Customer's dashboard. Where will be a modal will be open and ask for add password.
On the other hand there is also a login page from where user can login as usual. The login functionality is working properly.
Can someone help me to achieve the above functionality. Thanks in advance.
**Data is : **
email='user#gmail.com'
mobile='9875425698'
customerId='CI10001';
ApplicationForm Controller Where I am getting data successfully
class ApplicationForm extends Controller
{
public function saveApplicationForm(Request $request){
return $request;
}
}
Add user by submiting form
$password = bcrypt('secret'); //add here random password
$user = new User();
$user->email = 'xyz#gmail.com';
$user->mobileNo = '123456789';
$user->customerId = '1245';
$user->password = $password;
$user->save();
after you insert raw on user table login by user id without password
Auth::loginUsingId($user->id);
Auth::loginUsingId($user->id,true); // Login and "remember" the given user...
by otherwise login with email and password
Auth::attempt(['email' => $user->email, 'password' => $password], $remember);
all action do in one method(action)
Following my comment:
In the RegisterController (App\Http\Controllers\Auth)
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'institution' => $data['institution'],
'password' => 'NOT_SET',
]);
}
Then create a middleware (e.g. php artisan make:middleware Must_have_password)
namespace App\Http\Middleware;
use Closure;
use Auth;
class Must_have_password
{
/**
* Verify if password is set, otherwise redirect to password-set page.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$user = Auth::user();
if ($user && $user->password !== 'NOT_SET') {
return $next($request);
}
else return redirect('/set-password');
}
}
Of course, you then need to create a password setting view and hook that to the /set-password route. As I said in the comment, you want to make sure that /set-password route is well protected because you don't want people hijacking accounts that way. The good thing about this approach (using NOT_SET) is that people can always use the password_reset infrastructure to reset their password if they don't do it initially.
This is a bit hacky, but because Laravel always encrypts the passwords, there is no way the value can become NOT_SET in another way. Alternatively, you could add a boolean to your user-model (something like Must_Reset) that redirects to the password-reset page.
You can also hook in the password-reset functionality of Laravel, look for 'One Time Password Laravel' (e.g. here).
I tried to set the session but the user_id column is always null. Other columns are working just fine. Is user_id reserved for anything? Did this happen to anyone?
What I am trying here is to authenticate the user,save the data to session and redirect to dashboard.
Here is the code:
public function login(Request $request)
{
$validator = validator::make($request->all(),[
'username' => 'required',
'password' => 'required'
]);
if($validator->fails()){
return redirect('/')
->withInput()
->withErrors($validator);
}
$username = $request->get('username');
$user = User::where('username', $username)->first();
$checkpass = false;
if(!empty($user)){
$password = $request->get('password');
$dbpass = $user->getAttributes()['pass'];
$checkpass = Hash::check($password, $dbpass);
if($checkpass){
//saving session after success login check
$request->session()->put('id',\Session::getId());
$request->session()->put('user_id', $user->id);
$request->session()->put('ip_address', $request->ip());
$request->session()->put('user_agent', $request->header('User-Agent'));
$request->session()->put('payload', base64_encode($request->getContent()));
$request->session()->put('last_activity', time());
//redirect to dashboard
return redirect('/dashboard');
}
}
if(empty($user) || $checkpass == false){
$errors = new MessageBag(['password'=>['Email and/or password invalid.']]);
return redirect('/')
->withErrors($errors)
->withInput(Input::except('password'));
}
}
You are using the database session driver. Laravel will thus handle sessions via the Illuminate\Session\DatabaseSessionHandler.php. If you check out the source, you will find a method getDefaultPayload which is called whenever you write/save the session to the database.
Inside this method you will find this:
if ($container->bound(Guard::class)) {
$payload['user_id'] = $container->make(Guard::class)->id();
}
which sets the user_id to the currently logged in user via Laravel's Guard. However, since you are not logging in with the Guard, this method will see the current user id as null and that is why you always get null.
Furthermore, in truth none of the properties you are setting are actually being set. Instead, they are all being set in the getDefaultPayload method of DatabaseSessionHandler.php.
This is where you should start in figuring out the best way to integrate Laravel's database session with your implementation. As I don't have more information about your application, I can't give you further direction as what the best solution might be and what would be the best change to make. Without knowing anything though, I might look into integrating your login system with the Laravel Guard because it does provide some other functionality as well. Or, a quicker solution might be to just directly modify this bit of the getDefaultPayload method.
You can do this under Illuminate\Session\DatabaseSessionHandler.php
protected function performUpdate($sessionId, $payload)
{
if(!Auth::check())
{
unset($payload['user_id']);
}
return $this->getQuery()->where('id', $sessionId)->update($payload);
}
It will fix if you using Auth::attempt because when you attempt the Auth facade laravel will set the user_id on the Session table/file/etc, something like this:
if(Auth::attempt([
'email' => $email,
'password' => $password
])) {
$user = Auth::user();
return [
'user' => $user,
];
}
Try this solution by making some changes Illuminate\Session\DatabaseSessionHandler.php
/**
* Add the user information to the session payload.
*
* #param array $payload
* #return $this
*/
protected function addUserInformation(&$payload)
{
if ($this->container->bound(Guard::class)) {
$payload['user_id'] = $this->userId();
}
// UPDATE "user_id" IN TABLE "sessions" USING YOUR OWN SESSION DATA [BEGIN]
if(\Session::has('login_id')){
$my_user = \Session::get('login_id');
$payload['user_id'] = $my_user;
}
// UPDATE "user_id" IN TABLE "sessions" USING YOUR OWN SESSION DATA [END]
return $this;
}
But make sure the logged-in session has been created by your own auth function first.
Sample dd(Session::all); of my application:
array:8 [
"_token" => "VSI617e5HjtrYnj59hGPG9AS0R7y5vvUA1fQ7mV6"
"language" => "EN"
"_previous" => array:1 []
"_flash" => array:2 []
"login_id" => "123"
]
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';
}
}
I'm trying to create a user with the Zizaco/confide library, the user data comes from Facebook and not from a form.
I try this :
$user_profile = Facebook::api('/me','GET');
$new_user = new User;
$new_user->username = $user_profile['name'];
$new_user->password = Hash::make('secret');
$new_user->email = $user_profile['email'];
$new_user->confirmation_code = '456456';
$new_user->confirmed = true;
$new_user->save();
but it doesn't save the user. Any help ?
I found the problem, the confide library sets some default rules to create the user, you need to pass this rules to save a user:
public static $rules = array(
'username' => 'required|alpha_dash|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|between:4,11|confirmed',
'password_confirmation' => 'between:4,11',
);
with this example it works:
$user = new User();
$user->username = 'asdasd';
$user->email = 'angorusadf#gmail.com';
$user->password = '12312312';
$user->password_confirmation = '12312312';
$user->save();
Mabe the method should give you some information when you don't pass the rules.
Maybe it's a pretty late answer, but I'm posting this for future reference because I was myself looking for an answer to the same question, How to create a user programatically in zizaco/confide? or more generally, how to bypass zizaco/confide's requirement for setting a password when saving a user for the first time?
well, the answer is pretty simple, thanks to the new architecture which Zizaco implemented in the 4.* branch, it's now possible to register a new user validator class see more at the package's readme in short all you need in this very case though, is just to extend the current User validator and override validatePassword() to make it accept empty passwords for new users.
Below is an example implementation:
In routes.php
// we need to register our validator so that it gets used instead of the default one
// Register custom Validator for ConfideUsers
App::bind('confide.user_validator', 'MyUserValidator');
In app/models/MyUserValidator.php (that's basically a copy of the function in the class, simply just added a check whether this is a old user or not (if the user has an ID then this is an update operation) if this is a new user, the method always returns true!
/**
* Class MyUserValidator
* Custom Validation for Confide User
*/
class MyUserValidator extends \Zizaco\Confide\UserValidator //implements \Zizaco\Confide\UserValidatorInterface
{
/**
* Validates the password and password_confirmation of the given
* user
* #param ConfideUserInterface $user
* #return boolean True if password is valid
*/
public function validatePassword(\Zizaco\Confide\ConfideUserInterface $user)
{
$hash = App::make('hash');
if($user->getOriginal('password') != $user->password && $user->getOriginal('id')) {
if ($user->password == $user->password_confirmation) {
// Hashes password and unset password_confirmation field
$user->password = $hash->make($user->password);
unset($user->password_confirmation);
return true;
} else {
$this->attachErrorMsg(
$user,
'validation.confirmed::confide.alerts.wrong_confirmation',
'password_confirmation'
);
return false;
}
}
unset($user->password_confirmation);
return true;
}
}