Laravel 5.2 authentication with custom routes - php

I'm working in a web project using Laravel 5.2
My use case is very simple:
Before rendering any page, verify if the user is authenticated. If not, provide a login form with custom authentication (not the Eloquent's default stuff)
After reading about this scenario I have:
// routes.php
Route::get('/', 'HomeController#index');
Then if I want all my pages secured, I require the middleware auth in controller's constructor. All my controllers should follow the same pattern if I want to request a logged in user before serving any page.
// app/Http/Controllers/HomeController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return view('home');
}
}
So far, so good. If I visit my app at '/' I'm redirected to a /login page.
I created the login page:
// views/auth/login.blade.php
#extends('layouts.app')
#section('content')
<form class="form-signin" method="post" action="{{ url ('/login') }}">
{!! csrf_field() !!}
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputUsername" class="sr-only">Username</label>
<input type="text" id="inputUsername" class="form-control" placeholder="Username" name="username" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" name="password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
#endsection
Please notice the action of the form that redirects to /login.
Then I update the routes.php by providing the new following routes:
// Authentication Routes...
Route::get('login', 'Auth\AuthController#showLoginForm');
Route::post('login', 'Auth\AuthController#login');
Route::get('logout', 'Auth\AuthController#logout');
Route::get('/', 'HomeController#index');
With these new routes I'm catching login/logout scenarios and assigning AuthController's methods to handle them.
On the already implemented AuthController, I guess I need to define these methods.
I was not able to make this to work or maybe I'm doing this custom authentication in a wrong way.
I have:
// app/Http/Auth/AuthController.php
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller
{
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/';
public function __construct()
{
$this->middleware($this->guestMiddleware(), ['except' => 'logout']);
}
// ... lot of default stuff ..
protected function login($data)
{
//
// do custom login authentication with $data
// ie: validate thru a web service or something
//
return redirect()->intended('/');
}
}
Any suggestions on how to implement this?

Change your login function to
protected function login(Request $data)
{
//
// do custom login authentication with $data
// ie: validate thru a web service or something
//
return redirect()->intended('/');
}
This overrides the function from Illuminate\Foundation\Auth\AuthenticatesUsers that originally used by Laravel Auth

Thanks to everyone who responded to this question. I ended rolling out a custom integration where users are duplicated on the Eloquent's User model and my external service.
Unfortunately the lack of documentation around Laravel 5.2 and custom authentication methods makes this as a patch solution until something more stable comes out.
Thanks and happy coding!

Related

Laravel 9 html form submit throws 405. Expects PUT, request is GET

I'm trying to create a simple CRUD application with Laravel 9. I've run into a problem with HTML forms. I've created a page where you can edit data on rabbits existing in my database.
My form
<form name="editRabbitForm" action="{{ url('/rabbit/update') }}" method="PUT">
{{ csrf_field() }}
<!-- here be input fields -->
<button type="submit" class="btn btn-success">Save</button>
<a type="button" href="/rabbits" class="btn btn-danger">Cancel</a>
</form>
web.php routes
<?php
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\QuoteController;
use App\Http\Controllers\RabbitController;
use Illuminate\Support\Facades\Route;
Route::get('/rabbits', [RabbitController::class, 'index']);
Route::get('/rabbit/edit/{id}', [RabbitController::class, 'edit']);
Route::put('/rabbit/update', [RabbitController::class, 'update']);
RabbitController.php
<?php
namespace App\Http\Controllers;
use App\Models\Rabbit;
use Illuminate\Http\Request;
class RabbitController extends Controller
{
public function index() {
return view('rabbits.index', ['rabbits' => Rabbit::all()]);
}
public function edit($id) {
return view('rabbits.edit', ['rabbit' => Rabbit::where('breed_id', $id)->first()]);
}
public function update(Request $request) {
echo("SOME SORT OF RESULT!");
var_dump($request);
}
}
Before I even hit the controller I get an exception reading:
The GET method is not supported for this route. Supported methods: PUT.
I really don't get what I'm doing wrong in this scenario
As stated above in my comment:
To send a put request you will need to change the method to POST and add #method('PUT') in your form. This will insert a hidden input for the method. Laravel will then automatically route this request to the method specified by the put route in your routes file.
This is needed because as #ADyson writes, browsers are limited to GET and POST request.
And last but not least, browsers or in this case HTML forms are stupid.
Maybe someday this will be changed, who knows.

Laravel 5.8 showing "419 Page Expired" after clicking logout from an already cleared session

I run the php artisan make:auth command and I will explain step by step what I do after that to understand the scenario,
Login to a new session (example.com/home)
opened a new tab and pasted the URL, ie example.com/home.
Now 2 tabs are open with the same session.
I clicked logout from one of the tabs and it works perfectly fine
Then when I tried to logout from the other tab, it gave me an error saying "419 Page Expired" and it is going nowhere even after reloading.
The thing is, these kinds of scenarios may arise, and I don't want to see this error message, just logout after clicking logout, even if the session is expired.
Note: This issue is not because of not adding #csrf
Well that's an obvious message you can maybe try to make a better layout for that page, but still it is good to show it so the user knows what happened. If you want to handle it differently you can try to redirect to the login page.
So in your app\Exceptions\Handler.php file within the render method add this:
if ($exception instanceof \Illuminate\Session\TokenMismatchException) {
return redirect()->route('login');
}
A solution to the problem is relatively simple and requires a small addition to the
VerifyCsrfToken middleware;
use Closure;
public function handle($request, Closure $next)
{
if(!Auth::check() && $request->route()->named('logout')) {
$this->except[] = route('logout');
}
return parent::handle($request, $next);
}
Usually, this file contains just an $except array of routes that should be ignored from csrf.
In this code, we override the handle method and perform two checks.
is the user a guest (ie, not using an authenticated session), and,
is the route the logout route
If both are true then we add 'logout' to the except array. We then pass control to the core VerifyCsrfMiddleware which recognises the presence of the logout route in the array, and bypasses the check. The form data is correctly posted and we are redirected using the LogoutResponse.
The user sees no error page.
By checking in this way, we ensure that CSRF Token still protects genuine logout requests.
It's never too late)
Add app/Http/Middleware/VerifyCsrfToken.php
use Closure;
public function handle($request, Closure $next)
{
if($request->route()->named('logout')) {
if (auth()->check()) {
auth()->logout();
}
return redirect('/');
}
return parent::handle($request, $next);
}
I think these are the best solutions.
IMHO you can try to modify your app/Http/Middleware/VerifyCsrfToken.php file.
Edit the the $except property with something like this:
class VerifyCsrfToken extends Middleware
{
protected $except = [
'http://example.com/logout',
];
In a Laravel 6 project, I ended up modifying the VerifyCsrfTokenMiddleware as follows
As you will see, I simply added the logout named route to list of exclusion.
I overridden the __construct function because we cannot use route() function when initializing a new variable
<?php
namespace App\Http\Middleware;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
];
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Foundation\Application $app
* #param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* #return void
*/
public function __construct(Application $app, Encrypter $encrypter)
{
parent::__construct($app, $encrypter);
$this->except = [
route('logout')
];
}
}
<a href="{{ route('logout') }}" class="dropdown-item notify-item"="event.preventDefault(); document.getElementById('logout-form').submit();">
<i class="fa fa-power-off"></i> <span>{{ __('Logout') }} </span>
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
#csrf
</form>
You have missed the #csrf in you logout form, so only you getting Error 419
You need to add the CSRF Token to your form:
<form action="{{ route('logout') }}" method="POST">
#csrf
<button type="submit" class="btn nav-link">Logout</button>
</form>
Laravel 8.x Docs

How to logout and redirect to login page using Laravel 5.4?

I am using Laravel 5.4 and trying to implement authentication system. I used php artisan command make:auth to setup it. I edited the views according to my layout. Now, when I am trying to logout it throwing me this error
NotFoundHttpException in RouteCollection.php line 161:
could any one help me how to logout?
In your web.php (routes):
add:
Route::get('logout', '\App\Http\Controllers\Auth\LoginController#logout');
In your LoginController.php
add:
public function logout(Request $request) {
Auth::logout();
return redirect('/login');
}
Also, in the top of LoginController.php, after namespace
add:
use Auth;
Now, you are able to logout using yourdomain.com/logout URL or if you have created logout button, add href to /logout
Well even if what suggest by #Tauras just works I don't think it's the correct way to deal with this.
You said you have run php artisan make:auth which should have also inserted Auth::routes(); in your routes/web.php routing files. Which comes with default logout route already defined and is named logout.
You can see it here on GitHub, but I will also report the code here for simplicity:
/**
* Register the typical authentication routes for an application.
*
* #return void
*/
public function auth()
{
// Authentication Routes...
$this->get('login', 'Auth\LoginController#showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController#login');
$this->post('logout', 'Auth\LoginController#logout')->name('logout');
// Registration Routes...
$this->get('register', 'Auth\RegisterController#showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController#register');
// Password Reset Routes...
$this->get('password/reset', 'Auth\ForgotPasswordController#showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController#sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController#showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController#reset');
}
Then again please note that logout requires POST as HTTP request method. There are many valid reason behind this, but just to mention one very important is that in this way you can prevent cross-site request forgery.
So according to what I have just pointed out a correct way to implement this could be just this:
<a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('frm-logout').submit();">
Logout
</a>
<form id="frm-logout" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
Finally note that I have inserted laravel out of the box ready function {{ csrf_field() }}!
You can use the following in your controller:
return redirect('login')->with(Auth::logout());
Best way for Laravel 5.8
100% worked
Add this function inside your Auth\LoginController.php
use Illuminate\Http\Request;
And also add this
public function logout(Request $request)
{
$this->guard()->logout();
$request->session()->invalidate();
return $this->loggedOut($request) ?: redirect('/login');
}
here is another way to do it by calling Auth::logout() in route
Route::get('/logout', function(){
Auth::logout();
return Redirect::to('login');
});
I recommend you stick with Laravel auth routes in web.php: Auth::routes()
It will create the following route:
POST | logout | App\Http\Controllers\Auth\LoginController#logout
You will need to logout using a POST form. This way you will also need the CSRF token which is recommended.
<form method="POST" action="{{ route('logout') }}">
#csrf
<button type="submit">Logout</button>
</form>
In Laravel 6.2
Add the following route to : web.php
Route::post('logout', 'Auth\LoginController#logout')->name('logout');
Using Achor tag with logout using a POST form. This way you will also need the CSRF token.
<a class="log-out-btn" href="#" onclick="event.preventDefault();document.getElementById('logout-form').submit();"> Logout </a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
{{ csrf_field() }}
</form>
In 5.5
adding
Route::get('logout', 'Auth\LoginController#logout');
to my routes file works fine.
I know this question was asked for old version of laravel. I want to share my solution for Laravel 8.65 version.
In AuthenticatedSessionControler update destroy function like this.
public function destroy(Request $request)
{
Auth::guard('web')->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/admin');
}
update your last line from return redirect('/') to return redirect('/admin');
If you used the auth scaffolding in 5.5 simply direct your href to:
{{ route('logout') }}
There is no need to alter any routes or controllers.
It's better and safer to add to your LoginController.php the following code, that runs only after the standard logout:
use AuthenticatesUsers;
protected function loggedOut(Request $request)
{
return redirect('/new/redirect/you/want');
}
In Laravel 9
go to routes directory open web.php and write logout route:
use App\Http\Controllers\Auth\LoginController;
Route::get('logout', [LoginController::class,'logout']);
From blade add logout URL
<a class="dropdown-item" href={{route('logout')}}>Logout</a>
Notice
logout function will redirect to home page /
If you want to redirect to login page you should override on logout function in App\Http\Controllers\Auth\LoginController.php
public function logout(){
return redirect('login')->with(Auth::logout());
}
Just in case someone is interested on this topic for Laravel 8 or 9.
As you can read on the Laravel documentation on https://laravel.com/docs/9.x/authentication#logging-out
In addition to Auth::logout(), you will need to invalidate the user's session and regenerate their CSRF token:
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}
if you are looking to do it via code on specific conditions, here is the solution worked for me. I have used in middleware to block certain users:
these lines from below is the actual code to logout:
$auth = new LoginController();
$auth->logout($request);
Complete File:
namespace App\Http\Middleware;
use Closure;
use Auth;
use App\Http\Controllers\Auth\LoginController;
class ExcludeCustomers{
public function handle($request, Closure $next){
$user = Auth::guard()->user();
if( $user->role == 3 ) {
$auth = new LoginController();
$auth->logout($request);
header("Location: https://google.com");
die();
}
return $next($request);
}
}

laravel 5.2 Authentication Failure

Hello Guys i am new to laravel. When the new user register with authentication i am this error message. How to resolve my problem
The Authentication Failure with this error message.
Illuminate\Auth\SessionGuard::login() must be an instance of Illuminate\Contracts\Auth\Authenticatable, instance of App\customer given.
My controller is
public function store(Request $request)
{
$user = new customer;
$user->name=Input::get('name');
$user->email=Input::get('email');
$user->password=Input::get('password');
$user->save();
Auth::login($user);
return redirect::home();
}
and my routes
Route::get('register', 'testing#index');
Route::post('store', 'testing#store');
Route::get('login', 'testing#create');
Route::post('logout', 'testing#destroy');
and my register page is
<form action="store" method="post">
<label for="name">Name</label>
<input type="text" name="name" autocomplete="off">
<br>
<label for="email">Email</label>
<input type="text" name="email" autocomplete="off">
<br>
<label for="password">Password</label>
<input type="text" name="password" autocomplete="off">
<br>
<input type="hidden" name="_token" value="{{csrf_token()}}">
<br>
<input type="submit" name="submit" value="Submit">
</form>
Please help me guys how to register,login and logout with complete authentation.
Thanks you and welcome your suggestions.
Without knowing your actual model of customer I try to give you the best matching answer.
If you are very new to laravel the best appraoch ist to use
php artisan make:auth
To create:
Home View
App Layout
Auth Routes
Auth Controller
Password Reset
This will be done for you, using the default, already existing, App\User model. If you DO NOT want to user the default App\User and you want to use your App\Customer for authentication, then you will need to at least make your model extend Authenticable
use Illuminate\Foundation\Auth\User as Authenticatable;
class Customer extends Authenticatable
{
...
}
Without this you will surely receive the given error. However, you will still need to ensure that there are the required fields on your Customer model - like email, password, remember token etc. If you do not want to use these you will need to make further adaptions within your authentication controller.
As mentioned the best approach for beginners is using the generated auth. You can find more here: https://laravel.com/docs/5.2/authentication
Be careful - php artisan make:auth will create a few views and it will overwrite existing ones with the same name.
e.g.
/resources/views/home.blade.php
/resources/views/layout/app.blade.php
/resources/views/auth/login.blade.php
/resources/views/auth/register.blade.php
and a few more within auth
Looking at your code you could simply try replacing
$user = new customer;
with
$user = new User
and ofcourse pull in the user model
use App\User;

MethodNotAllowedHttpException in RouteCollection.php line 218:4

I will get MethodNotAllowedHttpException when submitting a form in laravel
Html file
<form method="POST" action="/cards/{{$card->id}}/notes">
<input name="_token" type="hidden" value="{{ csrf_token() }}"/>
<textarea name="body" class="form-control"></textarea>
<button type="submit">Add Note</button>
</form>
routes.php
Route::post('cards/{card}/notes','NotesController#store');
NotesController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
class NotesController extends Controller
{
public function store()
{
return request()->all();
}
}
Make sure you don't have a route, say a Route::post with a parameter that lies in front of the route you are trying to hit.
For example:
Route::post('{something}', 'SomethingController#index');
Route::post('cards/{card}/notes', 'NotesController#store');
In this case, no matter what you try to send to the cards route, it will always hit the something route because {something} is intercepting cards as a valid parameter and triggers the SomethingController.
Put the something route below the cards route and it should work.
MethodNotAllowedHttpException is thrown when no matching route (method and URI) was found, but a route with a matching URI but not matching method was found.
In your case, I guess the issue is because URI parameters differ between the route and the controller.
Here are two alternatives you can try:
Remove the parameter from your route:
Route::post('cards/notes','NotesController#store');
Add the parameter to your controller:
public function store($card)
{
return request()->all();
}
I have tried to solve this error in lumen and it took me quite a lot of time to figure out the problem.
The problem is with laravel itself.
Sometimes if you have another route like GET device/{variable}, laravel stops in this first route...
So what you need to do is change the route POST device to POST device/add
This link helped me a lot

Categories