Laravel 5 authentication without remember_token - php

I am using an existing database, and I'm not allowed to modify the tables, so adding a remember_token is not an option, but without it I'm unable to login. When I try to login Laravel does check the credentials and returns whether they match the records, but it only refreshes the page. I am pretty sure the remember_token is the cause since I've encountered this problem before, but this time I can't add a column to my users table.
Is there a way to use the out-of-the-box authentication without the remember_token?

Since Laravel v5.3.27 you can also disable the remember me functionality by setting the $rememberTokenName to false in your User model.
class User extends Authenticatable
{
use Notifiable;
protected $rememberTokenName = false;
// ...
}
source: this commit

In your User model add:
/**
* Overrides the method to ignore the remember token.
*/
public function setAttribute($key, $value)
{
$isRememberTokenAttribute = $key == $this->getRememberTokenName();
if (!$isRememberTokenAttribute)
{
parent::setAttribute($key, $value);
}
}
Credits: https://laravel.io/forum/05-21-2014-how-to-disable-remember-token

In order to really disable the "remember me" functionality and to be sure the remember_token field is not used, add the following code to the boot method of App\Providers\AuthServiceProvider.
Auth::provider('eloquent', function($app, array $config)
{
return new class($app['hash'], $config['model']) extends \Illuminate\Auth\EloquentUserProvider
{
public function retrieveByToken($identifier, $token)
{
return null;
}
public function updateRememberToken(\Illuminate\Contracts\Auth\Authenticatable $user, $token)
{
//Do nothing
}
};
});
assuming a default Laravel setup, the code above will work instantly.
Off course, it can be improved by defining the custom user provider in a seperate file instead of using a anonymous class.

in Laravel 9 you could set remember token to empty string, for example:
public function logout()
{
$user = Auth::user();
$user->setRememberToken('');
Auth::logout();
Session::flush();
return redirect('login');
}

Related

Using custom authentication on Laravel 6

I would like to manually authenticate the users in my company. The issue is that, I have 2 tables, called Student and Staff in the Oracle database.
As for the Student table, I get the idea of overriding the built in Auth method provided through the auth scaffolding command as the username and password are stored right into the table.
As for the Staff table, the password is stored a different column/table and encrypted using a stored procedure/package so the only way to get the user validation is by calling the package which only returns 0 or 1 only.
What I have done,
I wrote my own Routes, and added my own functions in LoginController.
public function loginStaff(Request $req){
$username = Str::upper($req->input('username'));
$password = $req->input('password');
$users = PortalUser::where('ID', $username)->firstOrFail();
if ($users->user_type == 'STAFF'){
$queryResult = DB::select('select PACKAGE.validStaff(?,?) from dual',[$username, $password]);
if($queryResult == 1){
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
}
}
I have successfully returned value of 1 and 0 in the controller.
So is there anything that I am missing?
Or should I manually set the session by myself using the session() method?
Thank you.
If you want to manually authenticate users, you can easily use sessions. Have the following code as reference:
//this is where I would like to auth the user.
//using Auth::Attempt and Auth::Login will only run the default query
// typically you store it using the user ID, but you can modify the session using other values.
session()->put('user_id', user id from database here);
And if you want to check whether user is authenticated, modify RedirectIfAuthenticated middleware to this:
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (session()->has('user_id')) {
return redirect( custom path here );
}
return $next($request);
}
}
When you want to logout the user, simply destroy the session key
session()->forget('user_id');
**Note: ** many broadcasting and addons use Laravel's authentication system (Guards) and you may need to hook into their code if you want to use them with your custom auth system
Laravel provides Custom Session Drivers which you can use to create or delete your sessions
<?php
namespace App\Extensions;
class MongoSessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName) {}
public function close() {}
public function read($sessionId) {}
public function write($sessionId, $data) {}
public function destroy($sessionId) {}
public function gc($lifetime) {}
}
Hope it helps, if not then comment down below. Will help you out.
###### Update #######
I think then you do have to make custom HTTP sessions from Laravel
Step 1: Create another table in your database for session, like this;
Schema::create('sessions', function ($table) {
$table->string('id')->unique();
$table->unsignedInteger('user_id')->nullable();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->text('payload');
$table->integer('last_activity');
});
Step 2: Store data in the session, you will typically use the put method or the session helper;
// Via a request instance...
$request->session()->put('key', 'value');
// Via the global helper...
session(['key' => 'value']);
Step 3: Get the key for specific user when your function returns 1
$value = $request->session()->get('key', function () {
return 'default';
});
Step 4: Delete the session, after some time you need to delete the session for security reasons then you can do.
$value = $request->session()->pull('key', 'default');

Public Accessible with Laravel Policy

I want to make some entries of Analysis publicly available. I tried to implement it with Policies but failed. I think it's because the AuthServiceProvider fails with AccessDeniedHttpException every time I try to access without an authorized user.
AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
Analysis::class => AnalysisPolicy::class
];
public function boot()
{
$this->registerPolicies();
}
}
AnalysisPolicy
public function view(User $user, Analysis $analysis)
{
if($analysis->demo === true){
return true;
}
return $user->id === $analysis->user_id;
}
AnalysisController
public function show(int $analysis)
{
$ana = Analysis::find($analysis);
$this->authorize('view', $ana);
...
}
I tried to just create a new Service Provider, but that didn't work either as I cannot call the registerPolicies function without extending from AuthServiceProvider.
Basically, all I want is to now check for anything if the demo Attribute is true.
Edit:
My Quick-Fix form now is just checking in the controller if it's a demo. But that's not a great solution in my opinion as I think the goal with Policies should be that I don't have Access Management in the Controller. So I'd love to find a better solution.
if(!$ana->demo){
$this->authorize('view', $ana);
}
Ok so it turns out you should always read the release notes of the latest Laravel version before asking a question.
As of Laravel 5.7 there is a proper solution for this:
public function update(?User $user, Post $post)
{
return $user->id === $post->user_id;
}
(https://laravel.com/docs/5.7/authorization#writing-policies)
By declaring $user optional, it's null for guest users and can be handled in the policy.

Laravel resource policy always false

I'm trying to allow user to view their own profile in Laravel 5.4.
UserPolicy.php
public function view(User $authUser, $user)
{
return true;
}
registered policy in AuthServiceProvider.php
protected $policies = [
App\Task::class => App\Policies\TaskPolicy::class,
App\User::class => App\Policies\UserPolicy::class
];
Routes
Route::group(['middleware' => 'auth'], function() {
Route::resource('user', 'UserController');
} );
Blade template
#can ( 'view', $user )
// yes
#else
// no
#endcan
UserController.php
public function profile()
{
return $this->show(Auth::user()->id);
}
public function show($id)
{
$user = User::find($id);
return view('user.show', array( 'user'=>$user,'data'=>$this->data ) );
}
The return is always 'false'. Same for calling policy form the controller. Where do I go wrong?
Answering my own question feels weird, but I hate it when I come across questions without followups.
So after double checking It turned out that if I remove authorizeResource from the constructor:
public function __construct()
{
$this->authorizeResource(User::class);
}
and check for authorization in the controller function:
$this->authorize('view',$user);
everything works.
I must've missed this part when I added $user as a parameter in the policy function. So the user to be viewed is never passed in the authorizeResource method.
Thanks everyone for taking your time to help me.
When you add
public function __construct()
{
$this->authorizeResource(User::class);
}
to your Controller, you have to edit all your function signatures to match it to the class e.g. your show signature has to change from public function show($id)
to public function show(User $user)
After that it should work
Just a different approach here to users viewing their own profile.
First, I will create a route for that
Route::group(['middleware' => 'auth'], function() {
Route::get('profile', 'UserController#profile');
});
Then in the profile function I do
public function profile()
{
$user = Auth::user();
return view('profile', compact('user'));
}
This way, user automatically only views their own profile.
Now, if you want to allow some users to view others' profiles, then you can use Policy. Why? Because I think user should ALWAYS be able to view their own profile. But not all users should view other users profiles.
Solution:
Change the second parameter from #can( 'view', $user ) to #can( 'view', $subject ) and it will work find.
Why:
Because you're doing it the wrong way.
public function view(User $user, $subject){
return true;
}
Just look carefully the policy view method, first parameter is authenticated user or current user and second parameter is $subject, Since policies organize authorization logic around models.
Policies are classes that organize authorization logic around a
particular model or resource. For example, if your application is a
blog, you may have a Post model and a corresponding PostPolicy to
authorize user actions such as creating or updating posts.
if you want to go further deep inside it.
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Auth/Access/Gate.php#L353
/**
* Resolve the callback for a policy check.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $ability
* #param array $arguments
* #return callable
*/
protected function resolvePolicyCallback($user, $ability, array $arguments)
{
return function () use ($user, $ability, $arguments) {
$instance = $this->getPolicyFor($arguments[0]);
// If we receive a non-null result from the before method, we will return it
// as the final result. This will allow developers to override the checks
// in the policy to return a result for all rules defined in the class.
if (method_exists($instance, 'before')) {
if (! is_null($result = $instance->before($user, $ability, ...$arguments))) {
return $result;
}
}
if (strpos($ability, '-') !== false) {
$ability = Str::camel($ability);
}
// If the first argument is a string, that means they are passing a class name
// to the policy. We will remove the first argument from this argument list
// because the policy already knows what type of models it can authorize.
if (isset($arguments[0]) && is_string($arguments[0])) {
array_shift($arguments);
}
if (! is_callable([$instance, $ability])) {
return false;
}
return $instance->{$ability}($user, ...$arguments);
};
}
See the last line where it is calling the method with $user and $argument( in our case Model ) is passed.
Laravel Docs for Authorization/Policies
It's possible to escape one or more policies methods using options parameter at authorizeResource with except:
public function __construct()
{
$this->authorizeResource(User::class, 'user', ['except' => ['view']]);
}
This should be on Laravel's documentation, but it isn't. I discovered this just guessing. I think this way it is a better approach thus, by removing authorizeResource method in the construct, it would be necessary to implement the authorization method for each resource action in order to protect the controller.

How to create Laravel 5.1 Custom Authentication driver?

I am working in Laravel authentication login using socialite. Now I can able to save data of user from socialite. But now I am facing problem how to authenticate user from gmail, github.
After some research I understood that I need to create custom authentication. I googled but all are Laravel 4.1 topics. If any one work on this please provide your answers.
I already read following topics but I didn't got how to do it?
http://laravel.com/docs/5.1/authentication#social-authentication
http://laravel.com/docs/5.1/providers
http://laravel-recipes.com/recipes/115/using-your-own-authentication-driver
http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5
Update
public function handleProviderCallback() {
$user = Socialite::with('github')->user();
$email=$user->email;
$user_id=$user->id;
//$authUser = User::where('user_id',$user_id)->where('email', $email)->first();
$authUser = $this->findOrCreateUser($user);
if(Auth::login($authUser, true)) {
return Redirect::to('user/UserDashboard');
}
}
private function findOrCreateUser($user) {
if ($authUser = User::where('user_id',$user->id)->first()) {
return $authUser;
}
return User::create([
'user_id' => $user->id,
'name' => $user->nickname,
'email' => $user->email,
'avatar' => $user->avatar
]);
}
This answer is most suited for Laravel 5.1. Please take care if you
are in some other version. Also keep in mind that IMHO this is a rather advanced level in Laravel, and hence if you don't fully understand what you are doing, you may end up crashing your application. The solution is not end to end correct. This is just a general guideline of what you need to do in order for this to work.
Adding Custom Authentication Drivers In Laravel 5.1
Hint: Laravel documentation for this topic is here.
Hint2: The last link you mentioned is quite useful in my opinion. I learned all of this after reading that link.
http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5
Before we start, I would first like to describe the login flow which will help you understand the process. Laravel uses a driver to connect to the database to fetch your records. Two drivers come pre-bundled with laravel - eloquent & database. We want to create a third so that we can customize it to our needs.
Illuminate\Auth\Guard inside your vendor folder is the main file which has code for the user to log in and log out. And this file mainly uses two Contracts (or interfaces) that we need to override in order for our driver to work. From Laravel's own documentation read this:
The Illuminate\Contracts\Auth\UserProvider implementations are only
responsible for fetching a Illuminate\Contracts\Auth\Authenticatable
implementation out of a persistent storage system, such as MySQL,
Riak, etc. These two interfaces allow the Laravel authentication
mechanisms to continue functioning regardless of how the user data is
stored or what type of class is used to represent it.
So the idea is that for our driver to work we need to implement Illuminate\Contracts\Auth\UserProvider and Illuminate\Contracts\Auth\Authenticatable and tell Laravel to use these implementations instead of the defaults.
So let's begin.
Step 1:
Choose a name for your driver. I name mine socialite. Then in your config/auth.php, change the driver name to socialite. By doing this we just told laravel to use this driver for authentication instead of eloquent which is default.
Step 2:
In your app/Provider/AuthServiceProvider in the boot() method add the following lines:
Auth::extend('socialite', function($app) {
$provider = new SocialiteUserProvider();
return new AuthService($provider, App::make('session.store'));
});
What we did here is:
We first used Auth facade to define the socialite driver.
SocialiteUserProvider is an implementation of UserProvider.
AuthService is my extension of Guard class. The second parameter this class's constructor takes is the session which laravel uses to get and set sessions.
So we basically told Laravel to use our own implementation of Guard class instead of the default one.
Step 3:
Create SocialiteUserProvider. If you read the Laravel's documentation, you will understand what each of these methods should return. I have created the first method as a sample. As you can see, I use my UserService class to fetch results. You can fetch your own results however you want to fetch them. Then I created an User object out of it. This User class implements the Illuminate\Contracts\Auth\Authenticatable contract.
<?php
namespace App\Extensions;
use App\User;
use App\Services\UserService;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class SocialiteUserProvider implements UserProvider
{
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function retrieveById($identifier)
{
$result = $this->userService->getUserByEmail($identifier);
if(count($result) === 0)
{
$user = null;
}
else
{
$user = new User($result[0]);
}
return $user;
}
public function retrieveByToken($identifier, $token)
{
// Implement your own.
}
public function updateRememberToken(Authenticatable $user, $token)
{
// Implement your own.
}
public function retrieveByCredentials(array $credentials)
{
// Implement your own.
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Implement your own.
}
}
Step 4:
Create User class which implements the Authenticatable. This class has to implement this interface because the Guard class will use this class to get values.
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
class User implements Authenticatable
{
protected $primaryKey = 'userEmail';
protected $attributes = [];
public function __construct(array $attributes)
{
$this->attributes = $attributes;
}
public function getUserAttributes()
{
return $this->attributes;
}
public function getAuthIdentifier()
{
return $this->attributes[$this->primaryKey];
}
public function getAuthPassword()
{
// Implement your own.
}
public function getRememberToken()
{
// Implement your own.
}
public function setRememberToken($value)
{
// Implement your own.
}
public function getRememberTokenName()
{
// Implement your own.
}
}
Step 5:
Finally create the AuthService class that will call the Guard methods. This is my own implementation. You can write your own as per your needs. What we have done here is extended the Guard class to implement two new functions which are self explanatory.
<?php
namespace App\Services;
use Illuminate\Auth\Guard;
class AuthService extends Guard
{
public function signin($email)
{
$credentials = array('email' => $email);
$this->fireAttemptEvent($credentials, false, true);
$this->lastAttempted = $user = $this->provider->retrieveById($email);
if($user !== null)
{
$this->login($user, false);
return true;
}
else
{
return false;
}
}
public function signout()
{
$this->clearUserDataFromStorage();
if(isset($this->events))
{
$this->events->fire('auth.logout', [$this->user()]);
}
$this->user = null;
$this->loggedOut = true;
}
}
Step 6: Bonus Step
Just to complete my answer, I will also explain the structure that UserService class expects. First lets understand what this class does. In our above steps we created everything to let laravel know how to use our authentication driver, instead of theirs. But we still haven't told laravel that how should it get the data. All we told laravel that if you call the userService->getUserByEmail($email) method, you will get your data. So now we simply have to implement this function.
E.g.1 You are using Eloquent.
public function getUserByEmail($email)
{
return UserModel::where('email', $email)->get();
}
E.g.2 You are using Fluent.
public function getUserByEmail($email)
{
return DB::table('myusertable')->where('email', '=', $email)->get();
}
Update: 19 Jun 2016
Thank you #skittles for pointing out that I have not clearly shown where the files should be placed. All the files are to be placed as per the namespace given. E.g. if the namespace is App\Extensions and the class name is SocialiteUserProvider then location of file is App\Extensions\SocialiteUserProvider.php. The App directory in laravel is the app folder.
Good tutorial for setting up laravel socialite here: https://mattstauffer.co/blog/using-github-authentication-for-login-with-laravel-socialite
Auth::login doesn't return a boolean value you can use attempt to do a Auth::attempt
if(Auth::login($authUser, true)) {
return Redirect::to('user/UserDashboard');
}
Follow the tutorial and do this, and just have middleware configured on the home route
$authUser = $this->findOrCreateUser($user);
Auth::login($authUser, true);
return Redirect::to('home');

Laravel, Auth and logging out from a model

I'm currently using Laravel 5 Authentification, but I have edited it to allow me to connect to an API server instead of an Eloquent model.
Here is the code of my custom UserProvider:
<?php namespace App\Auth;
use Illuminate\Contracts\Auth\UserProvider as UserProviderInterface;
use WDAL;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Auth\GenericUser;
use Session;
class WolfUserProvider implements UserProviderInterface {
private $_loggedUser;
public function __construct()
{
$this->_loggedUser = null;
$user = Session::get('user');
if (!empty($user)) {
$this->_loggedUser = unserialize($user);
}
}
public function retrieveById($id)
{
return $this->_loggedUser;
}
public function retrieveByToken($identifier, $token)
{
return null;
}
public function updateRememberToken(Authenticatable $user, $token)
{
//dd('updateRememberToken');
}
public function retrieveByCredentials(array $credentials)
{
$user = WDAL::getContactCredentials($credentials['login']);
return $user;
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
if($user->username == $credentials['login'] && $user->password == $credentials['password']){
$this->_loggedUser = $user;
Session::set('user', serialize($user));
return true;
}
else{
return false;
}
}
}
?>
This code might not be perfect as it still in early development ;-) (feel free to suggest me some ideas of improvement if you want to)
So when the user is logged, it has access to the whole platform and to several views and can communicate with the API server to display and edit data.
Sometimes, the API server can return "Invalid Session ID" and when my Model gets this message, the user should be redirected to the login page.
From a Controller it's really easy to handle I can use this code (logout link):
public function getLogout()
{
$this->auth->logout();
Session::flush();
return redirect('/');
}
But do you know how I should proceed from a Model ? I could of course edit all my controllers to check for the value returned by the Model to logout, but cannot it be done thanks to middlewares?
It seems to be really long to edit all my controllers, and this will imply a lot of duplicated code.
One of my tries was to throw an exception from the Controller, and catch in from the auth middleware.
It was not working, because I didn't write use Exception;
I'm now catching the exception, and can now redirect the user from the middleware.
Thank you anyway!

Categories