Related
I'm using Spatie's "Laravel Permission" Package for the ACL. Everything is working perfectly. I now want to allow only the User roles 2 and 3 to Login.
Previously, before using "Laravel Permission" package I had only this "role" column on my table and I could easily Log In a user with this line of code on Login Controller's credentials method.
protected function credentials(Request $request)
{
$credentials = $request->only($this->username(), 'password');
//$credentials['role'] = '1';
return $credentials;
}
$credentials = $request->only($this->username(), 'password');
$credentials['role'] = '1';
How do I allow Login only for the 2 and 3 User Roles?
You can override authenticated() in LoginController and check user role. Pretty simple.
protected function authenticated(Request $request, $user)
{
//Check user role, if it is not admin then logout
if(!$user->hasRole(['Admin', 'Super-Admin']))
{
$this->guard()->logout();
$request->session()->invalidate();
return redirect('/login')->withErrors('You are unauthorized to login');
}
}
You could go with the workaround as follow:
If you're using the default LoginController from the App\Http\Controllers\Auth folder, then override its attemptLogin() method that comes from the Trait used.
protected function attemptLogin(Request $request)
{
if( $this->guard()->attempt(
$this->credentials($request), $request->filled('remember')
) ) { // Credential auth was successful
// Get user model
$user = Auth::user();
return $user->hasRole([2, 3]); // Check if user has role ids 2 or 3
}
return false;
}
hasRoles() method comes from the HasRoles trait used for the User model.
Or you could override the Laravel credentials during login. In your default LoginController from the App\Http\Controllers\Auth folder, then override its credentials(Request $request) method that comes from the Trait used.
Override it to look something similar to this
protected function credentials(Request $request)
{
return [ 'email' => $request-> { this-> username() }, 'password' -> $request -> password, 'role_id' => [ '1', '2' ] ];
This is presuming you have a role_id in your user model. Worked for me.return
I am using Laravel 5.3 and customizing the Password Reset Email Template. I have done the following changes to create my own html email for the notification using a custom Mailable class. This is my progress so far:
ForgotPasswordController:
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$response = Password::sendResetLink($request->only('email'), function (Message $message) {
$message->subject($this->getEmailSubject());
});
switch ($response) {
case Password::RESET_LINK_SENT:
return Response::json(['status' => trans($response)], 200);
case Password::INVALID_USER:
return Response::json(['email' => trans($response)], 400);
}
}
User Model:
public function sendPasswordResetNotification($token)
{
Mail::queue(new ResetPassword($token));
}
ResetPassword Mailable Class:
protected $token;
public function __construct($token)
{
$this->token = $token;
}
public function build()
{
$userEmail = 'something'; // How to add User Email??
$userName = 'Donald Trump'; // How to find out User's Name??
$subject = 'Password Reset';
return $this->view('emails.password')
->to($userEmail)
->subject($subject)
->with([
'token' => $this->token
'userEmail' => $userEmail,
'userName' => $userName
]);
}
If you noticed above, I am not sure how do I pass the user's name and find out the user's email address. Do I need to send this data from the User Model or do I query it from the Mailable class? Can someone show me how I can do that please?
Usually you ask for the user email in order to send a reset password email, that email should come as a request parameter to your route controller.
By default, L5.3 uses post('password/email) route to handle a reset password request. This route execute sendResetLinkEmail method which is defined in the 'SendsPasswordResetEmails' trait used by the App\Http\Controllers\Auth\ForgotPasswordController.
From here you can take one of 2 options:
1st: You could overwrite the route to call another function in the same controller (or any other controller, in this case could be your postEmail function) which search for the user model by the email you received, then you can pass the user model as function parameter to the method which execute the queue mail action (this may or may not require to overwrite the SendsPasswordResetEmails, depends on how you handle your reset password method).
This solution would looks something like this:
In routes/web.php
post('password/email', 'Auth\ForgotPasswordController#postEmail')
in app/Mail/passwordNotification.php (for instance)
protected $token;
protected $userModel;
public function __construct($token, User $userModel)
{
$this->token = $token;
$this->userModel = $userModel;
}
public function build()
{
$userEmail = $this->userModel->email;
$userName = $this->userModel->email
$subject = 'Password Reset';
return $this->view('emails.password')
->to($userEmail)
->subject($subject)
->with([
'token' => $this->token
'userEmail' => $userEmail,
'userName' => $userName
]);
}
in app/Http/Controllers/Auth/ForgotPasswordController
public function postEmail(Request $request)
{
$this->validate($request, ['email' => 'required|email']);
$userModel = User::where('email', $request->only('email'))->first();
Mail::queue(new ResetPassword($token));
//Manage here your response
}
2nd: You could just overwirte the trait SendsPasswordResetEmails to search for the user model by the email and use your customized function in sendResetLinkEmail function. There you could use your function but notice that you still have to handle somehow an status to create a response as you already have it on ForgotPasswordController.
I hope it helps!
I am a bit confused on the laravel scopes part.
I have a user model and table.
How can I assign a user the role of user, customer and/or admin.
I have a SPA with vue and laravel api backend. I use https://laravel.com/docs/5.3/passport#consuming-your-api-with-javascript
Passport::tokensCan([
'user' => 'User',
'customer' => 'Customer',
'admin' => 'Admin',
]);
How can i assign which user model has which scope(s)?
Or are scopes not the same as roles?
How would you implement this?
Thanks in advance!
Or are scopes not the same as roles?
The biggest difference between the two is the context they apply to. Role-based Access Control (RBAC) governs the access control of a user when using the web application directly, while Oauth-2 scope governs the access to the API resources for an external client on behalf of a user.
How can i assign which user model has which scope(s)?
In general Oauth flow, a user (as a resource owner) is requested to authorize a client on things that it can and cannot do on his/her behalf, these are what you called scope. On successful authorization the scope being requested by the client will be assigned to the generated token not to the user per se.
Depending on which Oauth grant flow that you choose, the client should include the scope on its request. In Authorization code grant flow the scope should be included on HTTP GET query parameter when redirecting the user to authorization page, while on Password grant flow the scope must be included in HTTP POST body parameter to request a token.
How would you implement this?
This is an example with Password grant flow, with assumption that you completed the laravel/passport setup beforehand
Define scopes for both admin and user role. Be specific as you can, for example: admin can manage-order and user only read it.
// in AuthServiceProvider boot
Passport::tokensCan([
'manage-order' => 'Manage order scope'
'read-only-order' => 'Read only order scope'
]);
Prepare the REST controller
// in controller
namespace App\Http\Controllers;
class OrderController extends Controller
{
public function index(Request $request)
{
// allow listing all order only for token with manage order scope
}
public function store(Request $request)
{
// allow storing a newly created order in storage for token with manage order scope
}
public function show($id)
{
// allow displaying the order for token with both manage and read only scope
}
}
Assign the route with api guard and scope
// in api.php
Route::get('/api/orders', 'OrderController#index')
->middleware(['auth:api', 'scopes:manage-order']);
Route::post('/api/orders', 'OrderController#store')
->middleware(['auth:api', 'scopes:manage-order']);
Route::get('/api/orders/{id}', 'OrderController#show')
->middleware(['auth:api', 'scopes:manage-order, read-only-order']);
And when issuing a token check the user role first and grant the scope based on that role. To achieve this, we need an extra controller that use AuthenticatesUsers trait to provide login endpoint.
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
class ApiLoginController extends Controller
{
use AuthenticatesUsers;
protected function authenticated(Request $request, $user)
{
// implement your user role retrieval logic, for example retrieve from `roles` database table
$role = $user->checkRole();
// grant scopes based on the role that we get previously
if ($role == 'admin') {
$request->request->add([
'scope' => 'manage-order' // grant manage order scope for user with admin role
]);
} else {
$request->request->add([
'scope' => 'read-only-order' // read-only order scope for other user role
]);
}
// forward the request to the oauth token request endpoint
$tokenRequest = Request::create(
'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest);
}
}
Add route for api login endpoint
//in api.php
Route::group('namespace' => 'Auth', function () {
Route::post('login', 'ApiLoginController#login');
});
Instead of doing POST to /oauth/token route, POST to the api login endpoint that we provided before
// from client application
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/api/login', [
'form_params' => [
'grant_type' => 'password',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'username' => 'user#email.com',
'password' => 'my-password',
],
]);
return json_decode((string) $response->getBody(), true);
Upon successful authorization, an access_token and a refresh_token based on scope that we define before will be issued for the client application. Keep that somewhere and include the token to the HTTP header whenever making a request to the API.
// from client application
$response = $client->request('GET', '/api/my/index', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
The API now should return
{"error":"unauthenticated"}
whenever a token with under privilege is used to consumed restricted endpoint.
Implement the Raymond Lagonda response and it works very well, just to be careful with the following.
You need to override some methods from AuthenticatesUsers traits in ApiLoginController:
/**
* Send the response after the user was authenticated.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
protected function sendLoginResponse(Request $request)
{
// $request->session()->regenerate(); // coment this becose api routes with passport failed here.
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: response()->json(["status"=>"error", "message"=>"Some error for failes authenticated method"]);
}
/**
* Get the failed login response instance.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse
*/
protected function sendFailedLoginResponse(Request $request)
{
return response()->json([
"status"=>"error",
"message"=>"Autentication Error",
"data"=>[
"errors"=>[
$this->username() => Lang::get('auth.failed'),
]
]
]);
}
If you changed the login: username field to a custom username field eg: e_mail. You must refine the username method as in your LoginController.
Also you have to redefine and edit the methods: validateLogin, attemptLogin, credentials since once the login is validated, the request is forwarded to passport and must be called username.
I know this is a little late, but if you're consuming a backend API in an SPA using the CreateFreshApiToken in web middleware, then you can simply add an 'admin' middleware to your app:
php artisan make:middleware Admin
Then in \App\Http\Middleware\Admin do the following:
public function handle($request, Closure $next)
{
if (Auth::user()->role() !== 'admin') {
return response(json_encode(['error' => 'Unauthorised']), 401)
->header('Content-Type', 'text/json');
}
return $next($request);
}
Make sure you have added the role method to \App\User to retrieve the users role.
Now all you need to do is register your middleware in app\Http\Kernel.php $routeMiddleware, like so:
protected $routeMiddleware = [
// Other Middleware
'admin' => \App\Http\Middleware\Admin::class,
];
And add that to your route in routes/api.php
Route::middleware(['auth:api','admin'])->get('/customers','Api\CustomersController#index');
Now if you try to access the api without permission you will receive a "401 Unauthorized" error, which you can check for and handle in your app.
I've managed to get this into working, with #RaymondLagonda solution, for Laravel 5.5 with Sentinel, but it should, also work, without Sentinel.
The solution needs some class methods overriding (so please keep that in mind, for future updates), and adds some protection to your api routes (not exposing client_secret for example).
First step, is to modify your ApiLoginController in order to add construct function:
public function __construct(Request $request){
$oauth_client_id = env('PASSPORT_CLIENT_ID');
$oauth_client = OauthClients::findOrFail($oauth_client_id);
$request->request->add([
'email' => $request->username,
'client_id' => $oauth_client_id,
'client_secret' => $oauth_client->secret]);
}
In this example, you need to define var ('PASSPORT_CLIENT_ID') in your .env and create OauthClients Model, but you can safely skip this by putting your proper test values here.
One thing to notice, is that we are setting $request->email value to username, just to stick to Oauth2 convention.
Second step is, to override, sendLoginResponse method which is causing errors like Session storage not set, we don't need sessions here, cause it is api.
protected function sendLoginResponse(Request $request)
{
// $request->session()->regenerate();
$this->clearLoginAttempts($request);
return $this->authenticated($request, $this->guard()->user())
?: redirect()->intended($this->redirectPath());
}
Third step is, to modify your authenticated methods as suggested by #RaymondLagonda. You need to write your own logic here, and especially configure your scopes.
And final step (in case you are using Sentinel) is to modify AuthServiceProvider. Add
$this->app->rebinding('request', function ($app, $request) {
$request->setUserResolver(function () use ($app) {
return \Auth::user();
// return $app['sentinel']->getUser();
});
});
just after $this->registerPolicies(); in boot method.
After these steps you should be able, to get your api working, by providing username ('this will always be email, in this implementation'), password and grant_type='password'
At this point, you can add to middlewares scopes scopes:... or scope:... to protect your routes.
I hope, it is going to really help...
With #RaymondLagonda solution. If you are getting a class scopes not found error, add the following middleware to the $routeMiddleware property of your app/Http/Kernel.php file:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,`
Also, if you are getting the error Type error: Too few arguments to function, you should be able to get the $user from the request like below.
(I am using laratrust for managing roles)
public function login(Request $request)
{
$email = $request->input('username');
$user = User::where('email','=',$email)->first();
if($user && $user->hasRole('admin')){
$request->request->add([
'scope' => 'manage-everything'
]);
}else{
return response()->json(['message' => 'Unauthorized'],403);
}
$tokenRequest = Request::create(
'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest);
}
Thanks for this, this question was riddling my mind for a while! I took Raymond Lagonda's solution customised it a little for Laravel 5.6, using the built-in rate limiting, using a single thirdparty client (or be more custom if needed), while still giving each user a list of permissions (scopes).
Uses Laravel Passport password grant and follows Oauth flow
Gives you ability to set roles (scopes) for different users
don't expose/release client ID or client secret, only the user's username (email) and password, pretty much a password grant, minus the client/grant stuff
Examples at bottom
routes/api.php
Route::group(['namespace' => 'ThirdParty', 'prefix' => 'thirdparty'], function () {
Route::post('login', 'ApiLoginController#login');
});
ThirdParty/ApiLoginController.php
<?php
namespace App\Http\Controllers\ThirdParty;
use Hash;
use App\User;
use App\ThirdParty;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class ApiLoginController extends Controller
{
use AuthenticatesUsers;
/**
* Thirdparty login method to handle different
* clients logging in for different reasons,
* we assign each third party user scopes
* to assign to their token, so they
* can perform different API tasks
* with the same token.
*
* #param Request $request
* #return Illuminate\Http\Response
*/
protected function login(Request $request)
{
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$user = $this->validateUserLogin($request);
$client = ThirdParty::where(['id' => config('thirdparties.client_id')])->first();
$request->request->add([
'scope' => $user->scopes,
'grant_type' => 'password',
'client_id' => $client->id,
'client_secret' => $client->secret
]);
return Route::dispatch(
Request::create('/oauth/token', 'post')
);
}
/**
* Validate the users login, checking
* their username/password
*
* #param Request $request
* #return User
*/
public function validateUserLogin($request)
{
$this->incrementLoginAttempts($request);
$username = $request->username;
$password = $request->password;
$user = User::where(['email' => $username])->first();
abort_unless($user, 401, 'Incorrect email/password.');
$user->setVisible(['password']);
abort_unless(Hash::check($password, $user->password), 401, 'Incorrect email/password.');
return $user;
}
}
config/thirdparties.php
<?php
return [
'client_id' => env('THIRDPARTY_CLIENT_ID', null),
];
ThirdParty.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ThirdParty extends Model
{
protected $table = 'oauth_clients';
}
.env
## THIRDPARTIES
THIRDPARTY_CLIENT_ID=3
php artisan make:migration add_scope_to_users_table --table=users
// up
Schema::table('users', function (Blueprint $table) {
$table->text('scopes')->nullable()->after('api_access');
});
// down
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('scopes');
});
(note: api_access is a flag which decides whether a user can login to the website/frontend portion of the app, to view dashboards/records etc.),
routes/api.php
Route::group(['middleware' => ['auth.client:YOUR_SCOPE_HERE', 'throttle:60,1']], function () {
...routes...
});
MySQL - Users scopes
INSERT INTO `users` (`id`, `created_at`, `updated_at`, `name`, `email`, `password`, `remember_token`, `api_access`, `scopes`)
VALUES
(5, '2019-03-19 19:27:08', '2019-03-19 19:27:08', '', 'hello#email.tld', 'YOUR_HASHED_PASSWORD', NULL, 1, 'YOUR_SCOPE_HERE ANOTHER_SCOPE_HERE');
MySQL - ThirdParty Oauth Client
INSERT INTO `oauth_clients` (`id`, `user_id`, `name`, `secret`, `redirect`, `personal_access_client`, `password_client`, `revoked`, `created_at`, `updated_at`)
VALUES
(3, NULL, 'Thirdparty Password Grant Client', 'YOUR_SECRET', 'http://localhost', 0, 1, 0, '2019-03-19 19:12:37', '2019-03-19 19:12:37');
cURL - Logging in/requesting a token
curl -X POST \
http://site.localhost/api/v1/thirdparty/login \
-H 'Accept: application/json' \
-H 'Accept-Charset: application/json' \
-F username=hello#email.tld \
-F password=YOUR_UNHASHED_PASSWORD
{
"token_type": "Bearer",
"expires_in": 604800,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciO...",
"refresh_token": "def502008a75cd2cdd0dad086..."
}
Use longlived access_token/refresh_token as normal!
Accessing forbidden scope
{
"data": {
"errors": "Invalid scope(s) provided."
},
"meta": {
"code": 403,
"status": "FORBIDDEN"
}
}
How can you authenticate multiple types of users in Laravel 5.1 e.g. Jobseeker, Recruiter, Admin etc.
Some of you have suggested using a single users table to store only the password and email, creating profile tables to store user specific information (jobseeker_profile, recruiter_profile) and using roles to differentiate between the different types of users (i.e having a roles and role_user) table.
This is all very well but then what if the different types of users have different registration and login forms. How do you customize the default auth controller out of the box to display the correct view?
So if I have the following routes:
// Jobseeker Authentication routes...
Route::get('auth/login', 'Auth\AuthController#getLogin');
Route::post('auth/login', 'Auth\AuthController#postLogin');
Route::get('auth/logout', 'Auth\AuthController#getLogout');
// Jobseeker Registration routes...
Route::get('auth/register', 'Auth\AuthController#getRegister');
Route::post('auth/register', 'Auth\AuthController#postRegister');
// Recruiter Authentication routes...
Route::get('recruiter/auth/login', 'Auth\AuthController#getLogin');
Route::post('recruiter/auth/login', 'Auth\AuthController#postLogin');
Route::get('recruiter/auth/logout', 'Auth\AuthController#getLogout');
// Recruiter Registration routes...
Route::get('recruiter/auth/register', 'Auth\AuthController#getRegister');
Route::post('recruiter/auth/register', 'Auth\AuthController#postRegister');
This is the default auth controller out of the box:
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers;
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
traits used by the default out of the box auth controller:
trait AuthenticatesUsers
{
use RedirectsUsers;
public function getLogin()
{
return view('auth.login');
}
public function postLogin(Request $request)
{
$this->validate($request, [
'email' => 'required|email', 'password' => 'required',
]);
$credentials = $this->getCredentials($request);
if (Auth::attempt($credentials, $request->has('remember'))) {
return redirect()->intended($this->redirectPath());
}
return redirect($this->loginPath())
->withInput($request->only('email', 'remember'))
->withErrors([
'email' => $this->getFailedLoginMessage(),
]);
}
public function loginPath()
{
return property_exists($this, 'loginPath') ? $this->loginPath : '/auth/login';
}
}
trait RegistersUsers
{
use RedirectsUsers;
public function getRegister()
{
return view('auth.register');
}
public function postRegister(Request $request)
{
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
Auth::login($this->create($request->all()));
return redirect($this->redirectPath());
}
}
I'm sure this is a very common requirement for many web applications but I can't find any helpful tutorials for Laravel specific implementations. All the tutorial simply focus on the out of the box implementation for some odd reason.
Any help on the above would be much appreciated.
This is not a solution to your question directly, but alternative way to solve your question problem with.
In stead of creating different username and password for different groups, make a central authentication that has roles. It called user and roles.
You can define groups with different roles, and each roles has specific access to respective area.
Regarding registration process you can make two differnet views but using the same controller, and for each view you can create a hidden field to indicate if it is jobseekers group or recruiter group.
Both will receive two different confirmation emails where they should fill the rest of the profile information, like recruiter should put company name and jobseeker should put his name etc. they might have two different tables for profile information, but still using the same login system.
By adding condition to middleware and correct route, if jobseeker tries to access recruiter area even if jobseeker is logged in the system, the jobseeker won't be able to access that area or the opposite way.
Since Laravel 5.1 has build in user login system, so you have few choices, build your own roles or use 3rd party.
I suggest you to build your own so you have control over your code and can further develop it as you wish with time. It might take you half day to get it run and understand how it works, but it is worth spending that time with the right approach in stead of the way you go in your Question OR using 3rd party is fine too, there is a lot of packages around you can search for. I have personally used Entrust (https://github.com/Zizaco/entrust) it is easy and nice way to provide roles and permissions to your project.
Here is also a link to video developed by Jeffrey Way at Laracast, it builds user and roles system from scratch for Laravel 4. but since you have user part, just follow roles part and with small modifications you will have a roles system to your Laravel 5.1, I have tried it and it works.
Regarding your question in the comments, when you follow the video you will understand the concept.
Link to the video: https://laracasts.com/lessons/users-and-roles
You might need to create account to see the video, most of videos are free.
Good practice
It is always also a good practice to illustrate what you want to achieve that makes things easier, I have just made an example for your project, but that is only example for learning:
I encourage you to read some of the topics regarding roles, here you will also find some inspiration to 3rd party acl systems to Laravel, there might be more articles but here is some:
Reading:
https://laracasts.com/discuss/channels/laravel/which-package-is-best-for-roles-permissions/?page=2
https://laracasts.com/discuss/channels/general-discussion/laravel-5-user-groups-management
https://laracasts.com/discuss/channels/general-discussion/roles-and-permissions-in-laravel-5
EDIT
Important Note
Laravel 5.1 has introduced Authorization, I have not found much documentation online yet but it is worth to spend some time learning it:
http://laravel.com/docs/5.1/authorization#policies
NEW UPDATE
There are some great videos solution for what you asking, follow ACL parts here
https://laracasts.com/series/whats-new-in-laravel-5-1
This might be very interesting too:
https://laracasts.com/lessons/email-verification-in-laravel
This will give you a complete own developed solution.
You can achieve multiple authentication easily by pulling up the sarav/laravel-multiauth package
composer require sarav/laravel-multiauth
I assume you have separate tables for Jobseeker, Recruiter, Admin.
Step 1 : Open app.php and replace
Illuminate\Auth\AuthServiceProvider::class
with
Sarav\Multiauth\MultiauthServiceProvider::class
Then, open up auth.php file and remove
<?php
return [
'driver' => 'eloquent',
'model' => 'App\User::class',
'table' => 'users',
'password' => [
'email' => 'emails.password',
'table' => 'password_resets',
'expire' => 60,
],
];
and add the following code
return [
'multi' => [
'jobseeker' => [
'driver' => 'eloquent',
'model' => App\Jobseeker::class, // Model Class
'table' => 'jobseeker' // jobseeker table
],
'recruiter' => [
'driver' => 'eloquent',
'model' => App\Recruiter::class, // Model Class
'table' => 'recruiter' //recruiter table
],
'admin' => [
'driver' => 'eloquent',
'model' => App\Admin::class, // Model Class
'table' => 'admin' //admin table
],
],
'password' => [
'email' => 'emails.password',
'table' => 'password_resets',
'expire' => 60,
]
];
Thats it!
Now you can try login attempt by calling
\Auth::attempt('jobseeker', ['email'=> 'johndoe#example.com', 'password' => 'secret']);
\Auth::attempt('recruiter', ['email'=> 'johndoe#example.com', 'password' => 'secret']);
\Auth::attempt('admin', ['email'=> 'johndoe#example.com', 'password' => 'secret']);
Always remember first paramter should be your user parameter. Here I have given jobseeker for jobseeker login attempt, recruiter for recruiter attempt and admin for admin login attempt. Without the proper first parameter system will throw exception.
For more detailed information checkout this article
http://sarav.co/blog/multiple-authentication-in-laravel-continued/
Short Answer: Add user types to your users table with specific number.
TL;DR answer.
Long Answer:
If you have migrated your table, just run php artisan migrate:rollback.
Add following line to your migration table for users:
$table->integer("user_type")->default(0);
Here I am considering that user type zero is just a simple JobSeeker.
And in your form, you can add option with value zero and one such that people will be selecting what they want to be like recruiter. There is no need of other
As another solution, i can suggest you to use a polymorphic relation between User and Account, like
class User extends Eloquent {
...
public function account() {
return $this->morphTo();
}
}
class Account extends Eloquent {
...
public function user() {
return $this->morphOne(App\User::class, 'account');
}
}
class JobSeeker extends Account { ... }
class Recruiter extends Account { ... }
For different types of Account, you can use route prefixes and different auth controllers, specially for registration who differs for each account instances :
// Recruiter Authentication routes...
Route::group(['prefix' => 'recruiter'], function() {
Route::controller('auth', 'Auth\RecruiterAuthController');
});
At last, you can access the authenticated account directly from auth()->user()->account. it will return any instance of Account (Recruiter, Admin, ....)
hope it helps you ;)
I will try to explain how authentication is managed in Laravel 5.1
On application start AuthServiceProvider is called, which calls registerAuthenticator() function in which new AuthManager is created.
AuthServiceProvider -> registerAuthenticator() -> new AuthManager()
On manager create createNameDriver function will be called in which new nameProvider will be created, where name is your auth driver selected in auth config file. Then in that function new Guard will be created and nameProivder will be passed to its contractor. All auth functions in that Guard will use functions from that provider to manage auth. Provider implements UserProvider which has
retrieveById($identifier),
retrieveByToken($identifier, $token),
updateRememberToken(Authenticatable $user, $token),
retrieveByCredentials(array $credentials),
validateCredentials(Authenticatable $user, array $credentials)
functions.
Main idea of managing multi auth in Laravel 5.1 is to create new AutServiceProvider and on its boot pass app auth new AuthModelProvider which functions then will be used in same Guard. In AuthModelProvider you can manage all retrieve functions the way you need.
Here is all changed I've made to manage multi auth. My project name is APC, that's why I use it everywhere.
Add this function to your models
public function getAuthIdentifier()
{
return [self::MODULE_NAME => $this->getKey()];
}
Create AuthServiceProvider in Provider/YourProjectName directory. In boot function we extend auth from our new provider AuthModelProvider.
<?php
namespace App\Providers\Apc;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Hashing\BcryptHasher;
class AuthServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
self::getAuthModels();
$this->app['auth']->extend('apc', function() {
return new AuthModelProvider(self::getAuthModels(), new BcryptHasher());
});
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
public static function getAuthModels()
{
$configModels = config('auth.models');
$authModels = [];
foreach ($configModels as $key => $class) {
$authModel = new $class();
$authModels [$key]= $authModel;
}
return $authModels;
}
}
Create AuthModelProvider in same directory. Diff in my models is existence of login field in company table. But you can be more specific if you want. In retrieveByCridentials function I just look for existence of login and choose my model accordingly.
<?php
namespace App\Providers\Apc;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Hashing\Hasher as HasherContract;
use Illuminate\Support\Str;
class AuthModelProvider implements UserProvider
{
protected $users;
protected $hasher;
public function __construct($usersModels, HasherContract $hasher)
{
$this->users = $usersModels;
$this->hasher = $hasher;
}
/**
* Retrieve a user by their unique identifier.
*
* #param mixed $identifier
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifiers)
{
foreach ($identifiers as $key => $id) {
if (isset($this->users[$key])) {
return $this->users[$key]->where('id', $id)->active()->base()->first();
}
}
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* #param mixed $identifier
* #param string $token
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifiers, $token)
{
return null;
$user = $this->getUserByIdentifier($identifiers);
if ($user) {
return $user->where($user->getRememberTokenName(), $token)->active()->first();
}
}
/**
* Update the "remember me" token for the given user in storage.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param string $token
* #return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
$user->setRememberToken($token);
$user->save();
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {
return null;
}
if (isset($credentials['login'])) {
$userModel = $this->users['company'];
} else {
$userModel = $this->users['user'];
}
$query = $userModel->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
private function getUserByIdentifier($identifiers)
{
if (!$identifiers) {
}
foreach ($identifiers as $namespace => $id) {
if (isset($this->users[$namespace])) {
return $this->users[$namespace];
}
}
return null;
}
}
Add AuthServiceProvider to app conf file.
\App\Providers\Apc\AuthServiceProvider::class,
Make this changes to auth conf file.
'driver' => 'apc',
'models' => [
\App\Apc\User\User::MODULE_NAME => \App\Apc\User\User::class,
\App\Apc\Company\Company::MODULE_NAME => \App\Apc\Company\Company::class
],
That's all. Hope it was helpful.
i was using laravel bcrypt authentication in a back end application but client asked plain password authentication so that he can see the password of each user as administrator. My whole app logic is on laravel inbuilt authentication method an bcrypt hashing. how can i replace it to authenticate with plain password mach stored in database instead of storing hash ?
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
public function __construct()
{
$this->middleware('guest', ['except' => ['getLogout', 'getLogin']]);
}
public function postLogin()
{
$data = \Request::all();
$rules = [
'email' => 'required|email|max:255|exists:users',
'password' => 'required|exists:users'
];
$validator = \Validator::make($data, $rules);
if ($validator->fails()) {
//login data not exist in db
return redirect('/login')->withErrors($validator)->withInput();
} else {
$email = Request::input('email');
$pass = Request::input('password');
//in my table users, status must be 1 to login into app
$matchWhere = ['login' => $email, 'password' => $pass, 'status' => 1];
$count = \App\User::where($matchWhere)->count();
if ($count == 1) {
$user = \App\User::where($matchWhere)->first();
Auth::loginUsingId($user->id);
return redirect()->intended('/');
} else {
//not status active or password or email is wrong
$validator->errors()->add('Unauthorized', 'Not accepted in community yet');
return redirect('/login')->withErrors($validator)->withInput();
}
}
}
public function getLogin()
{
if (Auth::check()) {
return redirect()->intended('/');
} else {
return view('auth.login');
}
}
public function getLogout()
{
Auth::logout();
return redirect()->intended('/login');
}
}
If you are now using Laravel 5^, you can do that by searching for the class Illuminate/Auth/EloquentUserProvider and do some minor tweaks in there.
For e.g. find the public function retrieveByCredentials() and validateCredentials(). In the second function, you can see that the laravel is checking the hashed passwords to be fed into Auth::attempt() method. Just change it to plain checking and you are done.
public function retrieveByCredentials(array $credentials)
{
if (empty($credentials)) {
return;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a user, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
return $query->first();
}
/**
* Validate a user against the given credentials.
*
* #param \Illuminate\Contracts\Auth\Authenticatable $user
* #param array $credentials
* #return bool
*/
public function validateCredentials(UserContract $user, array $credentials)
{
$plain = $credentials['password'];
return $this->hasher->check($plain, $user->getAuthPassword());
}
Change $this->hasher->check to normal check and you will be done. :)
In laravel 4 you could have rewritten the HASH module . This stackoverflow thread explains how to use SHA1 instead of bycrypt [ check the accepted answer and comments ] .
You can make use of the method explained here and save your password without hashing .
Well, that really compromises your client's website security.
Storing plain passwords in the DB is not recommended at all. If someone gained access to the database his/her site will be really vulnerable, anyone with a copy of the database would have easy access to all kind of accounts. I insist you should create a reset/change password functionality instead of storing plain passwords in the DB.
Anyway, you could just get the plain password with
$password = Input::get('password');
And I guess you could authenticate users with
if (Auth::attempt(array('password' => $password)))
{
return Redirect::route('home');
}
Wow, these are all so complicated, it's as simple as.
if ($user = User::where('email', request()->email)->where('password', request()->password)->first()) {
Auth::login($user);
return redirect()->to('/');
}
Though I do agree that in a production environment you should not do this. But I can see for some applications if the users are aware the passwords are stored in plain text it may be ok.