Ask User's Password When Logging with Social Account - Laravel Socialite - laravel

I have fully functional Socialite social login with Google and Facebook but i want to add another step that is "ask a password before logging in".
This function logs the user immediately after successfully logging in with the provider:
public function callback(Request $request, $provider, SocialAccountsService $service)
{
if ($request->has("error")) {
return redirect()->route("website.index");
}
try {
$user = $service->createOrGetUser(
Socialite::driver($provider)->user(),
$provider
);
auth()->login($user);
} catch (InvalidStateException $e) {
Log::error($e->getMessage());
return redirect()->route("auth.register.index");
}
return redirect()->route("dashboard.profile.index");
}
What should i do so when they login with the provider they must insert a password before logging to my website?

Okey, you want to create user but do not want to log it. Than:
Delete auth()->login($user);
Add something like session()->put('created_social_user_id', $user->id);
Create PasswordCreation controller and view and redirect user from callback to it
In this controller - use session, get user_id, find before created model and ask user about password.
After password creation - add auth()->login($user); and redirect to dashboard

Related

Create session on consuming login with api on laravel

I have an api that has a method to start and I am calling it from a frontend project.
In the front end project I use Guzzle to make the call via post to the api and login, from which I get back a json with the user data and a jwt token.
But when I receive the token as I manage the session, I must create a session and save the token, since the laravel to authenticate I need a model user and have a database, which of course I do not have in this backend because I call the api to log in, which brings a token and user data, then as I manage it from the backend, I'm a little lost there.
$api = new Api();
$response = $api->loginapi(['user'=>'wings#test.com','password'=>'123']);
Because here I could not do Auth::login($user) to generate the session.
Because I don't have here the database because the login is done from the api.
There I call the api, of which the answer is the token, but how do I manage it from here, creating a session? saving the token?
thanks for your help.
With api, you don't usually manage a session. usually, you'd call something like
Auth::attempt([
'email' => 'me#example.com',
'password' => 'myPassword'
]);
If the credentials are correct, laravel will include a Set-Cookie header in response, and, that is how you authenticate with api. Via an auth cookie. You don't need to do anything else.
Let's show you how:
//AuthController.php
public function login(Request $request) {
$validatedData = $request->validate([
'email' => 'required|email',
'password' => 'required'
]);
if(Auth::attempt($validatedData)){
return ['success' => 'true'];
}
else{
return ['success' => false, 'message' => 'Email or password Invalid'];
}
}
public function currentUser (){
return Auth::user();
}
Now, the APi file
Route::post('/login', ['App\Http\Controllers\AuthController', 'login']);
Route::get('/current_user', ['App\Http\Controllers\AuthController', 'currentUser']);
Now if you make a call to /api/current_user initially, you'll get null response since you're not currently logged in. But once you make request to /api/login and you get a successful response, you are now logged in. Now if you go to /api/current_user, you should see that you're already logged in.
Important ::
If you are using fetch, you need to include credentials if you're using something other than fetch, check out how to use credentials with that library or api
You want to use the API to authenticate and then use the SessionGuard to create session including the remember_me handling.
This is the default login controller endpoint for logging in. You don't want to change this, as it makes sure that user's do not have endless login attempts (protects for brut-force attacks) and redirects to your current location.
public function login(Request $request)
{
$this->validateLogin($request);
// 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 (method_exists($this, 'hasTooManyLoginAttempts') &&
$this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
if ($request->hasSession()) {
$request->session()->put('auth.password_confirmed_at', time());
}
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
The core happens when we try to "attemptLogin" at
protected function attemptLogin(Request $request)
{
return $this->guard()->attempt(
$this->credentials($request), $request->boolean('remember')
);
}
When using the SessioGurad (which is default) the method attemptLogin fires a couple of events, checks if the user has valid credentials (by hashing the password and matching it with db) and then logs the user in, including the remember me functionality.
Now, if you don't care about events, you can just check from your API if the credentials match and then use the login method from the guard. This will also handle the remember me functionality. Something like this:
protected function attemptLogin(Request $request)
{
$username = $request->input($this->username());
$password = $request->input('password');
$result = \Illuminate\Support\Facades\Http::post(env('YOUR_API_DOMAIN') . '/api/v0/login' , [
'username' => $username,
'password' => $password
])->json();
if(empty($result['success'])){
return false;
}
// Maybe you need to create the user here if the login is for the first time?
$user = User::where('username', '=', $username)->first();
$this->guard()->login(
$user, $request->boolean('remember')
);
return true;
}

Laravel Login Issue for multiple roles

I have added multiple roles authentication in laravel 8.6. Used common middleware for this but occurring issues for login.
i.e If i logged in as admin (role id 1) it will redirected successfully to dashboard. But if you logged out and login as user (role id 2) it will redirect to login page again and the on second time it will redirected to dashboard on second attempt.
I need solution for it.
In your login controllers authenticated method,
If you are using spatie/larave-permission package
$user = Auth::user();
if ($user->hasRole('super-admin')) {
return redirect()->route('dashboard');
}
if ($user->hasRole('admin')) {
return redirect()->route('login');
}
If using self role middleware
$user = Auth::user();
if ($user->role_id === 1) {
return redirect()->route('dashboard');
}
if ($user->role_id === 2) {
return redirect()->route('login');
}
But you can not force a logged-in user to log in again. What is your plan? I strongly advise you to try something else.

Laravel SAML - user automatically logged out after log in

I have connected my Laravel app to the Azure, and I'm using the SAML2 protocol for user authentication. The issue which i have is that user is logged in application (Auth::login($user)), and after that when printing auth()->user() i get logged in user object. However, somehow user session is destroyed after that, and the user is redirected to the login page. Callback for SAML response is located in a service provider boot() method and looks like this:
public function boot()
{
Event::listen('Aacotroneo\Saml2\Events\Saml2LoginEvent', function (Saml2LoginEvent $event) {
$messageId = $event->getSaml2Auth()->getLastMessageId();
// Add your own code preventing reuse of a $messageId to stop replay attacks
$user = $event->getSaml2User();
$userMap = config('saml2_settings.user_map');
$emailAddress = $user->getAttribute($userMap['email']);
$laravelUser = User::where('email', '=', $emailAddress[0])->first();
if ($laravelUser) {
Auth::login($laravelUser);
return;
}
$azureService = new AzureService();
$newUser = $azureService->createNewUserFromSaml($userMap, $user);
if ($newUser){
Auth::login($newUser);
}
});
}

How to authorize user in laravel

We have auth microservice and an admin panel based on laravel. We login user by calling auth api and returning user id and token. How I could make user as authorized in laravel 5.3? I did not found any information googling.
$request = Request();
$authorize = new Authorize();
$response = $authorize->logIn($request->all());
if ($response->status == 'success') {
$user = new User();
$userResponse = $user->getUser($response->data->id);
Session::put('userId', $response->data->id);
Session::put('userToken', $response->data->token);
Session::put('userData', $userResponse);
if ($request->input('save_login')) {
Cookie::queue(Cookie::make('savedLogin', $request->input('login'), 129600, null, null, false, false));
} else {
Cookie::queue(Cookie::forget('savedLogin'));
}
return redirect('/');
}
You can manually log in the user with either the id or the User Instance.
From the docs:
Authenticate A User Instance
If you need to log an existing user instance into your application,
you may call the login method with the user instance. The given object
must be an implementation of the
Illuminate\Contracts\Auth\Authenticatable contract. Of course, the
App\User model included with Laravel already implements this
interface:
Auth::login($user);
// Login and "remember" the given user...
Auth::login($user, true);
Authenticate A User By ID
To log a user into the application by their ID, you may use the
loginUsingId method. This method accepts the primary key of the user
you wish to authenticate:
Auth::loginUsingId(1);
// Login and "remember" the given user...
Auth::loginUsingId(1, true);

Cartalyst Sentry and registration user

It is possible to create user from Admin panel, by administrator without password? I imagine follow procedure:
Administrator create user without password
User get email with instruction for entering password and activation account
User can register with email and his password
I don't think so. That's why when I create my users I generate a random password.
$user->password = str_shuffle("Random_Password"); // generate random initial password
I have done this before by hacking the 'forgotten password' functionality of Laravel (rather that reinventing the wheel). I can't say how well this fits into Sentry but it was pretty trivial to do it in plain old Laravel:
Create user with blank password
Add an entry into the password reminders table (manually, don't use Auth::remind or whatever it is as it'll send an email, but do use the code from the class to generate the token)
Send welcome email to user with link to /user/confirm (or whatever, the point is that it doesn't have to be /user/forgotten-password) and hook that route up in the normal way for forgotten password with an added check for $user->password == '' if you wanna make sure only unconfirmed people can go to that page (not that it really matters).
You may also wish to extend the timeout on the forgotten passwords or, as I did (proper hacky I know), when the user's in the /user/confirm version of the forgotten password functionality, just refresh the timeout in the table before passing through to Laravel's auth system for checking.
Our code is something like this:
On register:
// however you register the user:
$user = new User;
$user->email = Input::get('email');
$user->password = '';
$user->save();
// create a reminder entry for the user
$reminderRepo = App::make('auth.reminder.repository');
$reminderRepo->create($user);
Mail::send(
'emails.registered',
[
'token' => $reminder->token,
],
function ($message) use ($user) {
$message->to($user->email)->setSubject(Lang::get('account.email.registered.subject', ['name' => $user->name]));
}
);
Now the confirm link:
class AccountController extends Controller
{
public function confirm($token)
{
$reminder = DB::table('password_reminders')->whereToken($token)->first();
if (! $reminder) {
App::abort(404);
}
// reset reminder date to now to keep it fresh
DB::table('password_reminders')->whereToken($token)->update(['created_at' => Carbon\Carbon::now()]);
// send token to view but also email so they don't have to type it in (with password reminders it's is a good thing to make users type it, but with confirm account it feels weird)
return View::make('account.confirm-account')->withToken($token)->withEmail($reminder->email);
}
public function postConfirm($token)
{
$credentials = Input::only('email', 'password', 'password_confirmation', 'token');
$response = Password::reset($credentials, function ($user, $password) {
$user->password = $password;
$user->save();
});
switch ($response) {
case Password::INVALID_PASSWORD:
case Password::INVALID_TOKEN:
case Password::INVALID_USER:
return Redirect::back()->withInput()->with('message-error', Lang::get($response));
case Password::PASSWORD_RESET:
Auth::login(User::whereEmail(Input::get('email'))->first());
return Redirect::route('account.home')->with('message-info', Lang::get('messages.confirm_account.succeeded'));
}
}

Resources