Laravel Custom Registration Route/Logic - laravel

I would like to overwrite Laravel 5.2's public function postRegister() in my Auth controller.
I start by changing the route:
Route::post('/auth/register', 'Auth\AuthController#postRegisterI');
In my Auth controller, I now have this new postRegisterI() method as oppose to relying on the foundational postRegister(). But my changes in this new method don't seem to apply?
Is it still pointing to postRegister() for some reason? My AuthController looks like this:
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
public function postRegisterI(Request $request)
{
return print_r($request);
$validator = $this->validator($request->all());
if ($validator->fails()) {
$this->throwValidationException(
$request, $validator
);
}
//Auth::login($this->create($request->all()));
$this->create($request->all());
//return redirect($this->redirectPath());
return;
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|max:255|unique:users',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:2',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
]);
}
}
For example in my ajax requests to /auth/register/ it never echoes out my returns as I indicate and it also keeps trying to redirect me to /home. I'm doing this because instead of passing /auth/register a form, I'm passing it a Javascript array with data to not only create a user (email, password, etc) but also an application that the user needs to join.
Eventually, I would like to create the Application model with that data right when the User is created, but right now I'm not sure that my function is even being called correctly!
EDIT: So, it looks like the __construct() function is causing the issue... should I get rid of it? I don't understand why I need to comment that out to get my returns from postRegisterI()?

First, make sure that your new route is declared before the call to Route::auth(). Otherwise, Laravel will ignore your addition, and will always call the one that Easy Auth comes with.
Second. Why do you want to override a method using another method with a different name? By simply creating a method postRegister in your AuthController you are automatically overriding the method that resides in the Trait. So you shouldn't be worrying about having to add an additional method and route. Unless you want to have a multi-authentication system, and in this case, this wouldn't be the correct solution.
Another thing, for ajax request, make sure you validate that the request is of type ajax:
if ($request->ajax()){
//Code here
return response()->json([],200);
}
This controller is designed to check registration rules, create an user, and redirect to the redirection path. It is not designed for ajax handling, thus, you will need to add code for it

maybe this topic will help you custom register

Related

Laravel: Using multiple columns for authentication

I have a Laravel 6 application.
In a typical application, the user would just specify their email, where the email is unique.
However, in my application, I have 2 columns in the User model that is used to authenticate users.
app_id
email
unique(app_id, email)
So in order to login, we need to pass both an app_id and an email, along with the password. The same email could be used across different app_ids.
How would I achieve this?
The default login actions provided by Auth::routes() are the following:
Route::get('login', 'Auth\LoginController#showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController#login');
This is the default login function, part of the AuthenticatesUsers trait used by the LoginController:
/**
* Handle a login request to the application.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*
* #throws \Illuminate\Validation\ValidationException
*/
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)) {
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);
}
There are a few ways to approach this.
Option 1: Override login function in LoginController
# app/Http/Controllers/Auth/LoginController.php
public function login(Request $request)
{
// add more stuff like validation, return the view you want, etc.
// This is barebones
auth()->attempt($request->only(['app_id', 'login', 'password']);
}
Option 2: Override both the validateLogin and the credentials functions in LoginController
# app/Http/Controllers/Auth/LoginController.php
protected function validateLogin(Request $request)
{
$request->validate([
'app_id' => 'required|string',
'email' => 'required|string',
'password' => 'required|string',
]);
}
/**
* Get the needed authorization credentials from the request.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
protected function credentials(Request $request)
{
return $request->only('app_id', 'email', 'password');
}

Method Illuminate\Auth\RequestGuard::attempt does not exist. when trying custom authentication via web route

I have to authenticate users via an external api (something like ldap) and have been trying to realize authentication via a closure request guard as documented here https://laravel.com/docs/master/authentication#closure-request-guards
It works fine if the user logs in correctly, however on auth failure laravel throws the mentioned error https://laravel.com/docs/master/authentication#closure-request-guards if the failed attempt is returning null from the closure (as it says in the documentation). If it just returns false, laravel doesn't throw an error, however there is no validation feedback.
Auth::viaRequest('ldap', function ($request) {
$credentials = $request->only('login_id', 'password');
if ($user = ExternalLDPAAuth::auth()) {
return $user;
} else {
return null; // laravel throws error
// return false; // <- would not throw error, but no validation
}
}
Is there an easier way to do custom authentication?
I don't really understand the documentation about https://laravel.com/docs/5.7/authentication#authenticating-users
in the end I have to write the guard just like above anyway, right?
You haven't shown the code where you're calling attempt(), but you don't need to use that method when authenticating via the request. You use attempt() when a user attempts to login with credentials and you need to explicitly attempt to authenticate. With request authentication, Laravel will automatically attempt to authenticate as the request is handled, so your code can simply check to see if auth()->check() returns true or not.
In the end I decided to customize the EloquentUserProvider instead of the guard. In the end all i needed was additional logic to validate credentials and retrieve a user by credentials in case the user hadn't logged in yet. I.e. checking the normal eloquent logic first and then checking against the external API if nothing was found (also checking for case of changed password).
class CustomUserProvider extends EloquentUserProvider
{
/**
* 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)
{
// (...)
}
/**
* Retrieve a user by the given credentials.
*
* #param array $credentials
* #return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// (...)
}
}
// config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'custom',
],
// (...)
],
// providers/AuthServiceProvider.php
class AuthServiceProvider extends ServiceProvider
{
/**
* Register any authentication / authorization services.
*
* #return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('custom', function ($app, array $config) {
return new CustomUserProvider($app['hash'], $config['model']);
});
}
}

Specifying Middleware Within Controller's Constructor

Here's a code snippet from laravel in-built RegisterController.
public function __construct(){
$this->middleware('guest'); //What does it actually do?
}
I know that it's a good practice to define middleware in the
controller constructor but I need to know what
$this->middleware('guest') actually does and what parameter (where
?) it sets.
Second question: within the same controller (RegisterController), we use RegisterUser (defined in namespace Illuminate\Foundation\Auth) but it seems we never use it throughout the controller (overriding methods or attributes). I'm a little bit confused. Thnaks in advance!
class RegisterController extends Controller
{
use RegistersUsers; //?????
/**
* Where to redirect users after registration.
*
* #var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}
1) $this->middleware(...) called in a constructor of a controller is only adding what ever value is in ... to an array named middleware on the controller. That is it at that point. Nothing is "ran" at that point. You can see this functionality in the Controller your Controllers extend from, Illuminate\Routing\Controller.
Later when the Router needs to dispatch the Request it will build a middleware stack to pass the request through by gathering the middleware which includes asking the instance of the controller for its defined middleware via getMiddleware.
public function getMiddleware()
{
return $this->middleware;
}
2) The trait is including functionality and variables into your controller. There are routes pointing to some of these methods on the controller.
You could take all that code and paste it into your controller and it would be the same thing basically.
If you need to change any functionality of that controller or customize it you will end up redefining some of those methods or adjusting the variables.

Laravel authentication error message

I'm using custom authentication. Added user status(enable/disable) check in retrieveByCredentials function of my custom provider. Now how can I differentiate the error, whether its coming because user enter wrong credentials or because user is disabled?
So far I looked at following function sendFailedLoginResponse, but there is no way to differentiate.
Any suggestions how can I achieve this?
I've approached this in the following way:
/*
LoginController.php
*/
/**
* Override default login username to be used by the controller.
*
* #return string
*/
public function username()
{
return 'username';
}
/**
* Override default validation of the user login request.
*
* #param \Illuminate\Http\Request $request
*
* #return void
*/
protected function validateLogin(Request $request)
{
$this->validate($request, [
$this->username() => [
'required',
'min:4',
'max:30',
'regex:/^[\S]*$/',
Rule::exists('users')->where(function ($query)
{
$query->where('active', 1);
})
],
'password' => 'required|min:6|max:100'
]);
}
Substituting out whatever the name and expected value of your field is for "active" will enable you to validate depending on whether or not users are active/enabled. You mentioned you've already done this but in this case the validation error will also be the validation message for the "exists" rule. In my application I don't actually need to care why a user failed login but checking the validation message may, I suppose, be enough in your case?

validation failure but already redirect into controller laravel

I try to validate input, but when input incorrect not show error and redirect to another url.My code is something like this:
$rules = [
'point' => 'required|numeric|min:0|max:10',
'id_tieuchi' => 'required|numeric|min:1'
];
and
foreach ($data as $value)
{
$validator = Validator::make( $value, $rules);
}
I also using syntax into foreach but not working because controller not understand what's user in base url ../evaluate/$user/edit:
if ($validator->fails())
return redirect('evaluate/{user}/edit')->withInput()->with('message',$validator->messages()->all());
Laravel comes with Requests that will handle this logic for you. You can leverage them like so:
In App\Http\Requests, create a FooRequest.php and add this code to it:
<?php
namespace App\Http\Requests;
class FooRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
'point' => 'required|numeric|min:0|max:10',
'id_tieuchi' => 'required|numeric|min:1'
];
}
}
And in your FooController.php, be sure to add this to the top of the file:
use App\Http\Requests\FooRequest;
Then in your controller method, use the FooRequest as such:
public function bar(FooRequest $request)
{
// Do stuff here...
}
The FooRequest will validate whatever input you send to the bar method. If validation errors exist, it will automatically redirect back to whatever page you came from with the errors in the validator. If validation passes, it will enter the bar method with the items in the request in $request.
For more information, read about validation in Laravel.

Resources