What to expect in the next generation of Laravel Forge. Read the blog post
Rewatch this lesson
Courses
/
Getting Started with Laravel
/
Basic authentication: Login/Logout

Next video in… 10

What's Next?

Basic authentication: Login/Logout

Getting Started with Laravel

Basic authentication: Login/Logout
Complete the authentication system with login and logout functionality. Learn about sessions, middleware, and protecting routes from unauthorized access.

I've created at least two accounts now, mostly because I don't have a sign-in page just yet. So let's fix that—we're almost done!

Users can register, but what happens when they come back tomorrow? They need to log in! Let's complete our authentication system with login and logout functionality. This is the final piece to make Chirper a real multi-user application.

Step 1: Create the Login Form

Let's make it so we don't see that 404 Not Found error again when we try to visit the login page.

In our resources/views, we have the auth directory for register. I'm going to create a new file for login.blade.php. We can start with the register page itself and just make changes, or you can just paste in what's in the lesson. There's nothing too crazy here—it's exactly what the register page is, just with fewer fields and directing to a login method.

Create resources/views/auth/login.blade.php:

1<x-layout>
2 <x-slot:title>
3 Sign In
4 </x-slot:title>
5 
6 <div class="hero min-h-[calc(100vh-16rem)]">
7 <div class="hero-content flex-col">
8 <div class="card w-96 bg-base-100">
9 <div class="card-body">
10 <h1 class="text-3xl font-bold text-center mb-6">Welcome Back</h1>
11 
12 <form method="POST" action="/login">
13 @csrf
14 
15 <!-- Email -->
16 <label class="floating-label mb-6">
17 <input type="email"
18 name="email"
19 placeholder="[[email protected]](<mailto:[email protected]>)"
20 value="{{ old('email') }}"
21 class="input input-bordered @error('email') input-error @enderror"
22 required
23 autofocus>
24 <span>Email</span>
25 </label>
26 @error('email')
27 <div class="label -mt-4 mb-2">
28 <span class="label-text-alt text-error">{{ $message }}</span>
29 </div>
30 @enderror
31 
32 <!-- Password -->
33 <label class="floating-label mb-6">
34 <input type="password"
35 name="password"
36 placeholder="••••••••"
37 class="input input-bordered @error('password') input-error @enderror"
38 required>
39 <span>Password</span>
40 </label>
41 @error('password')
42 <div class="label -mt-4 mb-2">
43 <span class="label-text-alt text-error">{{ $message }}</span>
44 </div>
45 @enderror
46 
47 <!-- Remember Me -->
48 <div class="form-control mt-4">
49 <label class="label cursor-pointer justify-start">
50 <input type="checkbox"
51 name="remember"
52 class="checkbox">
53 <span class="label-text ml-2">Remember me</span>
54 </label>
55 </div>
56 
57 <!-- Submit Button -->
58 <div class="form-control mt-8">
59 <button type="submit" class="btn btn-primary btn-sm w-full">
60 Sign In
61 </button>
62 </div>
63 </form>
64 
65 <div class="divider">OR</div>
66 <p class="text-center text-sm">
67 Don't have an account?
68 <a href="/register" class="link link-primary">Register</a>
69 </p>
70 </div>
71 </div>
72 </div>
73 </div>
74</x-layout>

You can start to tell how a lot of this is repeatable in the sense of once you get the hang of what a form looks like, especially if it's a form that has similar fields, the next time you implement that form, it's going to be a lot easier. And Laravel just makes it easier too.

Step 2: Create Login and Logout Controllers

We created an Auth/Logout class already in the previous lesson for demonstration. Now we just need our Auth/Login. Following Laravel's single action controller pattern, let's create separate controllers for login and logout:

1php artisan make:controller Auth/Login --invokable
2php artisan make:controller Auth/Logout --invokable

If we go to our Login auth controller, this is our invokable single-class controller. This is going to be one of the few controllers that's a little bit different, but only slightly. We're going to pull in that Auth facade.

In app/Http/Controllers/Auth/Login.php:

1<?php
2 
3namespace App\Http\Controllers\Auth;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Auth;
8 
9class Login extends Controller
10{
11 public function __invoke(Request $request)
12 {
13 // Validate the input
14 $credentials = $request->validate([
15 'email' => 'required|email',
16 'password' => 'required',
17 ]);
18 
19 // Attempt to log in
20 if (Auth::attempt($credentials, $request->boolean('remember'))) {
21 // Regenerate session for security
22 $request->session()->regenerate();
23 
24 // Redirect to intended page or home
25 return redirect()->intended('/')->with('success', 'Welcome back!');
26 }
27 
28 // If login fails, redirect back with error
29 return back()
30 ->withErrors(['email' => 'The provided credentials do not match our records.'])
31 ->onlyInput('email');
32 }
33}

What this Auth facade is doing is we're using the attempt helper with this facade. We're taking the credentials that we've been given (this email and password), we're attaching the Remember checkbox if they gave that, and we're requesting a new session—we're regenerating that new session.

This attempt helper automatically has that remember variable. If it's false, it's false by default. Otherwise, if we pass that in, then it's true. This is doing all the heavy lifting for us.

Then we'll return back. Otherwise, if the auth attempt fails for whatever reason, then we're going to return back with errors. This is a neat little return helper to say, "Hey, we're returning to the previous page, regardless of where that was."

In app/Http/Controllers/Auth/Logout.php:

1<?php
2 
3namespace App\Http\Controllers\Auth;
4 
5use App\Http\Controllers\Controller;
6use Illuminate\Http\Request;
7use Illuminate\Support\Facades\Auth;
8 
9class Logout extends Controller
10{
11 public function __invoke(Request $request)
12 {
13 Auth::logout();
14 
15 // Invalidate session
16 $request->session()->invalidate();
17 $request->session()->regenerateToken();
18 
19 return redirect('/')->with('success', 'You have been logged out.');
20 }
21}

And seeing that we're requesting this session to be regenerated, it reminded me that I think we want to invalidate the session as well in the logout controller. So instead of just logging out this user, we can actually simplify this with Auth::logout() because we already know the authenticated user. Then we can say we need the request session and we're going to invalidate that, as well as regenerate a new token, so that way the user who is then not logged in still has a fresh token.

Step 3: Add Login/Logout Routes

Let's go ahead and add this login route to our web routes. In routes/web.php:

1use App\Http\Controllers\Auth\Login;
2use App\Http\Controllers\Auth\Logout;
3 
4// Login routes
5Route::view('/login', 'auth.login')
6 ->middleware('guest')
7 ->name('login');
8 
9Route::post('/login', Login::class)
10 ->middleware('guest');
11 
12// Logout route
13Route::post('/logout', Logout::class)
14 ->middleware('auth')
15 ->name('logout');

We want this to be a named route because we are using named routes. What we do need is the Route::view() to be able to pass in the /login page to the login view. And this is where we can have the middleware of guest and the named login route.

Why don't we name the logout as well, even though I don't think we're using it anywhere, but it's also helpful just in case we wanted to use it.

Note: Laravel automatically redirects to /login when an unauthenticated user tries to access a protected route. That's why we name it login.

Step 4: Update Chirp Component for Auth

Now we have this login page, and we should be able to log in with our previous email that we created. The logout button should work even with the named route.

If you haven't already, let's make sure we update resources/views/components/chirp.blade.php to properly check ownership so that way we know if the user can or cannot update or delete a chirp:

1@props(['chirp'])
2 
3<div class="card bg-base-100 shadow">
4 <div class="card-body">
5 <div class="flex space-x-3">
6 @if ($chirp->user)
7 <div class="avatar">
8 <div class="size-10 rounded-full">
9 <img src="<https://avatars.laravel.cloud/>{{ urlencode($chirp->user->email) }}"
10 alt="{{ $chirp->user->name }}'s avatar" class="rounded-full" />
11 </div>
12 </div>
13 @else
14 <div class="avatar placeholder">
15 <div class="size-10 rounded-full">
16 <img src="<https://avatars.laravel.cloud/f61123d5-0b27-434c-a4ae-c653c7fc9ed6?vibe=stealth>"
17 alt="Anonymous User" class="rounded-full" />
18 </div>
19 </div>
20 @endif
21 
22 <div class="min-w-0 flex-1">
23 <div class="flex justify-between w-full">
24 <div class="flex items-center gap-1">
25 <span class="text-sm font-semibold">{{ $chirp->user ? $chirp->user->name : 'Anonymous' }}</span>
26 <span class="text-base-content/60">·</span>
27 <span class="text-sm text-base-content/60">{{ $chirp->created_at->diffForHumans() }}</span>
28 @if ($chirp->updated_at->gt($chirp->created_at->addSeconds(5)))
29 <span class="text-base-content/60">·</span>
30 <span class="text-sm text-base-content/60 italic">edited</span>
31 @endif
32 </div>
33 
34 <!-- Replace the temporary @php block and $canEdit check with: -->
35 @can('update', $chirp)
36 <div class="flex gap-1">
37 <a href="/chirps/{{ $chirp->id }}/edit" class="btn btn-ghost btn-xs">
38 Edit
39 </a>
40 <form method="POST" action="/chirps/{{ $chirp->id }}">
41 @csrf
42 @method('DELETE')
43 <button type="submit"
44 onclick="return confirm('Are you sure you want to delete this chirp?')"
45 class="btn btn-ghost btn-xs text-error">
46 Delete
47 </button>
48 </form>
49 </div>
50 @endcan
51 </div>
52 <p class="mt-1">{{ $chirp->message }}</p>
53 </div>
54 </div>
55 </div>
56</div>

This ensures that we only show the edit and delete buttons for chirps that the current user owns.

Step 5: What is this "Remember Me" Functionality

Laravel's "Remember Me" feature:

  • Creates a long-lived cookie

  • Allows users to stay logged in between browser sessions

  • Automatically re-authenticates users when they return

The remember_token column in the users table stores this securely.

Testing the Complete Flow

Let's test the complete authentication flow:

  1. Logout if you're logged in (click logout in the nav)

  2. Try to create a chirp - you'll be redirected to login because of our middleware protection

  3. Log in with your credentials

  4. Check "Remember me" to stay logged in

  5. Create a chirp - it works and shows your avatar!

  6. Close your browser and come back - still logged in thanks to the remember token!

We are using the default authentication, the auth helper, and facade that Laravel gives, which includes remembering us and remembering that session.

Security Features

Laravel automatically protects you from:

  • Session hijacking - Sessions are regenerated on login

  • CSRF attacks - All forms require CSRF tokens

  • Password exposure - Passwords are hashed with bcrypt

  • Timing attacks - Password comparison is constant-time

  • Session fixation - Session IDs change after login

What We've Built

Just to recap what we've accomplished: if we try to create a chirp, because we have middleware attached to any of the POST methods for creating a chirp, editing a chirp, or anything like that, we can't do it unless we're authenticated. It takes us directly to the login page. We can register a new account if we'd like, or log back in with existing credentials.

Congratulations! You now have a complete authentication system:

  • ✅ User registration

  • ✅ Login with "remember me" functionality

  • ✅ Logout with session cleanup

  • ✅ Protected routes with middleware

  • ✅ User-specific content and permissions

Your Chirper app is now a real multi-user application where:

  • Anyone can view the chirp feed

  • Only registered users can create chirps

  • Users can only edit/delete their own chirps

  • Each chirp shows the user's avatar and name

We have the ability to add new chirps, and we can see that each chirp is attached to our particular user with our avatar. Logging out and signing in with a different email means that I cannot edit or delete chirps that another user created, but I can definitely edit and delete my own chirps. Full CRUD functionality!

Quick Design Update

Thankfully, the Laravel team has a TON of incredible designers who can help us make this look a lot better with just a small handful of changes. Now, the majority of these changes are all done in the app.css file for simplicity sake. Typically, within a modern application that uses TailwindCSS, you would modify the markup (HTML) itself. However, since I want to have you change as little as possible within your application, we are doing some things that wouldn't be considered "best practice."

That being said, the benefit of modern CSS with Laravel by using something like TailwindCSS is that everything just works. After all, Blade template files like we mentioned at the start is just HTML with some added niceties. All of Laravel's starter kits have TailwindCSS included out of the box so it's nice to learn to embrace it when at all possible.

In your resources/css/app.css file, we can add the following changes after the existing code:

1/* CHIRPER CUSTOM LARAVEL THEME */
2:root:has(input.theme-controller[value=laravelChirper]:checked),
3[data-theme="laravelChirper"] {
4 color-scheme: light;
5 
6 /* Canvas + text (neutral / cooler beige balance) */
7 --color-base-50: #fafafa; /* almost pure white, very faint gray */
8 --color-base-100: #f7f7f8; /* neutral canvas */
9 --color-base-200: #ededee; /* light neutral gray (still contrasty w/ border) */
10 --color-base-300: #e0e1e3; /* mid gray, cooler but not blue */
11 --color-base-content: #1a1a1c; /* neutral dark, softer than #111113 */
12 
13 /* Text tones */
14 --text-12: var(--color-base-content);
15 --text-10: color-mix(in oklab, var(--color-base-content) 64%, transparent);
16 
17 /* Brand / actions */
18 --color-primary: #5580d2; /* cooler button blue */
19 --color-primary-content: #ffffff;
20 --color-secondary: #2f3136; /* neutral charcoal */
21 --color-secondary-content: #ffffff;
22 --link: #5580d2;
23 --link-hover: color-mix(in oklab, #5580d2 80%, black);
24 
25 /* Borders / elevation */
26 --border: 1px;
27 --border-color-soft: rgba(0, 0, 0, 0.08); /* slightly stronger for visibility */
28 
29 /* Radii + misc */
30 --radius-xs: 3px;
31 --radius-sm: 5px;
32 --radius-field: 8px;
33 --radius-box: 10px;
34 
35 --radius-selector: var(--radius-sm);
36 --radius-field: var(--radius-field);
37 --radius-box: var(--radius-box);
38 
39 --depth: 1;
40 --noise: 0;
41}
42 
43/* Page background & base text */
44html[data-theme="laravelChirper"],
45:root:has(input.theme-controller[value=laravelChirper]:checked) body {
46 color: var(--text-12);
47}
48 
49/* Navbar = sand + hairline bottom */
50.navbar {
51 background: var(--color-base-100);
52 border-bottom: var(--border) solid var(--border-color-soft);
53 backdrop-filter: saturate(140%) blur(6px);
54}
55 
56/* Cards — no shadow, soft border, tidy radius */
57:where(.card) {
58 background: var(--color-base-100);
59 color: var(--text-12);
60 border-radius: var(--radius-box);
61 border: var(--border) solid var(--border-color-soft);
62 box-shadow: none !important;
63 /* kill all shadows */
64 transition: none;
65 /* no hover lift */
66}
67 
68:where(.card:hover),
69:where(.card.shadow),
70:where(.card.shadow:hover) {
71 box-shadow: none !important;
72}
73 
74:where(.card .card-body) {
75 padding: .875rem .875rem 1.125rem;
76}
77 
78/* a hair more breathing room */
79 
80/* INPUTS / TEXTAREAS */
81:where(.input, .textarea) {
82 border-radius: var(--radius-field);
83 border: var(--border) solid var(--border-color-soft);
84 background: #fff;
85}
86 
87/* reduce default textarea padding a touch */
88:where(.textarea) {
89 padding: .625rem .75rem;
90}
91 
92/* inside cards, a hair tighter */
93.card :where(.textarea) {
94 padding: .5625rem .75rem;
95}
96 
97:where(.input:focus, .textarea:focus) {
98 outline: 2px solid color-mix(in oklab, var(--color-primary) 25%, transparent);
99 outline-offset: 2px;
100}
101 
102/* LINKS (site-wide) */
103a {
104 color: var(--link);
105}
106 
107a:hover {
108 color: var(--link-hover);
109}
110 
111/* Fix: anchor-buttons should NOT be blue like links */
112:where(a.btn) {
113 color: inherit;
114 text-decoration: none;
115}
116 
117/* BUTTONS */
118:where(.btn) {
119 border-radius: 5px;
120 box-shadow: none;
121 font-weight: 500;
122 /* medium */
123 font-size: 0.875rem;
124 /* Tailwind text-base */
125 line-height: 1.25rem;
126 /* align with text-base */
127 padding-inline: 1rem;
128 /* a bit more horizontal breathing room */
129}
130 
131:where(.btn-primary),
132:where(a.btn-primary) {
133 background: var(--color-primary);
134 color: var(--color-primary-content);
135 border: none;
136}
137 
138:where(.btn-primary:hover),
139:where(a.btn-primary:hover) {
140 background: color-mix(in oklab, var(--color-primary) 85%, black);
141}
142 
143:where(.btn-ghost),
144:where(a.btn-ghost) {
145 color: var(--text-12);
146 border: var(--border) solid var(--border-color-soft);
147 background: transparent;
148}
149 
150:where(.btn-ghost:hover),
151:where(a.btn-ghost:hover) {
152 background: rgba(0, 0, 0, 0.03);
153}
154 
155:where(.btn-sm) {
156 height: 2rem;
157 min-height: 2rem;
158 padding-inline: .75rem;
159}
160 
161:where(.btn-xs) {
162 height: 1.5rem;
163 min-height: 1.5rem;
164 padding-inline: .5rem;
165 font-size: .75rem;
166}
167 
168/* Toast-only success color */
169:where(.toast) {
170 --color-success: #598b6e;
171 --color-success-content: #ffffff;
172}
173 
174/* Hairline border + ensure text/icons pick up the content color */
175:where(.toast .alert-success) {
176 background: var(--color-success);
177 color: var(--color-success-content);
178 border: var(--border) solid rgba(0, 0, 0, 0.08);
179}
180 
181:where(.link-primary) {
182 color: var(--color-primary) !important;
183}
184 
185:where(.link-primary:hover) {
186 color: color-mix(in oklab, var(--color-primary) 85%, black) !important;
187}
188 
189/* Timestamps / separators (like your `·` and “ago”) */
190.text-base-content\\/60 {
191 color: var(--text-10) !important;
192}
193 
194/* Footer strip */
195footer.footer {
196 background: var(--color-base-200);
197 color: var(--text-10);
198}
199 
200/* 1) Links shouldn't style buttons (fixes blue anchor-buttons) */
201a:not(.btn) {
202 color: var(--link);
203}
204 
205a:not(.btn):hover {
206 color: var(--link-hover);
207}
208 
209/* Anchor buttons keep button colors in all states */
210a.btn,
211a.btn:visited {
212 color: inherit !important;
213 text-decoration: none;
214}
215 
216a.btn-ghost,
217a.btn-ghost:visited {
218 color: var(--text-12) !important;
219}
220 
221a.btn-primary,
222a.btn-primary:visited {
223 color: var(--color-primary-content) !important;
224}
225 
226/* keep ghost text from turning blue on hover */
227a.btn-ghost:hover {
228 color: var(--text-12) !important;
229}
230 
231/* 2) Auth inputs full width inside cards/labels */
232.card .input,
233.card .textarea,
234.floating-label>input,
235.floating-label>.input,
236.floating-label>.textarea {
237 width: 100%;
238}
239 
240/* 3) Slightly rounder cards & fields*/
241:root:has(input.theme-controller[value=laravelChirper]:checked),
242[data-theme="laravelChirper"] {
243 --radius-box: 10px;
244 /* was 8px */
245 --radius-field: 8px;
246 /* was 6px */
247 --noise: 0;
248 /* was 1; Community look is clean */
249}
250 
251/* ensure cards pick up the token over defaults */
252.card {
253 border-radius: var(--radius-box) !important;
254}
255 
256h1,
257h2,
258h3,
259h4,
260h5,
261h6 {
262 font-weight: 400 !important;
263}

Next, we added some Favicons and an OG image and referenced them in our resources/views/components/layout.blade.php file. If you would like to use those for your application you can find them in the Github repository here.

In that same Layout file, we have a new logo to add. This can replace the existing button logo that we were using with our cute little bird emoji and our existing footer respectively.

1<a href="/">
2 <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 312 69"
3 class="h-7 w-auto px-4">
4 <path fill="#D9D9D9"
5 d="M40.97.446c22.091 0 40 17.909 40 40a39.805 39.805 0 0 1-6.56 21.95c-.61.928-1.87 1.105-2.825.541a18.92 18.92 0 0 0-9.651-2.63c-.787 0-1.563.048-2.325.141a19.108 19.108 0 0 0-7.03-9.607 18.57 18.57 0 0 0 5.996-11.778l8.493-7.543a.931.931 0 0 0-.43-1.607l-10.423-2.138c-3.207-5.575-9.218-9.329-16.103-9.329-10.256 0-18.57 8.33-18.571 18.605 0 1.274.128 2.519.372 3.721-6.58.032-12.37 3.407-15.764 8.517-1.07 1.61-3.854 1.582-4.265-.306a40.101 40.101 0 0 1-.914-8.537c0-22.091 17.908-40 40-40Z" />
6 <path fill="url(#a)"
7 d="M40.97.446c22.091 0 40 17.909 40 40a39.805 39.805 0 0 1-6.56 21.95c-.61.928-1.87 1.105-2.825.541a18.92 18.92 0 0 0-9.651-2.63c-.787 0-1.563.048-2.325.141a19.108 19.108 0 0 0-7.03-9.607 18.57 18.57 0 0 0 5.996-11.778l8.493-7.543a.931.931 0 0 0-.43-1.607l-10.423-2.138c-3.207-5.575-9.218-9.329-16.103-9.329-10.256 0-18.57 8.33-18.571 18.605 0 1.274.128 2.519.372 3.721-6.58.032-12.37 3.407-15.764 8.517-1.07 1.61-3.854 1.582-4.265-.306a40.101 40.101 0 0 1-.914-8.537c0-22.091 17.908-40 40-40Z" />
8 <path fill="#D9D9D9"
9 d="M45.065 28.834a3.718 3.718 0 0 1 3.714 3.72 3.719 3.719 0 0 1-3.715 3.722 3.719 3.719 0 0 1-3.713-3.721 3.718 3.718 0 0 1 3.714-3.721Z" />
10 <path fill="url(#b)"
11 d="M45.065 28.834a3.718 3.718 0 0 1 3.714 3.72 3.719 3.719 0 0 1-3.715 3.722 3.719 3.719 0 0 1-3.713-3.721 3.718 3.718 0 0 1 3.714-3.721Z" />
12 <path fill="#1B1B18"
13 d="m299.633 26.19 1.798 8.246v22.01h-7.192V26.19h5.394Zm.496 11.656-1.488-.682V31.77l.558-.744c.413-.702 1.074-1.488 1.984-2.356a13.566 13.566 0 0 1 3.162-2.232c1.24-.62 2.5-.93 3.782-.93.62 0 1.198.042 1.736.124.578.083 1.012.228 1.302.434v6.51h-1.984c-2.728 0-4.774.434-6.138 1.302-1.364.827-2.336 2.15-2.914 3.968ZM273.838 57.066c-2.976 0-5.58-.64-7.812-1.922-2.232-1.322-3.989-3.162-5.27-5.518-1.24-2.356-1.86-5.104-1.86-8.246 0-3.182.62-5.952 1.86-8.308 1.281-2.397 3.038-4.257 5.27-5.58 2.232-1.322 4.836-1.984 7.812-1.984 3.017 0 5.642.682 7.874 2.046 2.273 1.323 4.03 3.183 5.27 5.58 1.24 2.356 1.86 5.084 1.86 8.184 0 .372-.021.744-.062 1.116 0 .372-.021.703-.062.992H265.22V37.66h18.166l-1.488 3.286c0-2.852-.661-5.187-1.984-7.006-1.323-1.818-3.327-2.728-6.014-2.728-2.439 0-4.381.765-5.828 2.294-1.405 1.488-2.108 3.472-2.108 5.952v3.286c0 2.563.703 4.609 2.108 6.138 1.447 1.488 3.431 2.232 5.952 2.232 2.273 0 4.03-.475 5.27-1.426a13.445 13.445 0 0 0 3.286-3.534l5.146 3.1c-1.364 2.563-3.183 4.506-5.456 5.828-2.273 1.323-5.084 1.984-8.432 1.984ZM242.175 57.128c-2.728 0-5.104-.64-7.13-1.922-1.984-1.281-3.534-3.1-4.65-5.456-1.116-2.356-1.674-5.146-1.674-8.37 0-3.224.558-6.014 1.674-8.37 1.158-2.397 2.728-4.236 4.712-5.518 2.026-1.322 4.382-1.984 7.068-1.984 2.811 0 5.25.662 7.316 1.984 2.108 1.323 3.741 3.183 4.898 5.58 1.158 2.356 1.736 5.126 1.736 8.308 0 3.1-.578 5.849-1.736 8.246-1.157 2.356-2.79 4.196-4.898 5.518-2.108 1.323-4.546 1.984-7.316 1.984Zm-15.81 11.098V26.19h5.394l1.798 6.634h-.62v16.182h.62v19.22h-7.192Zm14.632-17.546c2.315 0 4.196-.847 5.642-2.542 1.447-1.736 2.17-3.988 2.17-6.758 0-2.81-.723-5.084-2.17-6.82-1.446-1.736-3.327-2.604-5.642-2.604-2.273 0-4.154.868-5.642 2.604-1.446 1.695-2.17 3.948-2.17 6.758 0 2.811.724 5.084 2.17 6.82 1.488 1.695 3.369 2.542 5.642 2.542ZM209.902 26.19l1.798 8.246v22.01h-7.192V26.19h5.394Zm.496 11.656-1.488-.682V31.77l.558-.744c.413-.702 1.075-1.488 1.984-2.356a13.566 13.566 0 0 1 3.162-2.232c1.24-.62 2.501-.93 3.782-.93.62 0 1.199.042 1.736.124.579.083 1.013.228 1.302.434v6.51h-1.984c-2.728 0-4.774.434-6.138 1.302-1.364.827-2.335 2.15-2.914 3.968ZM196.93 56.446h-7.192V26.19h7.192v30.256Zm-8.122-39.68c0-1.322.392-2.397 1.178-3.224.826-.868 1.942-1.302 3.348-1.302 1.322 0 2.397.434 3.224 1.302.826.827 1.24 1.902 1.24 3.224 0 1.323-.414 2.398-1.24 3.224-.827.827-1.902 1.24-3.224 1.24-1.406 0-2.522-.413-3.348-1.24-.786-.826-1.178-1.901-1.178-3.224ZM155.405 56.446V14.224h7.192v42.222h-7.192Zm19.902 0V37.598c0-2.025-.434-3.368-1.302-4.03-.827-.702-1.86-1.054-3.1-1.054-1.158 0-2.356.248-3.596.744a11.427 11.427 0 0 0-3.286 1.922 10.082 10.082 0 0 0-2.418 2.728l-1.426-6.138a20.156 20.156 0 0 1 3.782-3.286 16.408 16.408 0 0 1 4.34-2.17 14.833 14.833 0 0 1 4.836-.806c2.149 0 3.844.352 5.084 1.054 1.24.703 2.17 1.674 2.79 2.914.62 1.199 1.012 2.563 1.178 4.092.206 1.488.31 3.038.31 4.65v18.228h-7.192ZM131.034 57.128c-4.299 0-8.06-.93-11.284-2.79-3.224-1.901-5.725-4.484-7.502-7.75-1.736-3.306-2.604-7.047-2.604-11.222 0-3.141.516-6.034 1.55-8.68 1.074-2.686 2.562-5.001 4.464-6.944 1.942-1.942 4.216-3.451 6.82-4.526 2.604-1.074 5.456-1.612 8.556-1.612 4.836 0 8.804 1.013 11.904 3.038 3.1 2.026 5.58 4.816 7.44 8.37l-6.82 2.294c-1.736-2.521-3.638-4.278-5.704-5.27-2.026-.992-4.299-1.488-6.82-1.488-2.687 0-5.084.64-7.192 1.922-2.108 1.24-3.782 2.976-5.022 5.208-1.24 2.232-1.86 4.795-1.86 7.688 0 2.894.62 5.456 1.86 7.688 1.24 2.232 2.914 3.989 5.022 5.27 2.108 1.24 4.505 1.86 7.192 1.86 2.521 0 4.794-.496 6.82-1.488 2.066-.992 3.968-2.748 5.704-5.27l6.82 2.294c-1.86 3.555-4.34 6.345-7.44 8.37-3.1 2.026-7.068 3.038-11.904 3.038Z" />
14 <defs>
15 <linearGradient id="a" x1=".97" x2="72.586" y1=".446" y2="80.006"
16 gradientUnits="userSpaceOnUse">
17 <stop stop-color="#45B8FF" />
18 <stop offset="1" stop-color="#4B2A99" />
19 </linearGradient>
20 <linearGradient id="b" x1=".97" x2="72.586" y1=".446" y2="80.006"
21 gradientUnits="userSpaceOnUse">
22 <stop stop-color="#45B8FF" />
23 <stop offset="1" stop-color="#4B2A99" />
24 </linearGradient>
25 </defs>
26 </svg>
27</a>
1<footer class="w-full mt-24">
2 <div class="mx-auto w-full max-w-[1400px] px-4 xl:px-16">
3 <svg class="block w-full h-auto text-base-content opacity-8" viewBox="0 0 1280 308" aria-hidden="true"
4 focusable="false">
5 <path d="M50.2753 0H0V308.689H144.713V263.27H50.2753V0Z" fill="currentColor" />
6 <path
7 d="M322.209 130.973C315.796 120.684 306.688 112.602 294.883 106.718C283.081 100.84 271.201 97.8969 259.253 97.8969C243.798 97.8969 229.665 100.764 216.843 106.496C204.014 112.228 193.015 120.099 183.834 130.091C174.654 140.088 167.51 151.628 162.412 164.706C157.308 177.792 154.761 191.54 154.761 205.94C154.761 220.645 157.308 234.457 162.412 247.39C167.508 260.332 174.652 271.796 183.834 281.788C193.015 291.785 204.017 299.647 216.843 305.379C229.665 311.111 243.798 313.978 259.253 313.978C271.201 313.978 283.081 311.038 294.883 305.159C306.688 299.282 315.796 291.197 322.209 280.904V308.685H369.865V103.186H322.209V130.973ZM317.837 231.076C314.922 239.016 310.841 245.925 305.598 251.804C300.35 257.687 294.009 262.389 286.579 265.917C279.146 269.445 270.905 271.208 261.875 271.208C252.837 271.208 244.676 269.445 237.391 265.917C230.104 262.389 223.839 257.687 218.593 251.804C213.345 245.925 209.335 239.016 206.57 231.076C203.794 223.138 202.417 214.759 202.417 205.942C202.417 197.12 203.794 188.742 206.57 180.804C209.335 172.866 213.345 165.961 218.593 160.078C223.839 154.201 230.102 149.493 237.391 145.965C244.676 142.437 252.837 140.674 261.875 140.674C270.908 140.674 279.146 142.437 286.579 145.965C294.009 149.493 300.35 154.199 305.598 160.078C310.844 165.961 314.922 172.866 317.837 180.804C320.748 188.742 322.209 197.12 322.209 205.942C322.209 214.759 320.748 223.138 317.837 231.076Z"
8 fill="currentColor" />
9 <path
10 d="M709.568 130.973C703.155 120.684 694.047 112.602 682.242 106.718C670.44 100.84 658.56 97.8969 646.612 97.8969C631.157 97.8969 617.024 100.764 604.202 106.496C591.373 112.228 580.374 120.099 571.193 130.091C562.013 140.088 554.869 151.628 549.771 164.706C544.666 177.792 542.12 191.54 542.12 205.94C542.12 220.645 544.666 234.457 549.771 247.39C554.867 260.332 562.01 271.796 571.193 281.788C580.374 291.785 591.375 299.647 604.202 305.379C617.024 311.111 631.157 313.978 646.612 313.978C658.56 313.978 670.44 311.038 682.242 305.159C694.047 299.282 703.155 291.197 709.568 280.904V308.685H757.224V103.186H709.568V130.973ZM705.198 231.076C702.283 239.016 698.202 245.925 692.959 251.804C687.711 257.687 681.37 262.389 673.94 265.917C666.507 269.445 658.266 271.208 649.236 271.208C640.198 271.208 632.037 269.445 624.752 265.917C617.465 262.389 611.2 257.687 605.954 251.804C600.706 245.925 596.696 239.016 593.931 231.076C591.155 223.138 589.778 214.759 589.778 205.942C589.778 197.12 591.155 188.742 593.931 180.804C596.696 172.866 600.706 165.961 605.954 160.078C611.2 154.201 617.463 149.493 624.752 145.965C632.037 142.437 640.198 140.674 649.236 140.674C658.269 140.674 666.507 142.437 673.94 145.965C681.37 149.493 687.711 154.199 692.959 160.078C698.205 165.961 702.283 172.866 705.198 180.804C708.109 188.742 709.57 197.12 709.57 205.942C709.568 214.759 708.107 223.138 705.198 231.076Z"
11 fill="currentColor" />
12 <path d="M1280 1.12315e-05H1232.35V308.689H1280V1.12315e-05Z" fill="currentColor" />
13 <path d="M407.466 308.689H455.117V150.486H536.876V103.192H407.466V308.689Z" fill="currentColor" />
14 <path
15 d="M948.281 103.192L888.386 260.557L828.489 103.192H780.224L858.441 308.689H918.331L996.546 103.192H948.281Z"
16 fill="currentColor" />
17 <path
18 d="M1100.48 97.908C1042.13 97.908 995.937 146.279 995.937 205.944C995.937 271.9 1040.64 313.98 1106.59 313.98C1143.5 313.98 1167.06 299.745 1195.85 268.746L1163.66 243.621C1163.64 243.646 1139.36 275.802 1103.1 275.802C1060.96 275.802 1043.22 241.533 1043.22 223.803H1201.32C1209.62 155.913 1165.37 97.908 1100.48 97.908ZM1043.35 188.085C1043.71 184.13 1049.2 136.086 1100.1 136.086C1151.01 136.086 1157.19 184.123 1157.55 188.085H1043.35Z"
19 fill="currentColor" />
20 </svg>
21 </p>
22 </div>
23</footer>

Next, to better match the new design, we can remove the shadow styles from our card elements in our resources/views/components/chirp.blade.php and the resources/views/chirps/edit.blade.php files.

Lastly, we can modify the h1's of the Register and Login view files to use text-xl mt-1 instead of the previous text-3xl.

Looking good!

Laravel Auth Starter Kits

Building auth manually taught you how it works, but in real projects, you'd often use:

  • Laravel Starter Kits - Ready-to-use authentication scaffolding with complete user interfaces

  • Laravel Fortify - Headless authentication backend for building authentication from scratch with your own UI

  • Laravel Socialite - OAuth authentication with providers like GitHub, Google, Facebook, Twitter, and more

These provide production-ready auth with email verification, password resets, two-factor authentication, and more!

Now, it might seem simple, but give yourself a round of applause—we just created your first Laravel application with complete authentication! Next up: Let's wrap up and talk about what's next in your Laravel journey!

00:00
I've created at least two accounts now, mostly because I don't
00:03
have a sign in page just yet.
00:05
So let's fix that.
00:06
We're almost done.
00:07
Let's keep going.
00:08
Let's make it so we don't see this.
00:10
4 0 4. Not found error again, at least on this login page.
00:14
In our resources views, we have the author directory for register.
00:18
I'm going to create a new file for login dot blade dot php.
00:23
And we can of course, start with the register page itself and
00:27
just make changes like that.
00:29
Or you can just copy what is in the lesson on laravel.com/learn.
00:33
And paste that in and yeah, there's nothing too crazy here.
00:37
It's exactly what the register page is, just with fewer forms and directing
00:41
to a specific method of login.
00:44
You can start to tell how a lot of this is repeatable in the sense of once you
00:48
get the hang of what a form looks like, especially if it's a form that has similar
00:54
fields, the next time you implement that form, it's gonna be a lot easier,
00:58
and Laravel just makes it easier too.
00:59
We created an auth logout class already.
01:02
We just need our auth login, and we can do that all in one command.
01:06
Make controller, and we're gonna call this auth slash login and we're
01:10
going to pass the INVOCATIVE method.
01:13
So if we go to our login auth controller.
01:17
Yes.
01:17
This is our invocative single class controller.
01:20
This is going to be one of the few controllers that's a little
01:23
bit different, but only slightly.
01:26
We're going to pull in that auth facade
01:29
so what this off facade is doing is we're using the Attempt Helper with this facade.
01:33
We're taking the credentials that we've been given.
01:35
This email and password.
01:37
We are attaching the Remember checkbox.
01:40
If they gave that and we're requesting a new session, we're
01:43
regenerating that new session.
01:46
You can see here this attempt helper automatically has that remember variable.
01:50
If it's false, it's false by default.
01:53
Otherwise, if we pass that in, then it's true.
01:55
This is doing all the heavy lifting for us,
01:59
and then we'll return back.
02:01
Otherwise, if the oath attempt fails for whatever reason, then
02:06
we're going to return back.
02:08
This is a neat little return helper to say, Hey, we're returning to the previous
02:12
page, regardless of where that was, because maybe the user tried to chirp
02:16
and then we're gonna go to the homepage, but maybe they tried to log in, so we
02:21
need to return back to that login page.
02:24
But for the most part, uh, we are just returning them back to the login
02:28
page because we do have those errors.
02:30
Now you might see how this could be as simple as this, or it could get a little
02:34
bit more complicated if we wanted it to.
02:36
We could have additional, uh, validation methods to say, okay, maybe we need
02:41
to make sure that they have, I don't know, they are logging in in the proper.
02:46
Place they, uh, are not trying to log in multiple times, and you can
02:51
do all that with Laravel and there's a lot of great helpers available.
02:55
But this is just the absolute basics.
02:57
We're logging them back in again using the auth facade to attempt to log them back in
03:03
with the credentials that they've given.
03:05
When you do need to step into a little bit more trickier applications when
03:09
you want to log someone in, but want to make sure they're only logged in once.
03:13
Of course, Laravel has those helpers available to you.
03:17
But that is also where some starter kits can be incredibly helpful to be
03:22
able to have that kind of out of the box, or at least have the starting
03:27
base, the foundation out of the box ready to go so that you can add onto it
03:31
with the helpful lable documentation.
03:34
And seeing that we're requesting this session to be regenerated, it did remind
03:38
me that I think we want to invalidate the session as well in the logout controller.
03:44
So instead of just logging out this user.
03:47
And we can actually simplify this with auth logout because we already
03:52
know the authenticated user.
03:54
Then we can say, we need the request session and we're going to invalidate
03:59
that, as well as the request session.
04:03
Let's go ahead and regenerate a new token, so that way the user who is then
04:08
not logged in still has a fresh token.
04:12
Let's go ahead and add this login route to our web
04:15
login parameter route post of login.
04:22
And we're going to use that login class.
04:29
We're going to use the middleware of auth and name this,
04:36
the log in.
04:41
Why don't we name the log out as well, even though I don't think we're using
04:44
it anywhere, but it's also helpful just in case we wanted to use it.
04:48
Again, similar to how we used it in the layout for this register
04:53
route, we could go ahead and do this
04:56
for the action of logout if we wanted to.
04:59
Oops.
05:00
I accidentally put the GI instead of the logout method.
05:04
And I see what I did.
05:06
I don't necessarily want this to be a named route because we are
05:10
using the named route for logout.
05:11
What I do need is the route view to be able to pass in the
05:15
s login page to the login class.
05:20
This login blade view, I should say.
05:23
And this is where we can have the middleware of guest.
05:26
And we do want this removed from here as well, or at least the middleware of
05:30
guest for this particular login route.
05:33
And this is what we can name the login route.
05:37
And there we go.
05:38
That looks a lot better.
05:39
So now we have this login page.
05:42
If I was to log in to my previous email that I created,
05:45
we are welcome to back and this logout button should work
05:49
even with the name drought.
05:52
And there we go.
05:53
while I haven't done a good job of showing you how often you should commit
05:56
and push to your GitHub repository, the neat thing is because we set this
06:01
all up within Laravel Cloud, whereas soon as you push any changes, it's
06:05
automatically going to be deployed.
06:07
Uh, we can do that as.
06:08
Often, or as little as we might want, although it's better
06:12
to do it as often as you can.
06:14
But let's go ahead and do that right now so we can add everything that we've done.
06:18
We can commit this as the Let's do it, and we will go ahead and push this.
06:24
And so what we'll see now is the opportunity to then view our full
06:29
application live, ready to go, and with everything that we've built,
06:34
but now hosted on Laravel Cloud.
06:37
So if we go back to cloud.laravel.com and sign in with
06:41
your account that you created.
06:42
Our chira application has now been deployed.
06:45
We can click on this and yes, we have our application live now on Laravel Cloud
06:51
in production, ready for all to see.
06:53
It looks like it's waking up from hibernation right now.
06:56
And yeah, we could be the first to chirp here.
06:58
So why don't we go ahead and do that.
07:00
We'll sign up.
07:01
I will say first Laravel application created, go and chirp that.
07:09
Fantastic.
07:09
It looks great, and hopefully yours looks just as good too.
07:13
Just to recap, if we try to create a chirp, because we have this middleware
07:17
attached to any of the post methods for creating a chirp, editing a chirp,
07:21
or anything like that, of course we can't see editing or deleting a chirp.
07:26
I cannot click on this.
07:27
It takes me directly to the login page.
07:30
Of course, we can register a new account if we would like, or I can log back in.
07:38
We are using the default authentication, the auth helper, and facade that Laravel
07:43
gives, which includes remembering us and remembering that session.
07:47
We have the ability to add new chirps.
07:51
We can see that this chirp is attached to our particular user.
07:54
If I was to add another one,
07:56
it still uses my user and my little avatar
07:59
logging out and signing in with a different email.
08:02
It means that I cannot edit or delete these chirps that another
08:05
user created, but this is my chirp
08:12
and I can definitely edit this.
08:18
Full crud functionality of being able to create, read, update, and then delete
08:26
all within a neat little chirp application.
08:30
Now this is looking pretty good for someone like myself who is not a designer,
08:33
but thankfully I have some incredibly talented designers on the Laravel
08:37
team and with the power of modern CSS, like Tailwind CSS, which comes outta
08:42
the box with Laravel starter kits.