# Authentication

  • Laravel makes implementing authentication (opens new window) very simple as almost everything is configured for you out of the box
  • We already installed the Laravel Jetstream starterkit (opens new window) which comes with a full authentication scaffolding ready to use
  • The authentication configuration file is located at config/auth.php and contains several well documented options for tweaking the behavior of the authentication services

# Authentication Quickstart

# Routing

  • Laravel provides a quick way to list all routes using one simple command: php artisan route:list All routes
  • The routes that we need for our navigation are:
Method URI Route name Name in our navigation
GET login login Login
GET register register Register
POST logout logout Logout
GET dashboard dashboard Dashboard
GET user/profile profile.show Update Password
  • Only the login and register routes are already defined in our navigation component

# Public views

  • Jetstream added 7 (unprotected) view files to the resources/views/auth folder
    (login, register, verify-email, forgot-password, reset-password, confirm-password and two-factor-challenge)
  • All these views are based on the <x-guest-layout>layout and have no navigation

# Test the full auth cycle

# Login

# Forgot password

  • Logout again and click on Login
  • Click on Forgot your password?
  • Check your email and click on the link in the email

REMARK

First, check the mail configuration in the .env file

# Register

  • Click on Logout
  • Click in the navigation on Register
  • Register a new user, e.g. demo_vinylshop@mailinator.com with password demo1234
    (The password must be at least 8 characters and must be entered twice )
  • The user will be created in the database (open phpMyAdmin and check the table users) and is immediately logged in Login after register

REMARK

In the authentication cycle illustrated above only one error occurred. Obviously, your Laravel application will also react "correctly" in the following scenarios:

  • Login with an unknown email or with a wrong password
  • When registering or resetting the password, the entered password is too short or the two passwords are not equal
  • A password reset link is requested for a non-existing user
  • ...

# Update the public views

  • Let's do some customization of the auth views
    • Replace the Jetstream logo with our own logo
    • Change the light-green color of the information messages to a more distinctive color
    • Change the layout to our own layout
  • Open one of the auth views, e.g. resources/views/auth/forgot-password.blade.php
  • Crtl + Click on <x-jet-authentication-card-logo /> to open the file
    resources/views/vendor/jetstream/components/authentication-card-logo.blade.php
  • Comment out the original code and place the code for our own logo above it
 






<x-tmk.logo class="w-20 h-20 fill-current text-gray-500 animate-spin" />
{{--
<a href="/">
    <svg class="w-16 h-16" ... ></svg>
</a>
--}}
1
2
3
4
5
6

Logo

Log out!

  • You have to log out to get to the forgot-password page.

# Change color of information messages

  • Open the files:
    • resources/views/auth/forgot-password.blade.php
    • resources/views/auth/login.blade.php
  • Find the div around session('status) and change some classes:
    • font-medium to font-bold
    • text-green-600 to text-red-600

 




@if (session('status'))
    <div class="mb-4 font-bold text-sm text-red-600">
        {{ session('status') }}
    </div>
@endif
1
2
3
4
5

Color

# Replace the template layout (optional)

  • It's possible to replace this layout with our <x-vinylshop-layout> layout, but for simplicity we stick to the default layout

REMARKS

  • You don't have to change the layout in this course, but the modification below is just to illustrate how it works and can be useful in your future projects
  • To avoid breaking something, always place the original code in comment and copy the parts we need from the original code to our own template
 



 

{{--
<x-guest-layout>
    <x-jet-authentication-card> ... </x-jet-authentication-card>
</x-guest-layout>
--}}
1
2
3
4
5
  • All public auth views are based on the <x-guest-layout> layout
  • Let's customize this page by replacing the <x-guest-layout> template with our <x-vinylshop-layout> template
  • Open the resources/views/auth/login.blade.php file and set the original code in comment

# Retrieving the authenticated user

  • You may access the authenticated user via the auth() helper function or via the Auth facade
    • For example, the code below can be added to a controller of choice (you don't need to do this -> we will use/illustrate this further on in our navigation component and our admin middleware)
// Get the currently authenticated user
$user = auth()->user();             // or Auth::user();   

// Get one attribute off the currently authenticated user (e.g. name, email, id, ...)
$name = auth()->user()->name;       // or Auth::user()->name;  

// Shortcut for the currently authenticated user's id
$id = auth()->id();                 // or Auth::id();   
1
2
3
4
5
6
7
8

# Authentication directives and helpers inside Blade

  • The @auth and @guest Blade directives may be used to quickly determine if the current user is authenticated or is a guest
  • You can also use the auth() helper function or Auth facade (see above) inside a Blade template
 

 

 

 

@guest
    // only visible for guests and NOT for authenticated users (= not logged in)
@endguest

@auth
    // only visible for authenticated users and NOT for guests (= logged in)
@endauth
1
2
3
4
5
6
7

# Update the navigation component

  • Open the resources/views/components/layout/nav.blade.php file and add the @auth and @guest directives
    • If the user is not logged in, the navigation component should:
      • show the Login and Register links
      • hide the dropdown menu on te right
    • If the user is logged in, the navigation component should:
      • show the dropdown menu on the right
      • hide the Login and Register links

# Dashboard and profile pages

  • A logged-in user can access the dashboard and profile pages but with the default Jetstream <x-app-layout> layout component
  • Open the resources/views/components/layout/nav.blade.php file:
  • Line 6: replace Vinyl+Shop at the end of the href attribute with urlencode(auth()->user()->name)
  • Line 7: replace the alt attribute with auth()->user()->name
  • Line 11: replace My Name with auth()->user()->name
  • Line 12: replace route('home') with route('dashboard')
  • Line 13: replace route('home') with route('profile.show')





 
 



 
 
 









@auth
  <x-jet-dropdown align="right" width="48">
      {{-- avatar --}}
      <x-slot name="trigger">
          <img class="rounded-full h-8 w-8 object-cover cursor-pointer"
               src="https://ui-avatars.com/api/?name={{ urlencode(auth()->user()->name) }}"
               alt="{{ auth()->user()->name }}">
      </x-slot>
      <x-slot name="content">
          {{-- all users --}}
          <div class="block px-4 py-2 text-xs text-gray-400">{{ auth()->user()->name }}</div>
          <x-jet-dropdown-link href="{{ route('dashboard') }}">Dashboard</x-jet-dropdown-link>
          <x-jet-dropdown-link href="{{ route('profile.show') }}">Update Profile</x-jet-dropdown-link>
          <div class="border-t border-gray-100"></div>
          <x-jet-dropdown-link href="{{ route('home') }}">Logout</x-jet-dropdown-link>
          <div class="border-t border-gray-100"></div>
          {{-- admins only --}}
          ...
      </x-slot>
    </x-jet-dropdown>
@endauth
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Update the Dashboard page

  • Open the resources/views/dashboard.blade.php file:
    • Comment out the original code
    • Add the code below to the page

# Update the Profile page

  • Open the resources/views/profile/show.blade.php file:
    • Comment out the original code
    • Add the code below to the page

# Logout

  • When you look at the Logout link in the navigation component, you will see that it is a link to the home route
  • Let's change this to a link to the logout route
  • This will not work because the logout route is a POST route (not a GET route like the other routes) and we can't use a POST route in a link
  • We need to use a form to submit the POST request
  • Let's try to replace the link with a form that submits the POST request to the logout route
    (The classes on the submit button are copied from the x-jet-dropdown-link component)

CSRF protection

  • If you test/submit the form, you get a 419 | Page Expired error, because Laravel automatically protects your application from cross-site request forgery (opens new window) (CSRF) attacks
    • Laravel generates a "token" for each active user session
    • When you submit a form (with a POST request), Laravel checks whether this token corresponds to the mandatory token of the form
  • Add a CSRF token to the form using the @csrf Blade directive, which injects a hidden field (check the source code in the browser)
  • Now the form will work





 






<x-slot name="content">
    {{-- all users --}}
    ...
    <div class="border-t border-gray-100"></div>
    <form method="POST" action="{{ route('logout') }}">
        @csrf
        <button type="submit" class="block w-full text-left px-4 py-2 text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition">Logout</button>
    </form>
    <div class="border-t border-gray-100"></div>
    ...
</x-slot>
1
2
3
4
5
6
7
8
9
10
11

# Protecting Routes with Middleware

  • Middleware (opens new window) can be used to allow only the authenticated users access to a given route
    • Laravel ships with built in auth middleware
    • Since this middleware is already registered in your HTTP kernel, all you need to do is attach the middleware to a route or route group definition
      • Open routes/web.php
      • Protect the 'admin' route group with the auth middleware middleware(['auth']), such that these routes are only available to authenticated users
 




Route::middleware(['auth'])->prefix('admin')->name('admin.')->group(function () {
    Route::redirect('/', '/admin/records');
    Route::get('records', [RecordController::class, 'index'])->name('records.index');
});
1
2
3
4

# Make admin middleware

  • Yet, our site has normal users and admins and the records page should only be accessible by admins
    • Create a new admin middleware with the command php artisan make:middleware Admin
    • Open the file app/Http/Middleware/Admin.php
    • Update the handle() method
      • The abort() helper function sends you to an error page. The first argument determines which specific error page must be shown, and the second (optional) argument can be used to provide a specific error message.




 
 
 
 



class Admin
{
    public function handle($request, Closure $next)
    {
        if (auth()->user()->admin) {
            return $next($request);
        }
        return abort(403, 'Only administrators can access this page');
    }
}
1
2
3
4
5
6
7
8
9
10

NAMING CONVENTIONS

  • The name of a middleware starts with a capital letter (e.g. Admin) as it corresponds to a class
  • Open app/Http/Kernel.php and register the admin middleware to the $routeMiddleware variable



 


protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    ...
    'admin' => \App\Http\Middleware\Admin::class,
];
1
2
3
4
5

# Add middleware to the route group

  • Add the admin middleware, AFTER the auth middleware, to the 'admin' route group
 




Route::middleware(['auth', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    Route::redirect('/', '/admin/records');
    Route::get('records', [RecordController::class, 'index'])->name('records.index');
});
1
2
3
4

Abort 403 error

  • Look at the routes table to see which middleware belongs to a specific route

# Hide admin menu item for non-admins

  • Open resources/views/layouts/nav.blade.php
  • Add a @if(auth()->user()->admin) ... @endif Blade directive surround the admin menu item to hide it for non-admins

# Make active user middleware

  • Yet, we want to ban all the auth pages from users that have a profile status of active is false
    • Create a new active user middleware with the command php artisan make:middleware ActiveUser
    • Open the file app/Http/Middleware/ActiveUser.php
    • Update the handle() method
      • The abort() helper function sends you to an error page. The first argument determines which specific error page must be shown, and the second (optional) argument can be used to provide a specific error message.




 
 
 
 
 
 
 



class ActiveUser
{
    public function handle(Request $request, Closure $next)
    {
        if (auth()->user()->active) {
            return $next($request);
        }
        // logout the user from the web guard
        auth('web')->logout();
        // abort the request with a message
        return abort(403, 'Your account is not active. Please contact the administrator.');
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
  • Open app/Http/Kernel.php and register the ActiveUser middleware to the $routeMiddleware variable




 


protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    ...
    'admin' => \App\Http\Middleware\Admin::class,
    'active' => \App\Http\Middleware\ActiveUser::class,
];
1
2
3
4
5
6
  • Add the active middleware, to the 'admin' route group and the 'auth' route group
 










 







Route::middleware(['auth', 'active', 'admin'])->prefix('admin')->name('admin.')->group(function () {
    Route::redirect('/', '/admin/records');
    Route::get('records', [RecordController::class, 'index'])->name('records.index');
});

...

Route::middleware([
    'auth:sanctum',
    config('jetstream.auth_session'),
    'verified',
    'active',
])->group(function () {
    Route::get('/dashboard', function () {
        return view('dashboard');
    })->name('dashboard');
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • Change the active status of the ITF User 1 user to false in the database
  • Try to login with the ITF User 1 user:
    • Email: itf_user_1@mailinator.com and password: itfuser1
    • You'll be logged out
    • You'll see the 403 error page (with customized message)
Last Updated: 11/23/2022, 3:15:42 PM