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.
Related
I am trying to ingrate this flow of authentication in a Laravel 7 applciation:
User enters their email, submits the form.
User is sent to a form with a single input field;
System sends email to the user with a 10 (or whatever) digit code;
User inputs that code and is authenticated;
If on a later stage the user tries to enter the same email - same thing happens with a new code. No passwords or whatever.
Is there any way to achieve that out of the box (or with a package) or should I write it myself?
Yes, the efficient way to do is to inherit the laravel auth methods and change them accordingly. Make a resource controller by any name ex- UserController and write this code--
public $successStatus = 200;
public function login(Request $request){
Log::info($request);
if(Auth::attempt(['email' => request('email'), 'password' => request('password')])){
return view('home');
}
else{
return Redirect::back ();
}
}
public function loginWithOtp(Request $request){
Log::info($request);
$user = User::where([['email','=',request('email')],['otp','=',request('otp')]])->first();
if( $user){
Auth::login($user, true);
User::where('email','=',$request->email)->update(['otp' => null]);
return view('home');
}
else{
return Redirect::back ();
}
}
public function sendOtp(Request $request){
$otp = rand(1000,9999);
Log::info("otp = ".$otp);
$user = User::where('email','=',$request->email)->update(['otp' => $otp]);
// send otp to email using email api
return response()->json([$user],200);
}
then add these routes to your routes file--
Route::post('login', 'UserController#login')->name('newlogin');
Route::post('loginWithOtp', 'UserController#loginWithOtp')->name('loginWithOtp');
Route::get('loginWithOtp', function () {
return view('auth/OtpLogin');
})->name('loginWithOtp');
Route::any('sendOtp', 'UserController#sendOtp');
and then add OtpLogin.blade.php view and you are good to go
I have two different tables for users credentials: "users" and "contacts". In contacts I have stored users emails, phone and other contacts informations. I want to use the emails in contacts table for the login and authentication methods in Laravel but I don't know how to setup the User and Contacts models or something else that I need. Any idea how can I use contacts and users tables for the login in the application? I'm new in Laravel, any help is appreciated, thank you!
Below a basic tables structure in mysql db:
users: id | username | password
contacts: email | phone | other | user_id
In your LoginController, you can overwrite the attemptLogin method as follow:
public function attemptLogin(Request $request) {
$contact = Contact::where('email', $email)->first();
if (Auth::attempt(['id' => $contact->user_id, 'password' => $password])) {
// Authentication passed...
}
}
OR
public function attemptLogin(Request $request) {
$user = User::whereHas('contacts', function($query){
$query->where('email', $email);
});
if (Auth::login($user)) {
// Authentication passed...
}
}
Instead of overwriting attemptLogin(), which somehow does not work out for me. I prefer overwriting credentials(), which ensures that all auth which is "correctly" implemented will use the "correct" values.
How this could look like:
/**
* Get authorization credentials from request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
// username is set in LoginController to email
$credentials = $request->only($this->username(), 'password');
$contact = Contact::where($this->username(), $credentials[$this->username()])->first();
// if we've not found a contact, we fallback to default
if (! $contact) {
return $credentials;
}
// else we set user_id and password as credential params
return array_merge($request->only('password'), [
'user_id' => $contact->user_id,
]);
}
i'm prototyping an API with Laravel and noticed that the API-Token is not case-sensitive when using the standard Auth-Guard for API. So api_tokens like 'CVC' and 'cvc' are treated the same.
Is that an expected behaviour? Is that ideal in regard of security? Dont think so, even with a 60-byte-string, or what do you think? And is there a way to change that?
Thanks for your thoughts!
Carsten
This shouldn't be the case. Laravel attempts to resolve the token in several ways first
* Get the token for the current request.
*
* #return string
*/
public function getTokenForRequest()
{
$token = $this->request->query($this->inputKey);
if (empty($token)) {
$token = $this->request->input($this->inputKey);
}
if (empty($token)) {
$token = $this->request->bearerToken();
}
if (empty($token)) {
$token = $this->request->getPassword();
}
return $token;
}
Where that method is invoked when attempting to resolve an instance of the user:
/**
* Get the currently authenticated user.
*
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
// If we've already retrieved the user for the current request we can just
// return it back immediately. We do not want to fetch the user data on
// every call to this method because that would be tremendously slow.
if (! is_null($this->user)) {
return $this->user;
}
$user = null;
$token = $this->getTokenForRequest();
if (! empty($token)) {
$user = $this->provider->retrieveByCredentials(
[$this->storageKey => $token]
);
}
return $this->user = $user;
}
And the provider in this case is the DatabaseUserProvider, which the method retrieveByCredentials performs a strict case-sensitive check using the Database Factories ->where() method, no like is used, you can see that here:
public function retrieveByCredentials(array $credentials)
{
// 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
// generic "user" object that will be utilized by the Guard instances.
$query = $this->conn->table($this->table);
foreach ($credentials as $key => $value) {
if (! Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
// Now we are ready to execute the query to see if we have an user matching
// the given credentials. If not, we will just return nulls and indicate
// that there are no matching users for these given credential arrays.
$user = $query->first();
return $this->getGenericUser($user);
}
So no, your case is not typical, and likely there are other components in play here that we're not privy to.
I am trying to check if the user is active then redirect it to the application and if the user is not active then keep the user on login page saying "your account has been disabled or you dont have a valid account. Please contact your admin". I am done with the whole application but this is the only thing I cant be able to understand. I tried it Handler.php, tried writing postLogin(), getLogin() functions, changed the route too but still cant be able to figure out. This authentication method of laravel is so confusing as we dont know where the functions are. Any help would be much appreciated.
Routes
// Route::auth();
Route::get('auth/login', 'Auth\AuthController#getLogin');
Route::post('auth/login', 'Auth\AuthController#postLogin');
Route::get('auth/logout', 'Auth\AuthController#getLogout');
AuthController
In postLogin() I am just checking whether it goes in to the function or not but it not going in either of the functions if I comment out route::auth()
public function postLogin(Request $request)
{
echo "getting in this function";
}
public function getLogin($id)
{
echo "getting in to this function";
$users = User::find($id);
if($users->is_active == 1) {
return redirect()->intended('dashboard');
} else {
Session::flash("message", "Authentication failed. Please contact your admin.");
return redirect('/login');
}
}
For this, first I would suggest to use Laravel's default route and controllers for authentication and then amend it as per the need.
This will surely work:
routes/web.php
Route::auth();
Http/Controllers/Auth/LoginController.php
public function login(Request $request)
{
$email = $request->input('email');
$password = $request->input('password');
$remember = $request->input('remember') ? true : false;
if ($user = Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
if(Auth::user()->is_active == 1) {
return redirect()->intended('dashboard');
} else {
Auth::logout();
Session::flash("message", "Authentication failed. Please contact your admin.");
return redirect('/login');
}
} else {
return $this->sendFailedLoginResponse($request);
}
}
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!