If you’re anything like me; you’ve spent a great deal of time building password-protected systems. I used to dread the point at which I had to bolt on the authentication system to a CMS or shopping cart. That was until I learned how easy it was with Laravel 4.
Laravel 4 uses Composer to manage its dependencies. You can install Composer by following the instructions at http://getcomposer.org/doc/00-intro.md#installation-nix.
Once you have Composer working, make a new directory or navigation to an existing directory and install Laravel with the following command:
1
❯ composer create-project laravel/laravel . 2
3
Installing laravel/laravel (
v4.1.27)
4
...
If you chose not to install Composer globally (though you really should), then the command you use should resemble the following:
1
❯ php composer.phar create-project laravel/laravel . 2
3
Installing laravel/laravel (
v4.1.27)
4
...
Both of these commands will start the process of installing Laravel 4. There are many dependencies to be sourced and downloaded; so this process may take some time to finish.
One of the best ways to manage users and authentication is by storing them in a database. The default Laravel 4 authentication components assume you will be using some form of database storage, and they provide two drivers with which these database users can be retrieved and authenticated.
To use either of the provided drivers, we first need a valid connection to the database. Set it up by configuring and of the sections in the app/config/database.php
file. Here’s an example of the SQLite database I use for testing:
1
<?
php
2
3
return
[
4
"fetch"
=>
PDO
::
FETCH_CLASS
,
5
"default"
=>
"sqlite"
,
6
"connections"
=>
[
7
"sqlite"
=>
[
8
"driver"
=>
"sqlite"
,
9
"database"
=>
__DIR__
.
"/../database/production.sqlite"
10
]
11
],
12
"migrations"
=>
"migration"
13
];
The first driver which Laravel 4 provides is a called database. As the name suggests; this driver queries the database directly, in order to determine whether users matching provided credentials exist, and whether the appropriate authentication credentials have been provided.
If this is the driver you want to use; you will need the following database table in the database you have already configured:
1
CREATE
TABLE
user
(
2
id
integer
PRIMARY
KEY
NOT
null
,
3
username
varchar
NOT
null
,
4
password
varchar
NOT
null
,
5
email
varchar
NOT
null
,
6
remember_token
varchar
NOT
null
,
7
created_at
datetime
NOT
null
,
8
updated_at
datetime
NOT
null
9
);
The second driver which Laravel provides is called eloquent. Eloquent is also the name of the ORM which Laravel provides, for abstracting model data. It is similar in that it will ultimately query a database to determine whether a user is authentic, but the interface which it uses to make that determination is quite different from direct database queries.
If you’re using Laravel to build medium-to-large applications, then you stand a good chance of using Eloquent models to represent database objects. It is with this in mind that I will spend some time elaborating on the involvement of Eloquent models in the authentication process.
Since we’re using Eloquent to manage how our application communicates with the database; we may as well use Laravel’s database table manipulation tools.
To get started, navigate to the root of your project and type the following command:
1
❯ php artisan migrate:make --table=
"user"
create_user_table 2
3
Created Migration: 2014_05_04_193719_create_user_table 4
Generating optimized class loader 5
Compiling common classes
This will generate the scaffolding for the users table, which should resemble the following:
1
<?
php
2
3
use
Illuminate\Database\Schema\Blueprint
;
4
use
Illuminate\Database\Migrations\Migration
;
5
6
class
CreateUserTable
7
extends
Migration
8
{
9
public
function
up
()
10
{
11
Schema
::
table
(
"user"
,
function
(
Blueprint
$table
)
{
12
13
});
14
}
15
16
public
function
down
()
17
{
18
Schema
::
table
(
"user"
,
function
(
Blueprint
$table
)
{
19
20
});
21
}
22
}
The file naming scheme may seem odd, but it is for a good reason. Migration systems are designed to be able to run on any server, and the order in which they must run is fixed. All of this is to allow changes to the database to be version-controlled.
The migration is created with just the most basic scaffolding, which means we need to add the fields for the users table:
1
<?
php
2
3
use
Illuminate\Database\Schema\Blueprint
;
4
use
Illuminate\Database\Migrations\Migration
;
5
6
class
CreateUserTable
7
extends
Migration
8
{
9
public
function
up
()
10
{
11
Schema
::
create
(
"user"
,
function
(
Blueprint
$table
)
{
12
$table
->
increments
(
"id"
);
13
$table
->
string
(
"username"
);
14
$table
->
string
(
"password"
);
15
$table
->
string
(
"email"
);
16
$table
->
string
(
"remember_token"
)
->
nullable
();
17
$table
->
timestamps
();
18
});
19
}
20
21
public
function
down
()
22
{
23
Schema
::
dropIfExists
(
"user"
);
24
}
25
}
Here; we’ve added fields for id
, username
, password
, created_at
and updated_at
. We’ve also added the drop method, which will be run if the migrations are reversed; which will drop the users table if it exists.
This migration will work, even if you only want to use the database driver, but it’s usually part of a larger setup; including models and seeders.
Laravel 4 provides a User
model, with all the interface methods it requires. I have modified it slightly, but the basics are still there…
1
<?
php
2
3
use
Illuminate\Auth\UserInterface
;
4
use
Illuminate\Auth\Reminders\RemindableInterface
;
5
6
class
User
7
extends
Eloquent
8
implements
UserInterface
,
RemindableInterface
9
{
10
protected
$table
=
"user"
;
11
protected
$hidden
=
[
"password"
];
12
13
public
function
getAuthIdentifier
()
14
{
15
return
$this
->
getKey
();
16
}
17
18
public
function
getAuthPassword
()
19
{
20
return
$this
->
password
;
21
}
22
23
public
function
getRememberToken
()
24
{
25
return
$this
->
remember_token
;
26
}
27
28
public
function
setRememberToken
(
$value
)
29
{
30
$this
->
remember_token
=
$value
;
31
}
32
33
public
function
getRememberTokenName
()
34
{
35
return
"remember_token"
;
36
}
37
38
public
function
getReminderEmail
()
39
{
40
return
$this
->
email
;
41
}
42
}
The User
model extends Eloquent
and implements two interfaces which ensure the model is valid for authentication and reminder operations. We’ll look at the interfaces later, but its important to note the methods these interfaces require.
Laravel allows the user of either email address or username with which to identify the user, but it is a different field from that which the getAuthIdentifier()
returns. The UserInterface
interface does specify the password field name, but this can be changed by overriding the getAuthPassword()
method.
The reminder token methods are used to create and validate account-specific security tokens. The finer details are best left to another lesson…
The getReminderEmail()
method returns an email address with which to contact the user with a password reset email, should this be required.
You are otherwise free to specify any model customisation, without fear it will break the built-in authentication components.
Laravel 4 also includes seeding system, which can be used to add records to your database after initial migration. To add the initial users to my project, I have the following seeder class:
1
<?
php
2
3
class
UserSeeder
4
extends
DatabaseSeeder
5
{
6
public
function
run
()
7
{
8
$users
=
[
9
[
10
"username"
=>
"christopher.pitt"
,
11
"password"
=>
Hash
::
make
(
"7h3¡MOST!53cu23"
),
12
"email"
=>
"[email protected]"
13
]
14
];
15
16
foreach
(
$users
as
$user
)
{
17
User
::
create
(
$user
);
18
}
19
}
20
}
Running this will add my user account to the database, but in order to run this; we need to add it to the main DatabaseSeeder
class:
1
<?
php
2
3
class
DatabaseSeeder
4
extends
Seeder
5
{
6
public
function
run
()
7
{
8
Eloquent
::
unguard
();
9
$this
->
call
(
"UserSeeder"
);
10
}
11
}
The DatabaseSeeder
class will seed the users table with my account when invoked. If you’ve already set up your migration and model, and provided valid database connection details, then the following commands should get everything up and running:
1
❯ composer dump-autoload 2
3
Generating autoload files 4
5
❯ php artisan migrate 6
7
Migrated: ****_**_**_******_create_user_table 8
9
❯ php artisan db:seed 10
11
Seeded: UserSeeder
The first command makes sure all the new classes we’ve created are picked up by the class autoloader. The second creates the database tables specified for the migration. The third seeds the user data into the users table.
The configuration options for the authentication components are sparse, but they do allow for some customisation.
1
<?
php
2
3
return
[
4
"driver"
=>
"eloquent"
,
5
"model"
=>
"User"
,
6
"reminder"
=>
[
7
"email"
=>
"email/request"
,
8
"table"
=>
"token"
,
9
"expire"
=>
60
10
]
11
];
All of these settings are important, and most are self-explanatory. The view used to compose the request email is specified with the email
key and the time in which the reset token will expire is specified by the expire
key.
To allow authentic users to use our application, we’re going to build a login page; where users can enter their login details. If their details are valid, they will be redirected to their profile page.
Before we create any of the pages for out application; it would be wise to abstract away all of our layout markup and styling. To this end; we will create a layout view with various includes, using the Blade template engine.
First off, we need to create the layout view:
1
<!DOCTYPE html>
2
<html
lang=
"en"
>
3
<head>
4
<meta
charset=
"UTF-8"
/>
5
<link
rel=
"stylesheet"
href=
"/css/layout.css"
/>
6
<title>
Tutorial</title>
7
</head>
8
<body>
9
@include("header") 10
<div
class=
"content"
>
11
<div
class=
"container"
>
12
@yield("content") 13
</div>
14
</div>
15
@include("footer") 16
</body>
17
</html>
The layout view is mostly standard HTML, with two Blade-specific tags in it. The @include
tags tell Laravel to include the views (named in those strings; as header
and footer
) from the views directory.
Notice how we’ve omitted the .blade.php
extension? Laravel automatically adds this on for us. It also binds the data provided to the layout view to both includes.
The second Blade tag is @yield
. This tag accepts a section name, and outputs the data stored in that section. The views in our application will extend this layout view; while specifying their own content
sections so that their markup is embedded in the markup of the layout. You’ll see exactly how sections are defined shortly.
1
@section("header") 2
<div
class=
"header"
>
3
<div
class=
"container"
>
4
<h1>
Tutorial</h1>
5
</div>
6
</div>
7
@show
The header include file contains two blade tags which, together, instruct Blade to store the markup in the named section, and render it in the template.
1
@section("footer") 2
<div
class=
"footer"
>
3
<div
class=
"container"
>
4
Powered by <a
href=
"http://laravel.com"
>
Laravel</a>
5
</div>
6
</div>
7
@show
Similarly, the footer include wraps its markup in a named section and immediately renders it in the template.
You may be wondering why we would need to wrap the markup, in these include files, in sections. We are rendering them immediately, after all. Doing this allows us to alter their contents. We will see this in action soon.
1
body
{
2
margin
:
0
;
3
padding
:
0
0
50px
0
;
4
font-family
:
"Helvetica"
,
"Arial"
;
5
font-size
:
14px
;
6
line-height
:
18px
;
7
cursor
:
default
;
8
}
9
10
a
{
11
color
:
#ef7c61
;
12
}
13
14
.container
{
15
width
:
960px
;
16
position
:
relative
;
17
margin
:
0
auto
;
18
}
19
20
.header
,
.footer
{
21
background
:
#000
;
22
line-height
:
50px
;
23
height
:
50px
;
24
width
:
100
%
;
25
color
:
#fff
;
26
}
27
28
.header
h1
,
.header
a
{
29
display
:
inline
-
block
;
30
}
31
32
.header
h1
{
33
margin
:
0
;
34
font-weight
:
normal
;
35
}
36
37
.footer
{
38
position
:
absolute
;
39
bottom
:
0
;
40
}
41
42
.content
{
43
padding
:
25px
0
;
44
}
45
46
label
,
input
{
47
clear
:
both
;
48
float
:
left
;
49
margin
:
5px
0
;
50
}
We finish by adding some basic styles; which we linked to in the head
element. These alter the default fonts and layout. Your application would still work without them, but it would just look a little messy.
The login view is essentially a form; in which users enter their credentials.
1
@extends("layout") 2
@section("content") 3
{{ Form::open() }} 4
{{ Form::label("username", "Username") }} 5
{{ Form::text("username") }} 6
{{ Form::label("password", "Password") }} 7
{{ Form::password("password") }} 8
{{ Form::submit("login") }} 9
{{ Form::close() }} 10
@stop
The @extends
tag tells Laravel that this view extends the layout view. The @section
tag then tells it what markup to include in the content section. These tags will form the basis for all the views (other than layout) we will be creating.
We then use {{
and }}
to tell Laravel we want the contained code to be rendered, as if we were using the echo
statement. We open the form with the Form::open()
method; providing a route for the form to post to, and optional parameters in the second argument.
We then define two labels and three inputs. The labels accept a name argument, followed by a text argument. The text input accepts a name argument, a default value argument and and optional parameters. The password input accepts a name argument and optional parameters. Lastly, the submit input accepts a name argument and a text argument (similar to labels).
We close out the form with a call to Form::close()
.
Our login view is now complete, but basically useless without the server-side code to accept the input and return a result. Let’s get that sorted!
The login action is what glues the authentication logic to the views we have created. If you have been following along, you might have wondered when we were going to try any of this stuff out in a browser. Up to this point; there was nothing telling our application to load that view.
To begin with; we need to add a route for the login action:
1
<?
php
2
3
Route
::
any
(
"/"
,
[
4
"as"
=>
"user/login"
,
5
"uses"
=>
"UserController@login"
6
]);
The routes file displays a holding page for a new Laravel 4 application, by rendering a view directly. We’ve just changed that to use a controller and action.
We specify a name for the route with the as
key, and give it a destination with the uses
key. This will match all calls to the default /
route, and even has a name which we can use to refer back to this route easily.
Next up, we need to create the controller:
1
<?
php
2
3
class
UserController
4
extends
Controller
5
{
6
public
function
login
()
7
{
8
return
View
::
make
(
"user/login"
);
9
}
10
}
We define the UserController
, which extends the Controller
class. We’ve also created the login()
method that we specified in the routes file. All this currently does is render the login view to the browser, but it’s enough for us to be able to see our progress!
Unless you’ve got a personal web server set up, you’ll probably want to use the built-in web server that Laravel provides. Technically it’s just bootstrapping the framework on top of the personal web server that comes bundled with PHP 5.3, but we still need to run the following command to get it working:
1
❯ php artisan serve 2
3
Laravel development server started on http://localhost:8000
When you open your browser at http://localhost:8000, you should see the login page. If it’s not there, you’ve probably overlooked something leading up to this point.
Right, so we’ve got the form and now we need to tie it into the database so we can authenticate users correctly.
1
<?
php
2
3
class
UserController
4
extends
Controller
5
{
6
public
function
login
()
7
{
8
if
(
$this
->
isPostRequest
())
{
9
$validator
=
$this
->
getLoginValidator
();
10
11
if
(
$validator
->
passes
())
{
12
echo
"Validation passed!"
;
13
}
else
{
14
echo
"Validation failed!"
;
15
}
16
}
17
18
return
View
::
make
(
"user/login"
);
19
}
20
21
protected
function
isPostRequest
()
22
{
23
return
Input
::
server
(
"REQUEST_METHOD"
)
==
"POST"
;
24
}
25
26
protected
function
getLoginValidator
()
27
{
28
return
Validator
::
make
(
Input
::
all
(),
[
29
"username"
=>
"required"
,
30
"password"
=>
"required"
31
]);
32
}
33
}
Our UserController
class has changed somewhat. Firstly, we need to act on data that is posted to the login()
method; and to do that we check the server property REQUEST_METHOD
. If this value is POST
we can assume that the form has been posted to this action, and we proceed to the validation phase.
Laravel 4 provides a great validation system, and one of the ways to use it is by calling the Validator::make()
method. The first argument is an array of data to validate, and the second argument is an array of rules.
We have only specified that the username and password fields are required, but there are many other validation rules (some of which we will use in a while). The Validator
class also has a passes()
method, which we use to tell whether the posted form data is valid.
If you post this form; it will now tell you whether the required fields were supplied or not, but there is a more elegant way to display this kind of message…
1
public
function
login
()
2
{
3
$data
=
[];
4
5
if
(
$this
->
isPostRequest
())
{
6
$validator
=
$this
->
getLoginValidator
();
7
8
if
(
$validator
->
passes
())
{
9
echo
"Validation passed!"
;
10
}
else
{
11
$data
[
"error"
]
=
"Username and/or password invalid."
;
12
}
13
}
14
15
return
View
::
make
(
"user/login"
,
$data
);
16
}
Instead of showing individual error messages for either username or password; we’re showing a single error message for both. Login forms are a little more secure that way!
To display this error message, we also need to change the login view:
1
@extends("layout") 2
@section("content") 3
{{ Form::open() }} 4
@if (isset($error)) 5
{{ $error }}<br
/>
6
@endif 7
{{ Form::label("username", "Username") }} 8
{{ Form::text("username") }} 9
{{ Form::label("password", "Password") }} 10
{{ Form::password("password") }} 11
{{ Form::submit("login") }} 12
{{ Form::close() }} 13
@stop
As you can probably see; we’ve added a check for the existence of the error message, and rendered it. If validation fails, you will now see the error message above the username field.
One of the common pitfalls of forms is how refreshing the page most often re-submits the form. We can overcome this with some Laravel magic. We’ll store the posted form data in the session, and redirect back to the login page!
1
public
function
login
()
2
{
3
if
(
$this
->
isPostRequest
())
{
4
$validator
=
$this
->
getLoginValidator
();
5
6
if
(
$validator
->
passes
())
{
7
echo
"Validation passed!"
;
8
}
else
{
9
return
Redirect
::
back
()
10
->
withInput
()
11
->
withErrors
(
$validator
);
12
}
13
}
14
15
return
View
::
make
(
"user/login"
);
16
}
Instead of assigning errors messages to the view, we redirect back to the same page, passing the posted input data and the validator errors. This also means we will need to change our view:
1
@
extends
(
"layout"
)
2
@
section
(
"content"
)
3
{{
Form
::
open
()
}}
4
{{
$errors
->
first
(
"password"
)
}}
<
br
/>
5
{{
Form
::
label
(
"username"
,
"Username"
)
}}
6
{{
Form
::
text
(
"username"
,
Input
::
old
(
"username"
))
}}
7
{{
Form
::
label
(
"password"
,
"Password"
)
}}
8
{{
Form
::
password
(
"password"
)
}}
9
{{
Form
::
submit
(
"login"
)
}}
10
{{
Form
::
close
()
}}
11
@
stop
We can now hit that refresh button without it asking us for permission to re-submit data.
The last step in authentication is to check the provided form data against the database. Laravel handles this easily for us:
1
<?
php
2
3
class
UserController
4
extends
Controller
5
{
6
public
function
login
()
7
{
8
if
(
$this
->
isPostRequest
())
{
9
$validator
=
$this
->
getLoginValidator
();
10
11
if
(
$validator
->
passes
())
{
12
$credentials
=
$this
->
getLoginCredentials
();
13
14
if
(
Auth
::
attempt
(
$credentials
))
{
15
return
Redirect
::
route
(
"user/profile"
);
16
}
17
18
return
Redirect
::
back
()
->
withErrors
([
19
"password"
=>
[
"Credentials invalid."
]
20
]);
21
}
else
{
22
return
Redirect
::
back
()
23
->
withInput
()
24
->
withErrors
(
$validator
);
25
}
26
}
27
28
return
View
::
make
(
"user/login"
);
29
}
30
31
protected
function
isPostRequest
()
32
{
33
return
Input
::
server
(
"REQUEST_METHOD"
)
==
"POST"
;
34
}
35
36
protected
function
getLoginValidator
()
37
{
38
return
Validator
::
make
(
Input
::
all
(),
[
39
"username"
=>
"required"
,
40
"password"
=>
"required"
41
]);
42
}
43
44
protected
function
getLoginCredentials
()
45
{
46
return
[
47
"username"
=>
Input
::
get
(
"username"
),
48
"password"
=>
Input
::
get
(
"password"
)
49
];
50
}
51
}
We simply need to pass the posted form $credentials
to the Auth::attempt()
method and, if the user credentials are valid, the user will be logged in. If valid, we return a redirect to the user profile page.
Let’s set this page up:
1
@extends("layout") 2
@section("content") 3
<h2>
Hello {{ Auth::user()->username }}</h2>
4
<p>
Welcome to your sparse profile page.</p>
5
@stop
1
Route
::
any
(
"/profile"
,
[
2
"as"
=>
"user/profile"
,
3
"uses"
=>
"UserController@profile"
4
]);
1
public
function
profile
()
2
{
3
return
View
::
make
(
"user/profile"
);
4
}
Once the user is logged in, we can get access to their record by calling the Auth::user()
method. This returns an instance of the User model (if we’re using the Eloquent auth driver, or a plain old PHP object if we’re using the Database driver).
The password reset components built into Laravel are great! We’re going to set it up so users can reset their passwords just by providing their email address.
We need two views for users to be able to reset their passwords. We need a view for them to enter their email address so they can be sent a reset token, and we need a view for them to enter a new password for their account.
1
@extends("layout") 2
@section("content") 3
{{ Form::open() }} 4
{{ Form::label("email", "Email") }} 5
{{ Form::text("email", Input::old("email")) }} 6
{{ Form::submit("reset") }} 7
{{ Form::close() }} 8
@stop
This view is similar to the login view, except it has a single field for an email address.
1
@extends("layout") 2
@section("content") 3
{{ Form::open() }} 4
{{ $errors->first("token") }}<br
/>
5
{{ Form::label("email", "Email") }} 6
{{ Form::text("email", Input::get("email")) }} 7
{{ $errors->first("email") }}<br
/>
8
{{ Form::label("password", "Password") }} 9
{{ Form::password("password") }} 10
{{ $errors->first("password") }}<br
/>
11
{{ Form::label("password_confirmation", "Confirm") }} 12
{{ Form::password("password_confirmation") }} 13
{{ $errors->first("password_confirmation") }}<br
/>
14
{{ Form::submit("reset") }} 15
{{ Form::close() }} 16
@stop
Ok, you get it by now. There’s a form with some inputs and error messages. I’ve also slightly modified the password token request email, though it remains mostly the same as the default view provided by new Laravel 4 installations.
1
<!DOCTYPE html>
2
<html
lang=
"en"
>
3
<head>
4
<meta
charset=
"utf-8"
/>
5
</head>
6
<body>
7
<h1>
Password Reset</h1>
8
To reset your password, complete this form: 9
{{ URL::route("user/reset", compact("token")) }} 10
</body>
11
</html>
In order for the actions to be accessible; we need to add routes for them.
1
Route
::
any
(
"/request"
,
[
2
"as"
=>
"user/request"
,
3
"uses"
=>
"UserController@request"
4
]);
5
6
Route
::
any
(
"/reset/{token}"
,
[
7
"as"
=>
"user/reset"
,
8
"uses"
=>
"UserController@reset"
9
]);
Remember; the request route is for requesting a reset token, and the reset route is for resetting a password. We also need to generate the password reset tokens table; using artisan.
1
❯ php artisan auth:reminders-table 2
3
Migration created successfully! 4
Generating optimized class loader
This will generate a migration template for the reminder table:
1
<?
php
2
3
use
Illuminate\Database\Schema\Blueprint
;
4
use
Illuminate\Database\Migrations\Migration
;
5
6
class
CreateTokenTable
7
extends
Migration
8
{
9
public
function
up
()
10
{
11
Schema
::
create
(
"token"
,
function
(
Blueprint
$table
)
{
12
$table
->
string
(
"email"
)
->
index
();
13
$table
->
string
(
"token"
)
->
index
();
14
$table
->
timestamp
(
"created_at"
);
15
});
16
}
17
18
public
function
down
()
19
{
20
Schema
::
drop
(
"token"
);
21
}
22
}
With these in place, we can begin to add our password reset actions:
1
public
function
request
()
2
{
3
if
(
$this
->
isPostRequest
())
{
4
$response
=
$this
->
getPasswordRemindResponse
();
5
6
if
(
$this
->
isInvalidUser
(
$response
))
{
7
return
Redirect
::
back
()
8
->
withInput
()
9
->
with
(
"error"
,
Lang
::
get
(
$response
));
10
}
11
12
return
Redirect
::
back
()
13
->
with
(
"status"
,
Lang
::
get
(
$response
));
14
}
15
16
return
View
::
make
(
"user/request"
);
17
}
18
19
protected
function
getPasswordRemindResponse
()
20
{
21
return
Password
::
remind
(
Input
::
only
(
"email"
));
22
}
23
24
protected
function
isInvalidUser
(
$response
)
25
{
26
return
$response
===
Password
::
INVALID_USER
;
27
}
The main magic in this set of methods is the call to the Password::remind()
method. This method checks the database for a matching user. If one is found, an email is sent to that user, or else an error message is returned.
We should adjust the reset view to accommodate this error message:
1
@extends("layout") 2
@section("content") 3
{{ Form::open() }} 4
@if (Session::get("error")) 5
{{ Session::get("error") }}<br
/>
6
@endif 7
@if (Session::get("status")) 8
{{ Session::get("status") }}<br
/>
9
@endif 10
{{ Form::label("email", "Email") }} 11
{{ Form::text("email", Input::old("email")) }} 12
{{ Form::submit("reset") }} 13
{{ Form::close() }} 14
@stop
When navigating to this route, you should be presented with a form containing an email address field and a submit button. Completing it with an invalid email address should render an error message, while completing it with a valid email address should render a success message. That is, provided the email is sent…
1
public
function
reset
(
$token
)
2
{
3
if
(
$this
->
isPostRequest
())
{
4
$credentials
=
Input
::
only
(
5
"email"
,
6
"password"
,
7
"password_confirmation"
8
)
+
compact
(
"token"
);
9
10
$response
=
$this
->
resetPassword
(
$credentials
);
11
12
if
(
$response
===
Password
::
PASSWORD_RESET
)
{
13
return
Redirect
::
route
(
"user/profile"
);
14
}
15
16
return
Redirect
::
back
()
17
->
withInput
()
18
->
with
(
"error"
,
Lang
::
get
(
$response
));
19
}
20
21
return
View
::
make
(
"user/reset"
,
compact
(
"token"
));
22
}
23
24
protected
function
resetPassword
(
$credentials
)
25
{
26
return
Password
::
reset
(
$credentials
,
function
(
$user
,
$pass
)
{
27
$user
->
password
=
Hash
::
make
(
$pass
);
28
$user
->
save
();
29
});
30
}
Similar to the Password::remind()
method, the Password::reset()
method accepts an array of user-specific data and does a bunch of magic. That magic includes checking for a valid user account and changing the associated password, or returning an error message.
We need to create the reset view, for this:
1
@
extends
(
"layout"
)
2
@
section
(
"content"
)
3
{{
Form
::
open
()
}}
4
@
if
(
Session
::
get
(
"error"
))
5
{{
Session
::
get
(
"error"
)
}}
<
br
/>
6
@
endif
7
{{
Form
::
label
(
"email"
,
"Email"
)
}}
8
{{
Form
::
text
(
"email"
,
Input
::
old
(
"email"
))
}}
9
{{
$errors
->
first
(
"email"
)
}}
<
br
/>
10
{{
Form
::
label
(
"password"
,
"Password"
)
}}
11
{{
Form
::
password
(
"password"
)
}}
12
{{
$errors
->
first
(
"password"
)
}}
<
br
/>
13
{{
Form
::
label
(
"password_confirmation"
,
"Confirm"
)
}}
14
{{
Form
::
password
(
"password_confirmation"
)
}}
15
{{
$errors
->
first
(
"password_confirmation"
)
}}
<
br
/>
16
{{
Form
::
submit
(
"reset"
)
}}
17
{{
Form
::
close
()
}}
18
@
stop
Laravel includes a filters file, in which we can define filters to run for single or even groups of routes. The most basic one we’re going to look at is the auth filter:
1
<?
php
2
3
Route
::
filter
(
"auth"
,
function
()
{
4
if
(
Auth
::
guest
())
{
5
return
Redirect
::
route
(
"user/login"
);
6
}
7
});
This filter, when applied to routes, will check to see whether the user is currently logged in or not. If they are not logged in, they will be directed to the login route.
In order to apply this filter, we need to modify our routes file:
1
<?
php
2
3
Route
::
any
(
"/"
,
[
4
"as"
=>
"user/login"
,
5
"uses"
=>
"UserController@login"
6
]);
7
8
Route
::
group
([
"before"
=>
"auth"
],
function
()
{
9
10
Route
::
any
(
"/profile"
,
[
11
"as"
=>
"user/profile"
,
12
"uses"
=>
"UserController@profile"
13
]);
14
15
});
16
17
Route
::
any
(
"/request"
,
[
18
"as"
=>
"user/request"
,
19
"uses"
=>
"UserController@request"
20
]);
21
22
Route
::
any
(
"/reset/{token}"
,
[
23
"as"
=>
"user/reset"
,
24
"uses"
=>
"UserController@reset"
25
]);
You’ll notice that we’ve wrapped the profile route in a callback, which executes the auth filter. This means the profile route will only be accessible to authenticated users.
To test these new security measures out, and to round off the tutorial, we need to create a logout()
method and add links to the header so that users can log out.
1
public
function
logout
()
2
{
3
Auth
::
logout
();
4
5
return
Redirect
::
route
(
"user/login"
);
6
}
Logging a user out is as simple as calling the Auth::logout()
method. It doesn’t clear the session, mind you, but it will make sure our auth
filter kicks in…
This is what the new header include looks like:
1
@section("header") 2
<div
class=
"header"
>
3
<div
class=
"container"
>
4
<h1>
Tutorial</h1>
5
@if (Auth::check()) 6
<a
href=
"{{ URL::route("
user
/
logout
")
}}"
>
7
logout 8
</a>
| 9
<a
href=
"{{ URL::route("
user
/
profile
")
}}"
>
10
profile 11
</a>
12
@else 13
<a
href=
"{{ URL::route("
user
/
login
")
}}"
>
14
login 15
</a>
16
@endif 17
</div>
18
</div>
19
@show
Lastly, we should add a route to the logout action:
1
Route
::
any
(
"/logout"
,
[
2
"as"
=>
"user/logout"
,
3
"uses"
=>
"UserController@logout"
4
]);