Skip to content

Commit fac09fc

Browse files
authored
Merge pull request #10 from rickwest/prevent_password_bypass
Prevent password reset bypassing email confirmation.
2 parents baad41c + 44230f6 commit fac09fc

File tree

7 files changed

+118
-12
lines changed

7 files changed

+118
-12
lines changed

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ And run the migrations:
2929
php artisan migrate
3030
```
3131

32-
### Configuring the login and register controllers
33-
In order to make use of the email verification, replace the `AuthenticatesUsers` and `RegistersUsers` traits that
32+
### Configuring the login, register and forgot password controllers
33+
In order to make use of the email verification, replace the `AuthenticatesUsers`, `RegistersUsers` and the `SendsPasswordResetEmails` traits that
3434
come with Laravel, with the ones provided by this package.
3535

36-
These traits can be found in these two files:
36+
These traits can be found in these three files:
3737

3838
- `App\Http\Controllers\Auth\LoginController`
3939
- `App\Http\Controllers\Auth\RegisterController`
40+
- `App\Http\Controllers\Auth\SendsPasswordResetEmails`
4041

4142
### Add the confirmation and resend routes
4243

@@ -58,6 +59,9 @@ To show them to your users, add this to your `login.blade.php`:
5859
{!! session('confirmation') !!}
5960
</div>
6061
@endif
62+
```
63+
and this to both your `login.blade.php` and `email.blade.php`
64+
```blade
6165
@if ($errors->has('confirmation') > 0 )
6266
<div class="alert alert-danger" role="alert">
6367
{!! $errors->first('confirmation') !!}

resources/lang/en/confirmation.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
'confirmation_button' => 'Verify now',
88

99
'not_confirmed' => 'The given email address has not been confirmed. <a href=":resend_link">Resend confirmation link.</a>',
10+
'not_confirmed_reset_password' => 'The given email address has not been confirmed. To reset the password you must first confirm the email address.<a href=":resend_link">Resend confirmation link.</a>',
1011
'confirmation_successful' => 'You successfully confirmed your email address. Please log in.',
1112
'confirmation_info' => 'Please confirm your email address.',
1213
'confirmation_resent' => 'We sent you another confirmation email. You should receive it shortly.',
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace BeyondCode\EmailConfirmation\Traits;
4+
5+
use Illuminate\Http\Request;
6+
use Illuminate\Support\Facades\Password;
7+
use Illuminate\Validation\ValidationException;
8+
9+
/**
10+
* Trait SendsPasswordResetEmails
11+
* @package BeyondCode\EmailConfirmation\Traits
12+
*/
13+
trait SendsPasswordResetEmails
14+
{
15+
use \Illuminate\Foundation\Auth\SendsPasswordResetEmails;
16+
17+
/**
18+
* Send a reset link to the given user.
19+
*
20+
* @param Request $request
21+
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
22+
* @throws ValidationException
23+
*/
24+
public function sendResetLinkEmail(Request $request)
25+
{
26+
$this->validateEmail($request);
27+
28+
$user = $this->broker()->getUser($request->only('email'));
29+
30+
// If the user hasn't confirmed their email address,
31+
// we will throw a validation exception for this error.
32+
// A user can not request a password reset link if they are not confirmed.
33+
if (is_null($user->confirmed_at)) {
34+
throw ValidationException::withMessages([
35+
'confirmation' => [
36+
__('confirmation::confirmation.not_confirmed_reset_password', [
37+
'resend_link' => route('auth.resend_confirmation')
38+
])
39+
]
40+
]);
41+
}
42+
43+
// We will send the password reset link to this user. Once we have attempted
44+
// to send the link, we will examine the response then see the message we
45+
// need to show to the user. Finally, we'll send out a proper response.
46+
$response = $this->broker()->sendResetLink(
47+
$request->only('email')
48+
);
49+
50+
return $response == Password::RESET_LINK_SENT
51+
? $this->sendResetLinkResponse($response)
52+
: $this->sendResetLinkFailedResponse($request, $response);
53+
}
54+
}

tests/ConfirmationTest.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace BeyondCode\EmailConfirmation\Tests;
44

5+
use Illuminate\Auth\Notifications\ResetPassword;
56
use Illuminate\Support\Facades\Notification;
67
use BeyondCode\EmailConfirmation\Tests\Models\User;
78
use BeyondCode\EmailConfirmation\Notifications\ConfirmEmail;
@@ -118,4 +119,24 @@ public function it_returns_404_for_invalid_codes()
118119

119120
$response->assertStatus(404);
120121
}
122+
123+
/** @test */
124+
public function it_does_not_allow_reset_password_request_for_unconfirmed_users()
125+
{
126+
Notification::fake();
127+
128+
$user = User::create([
129+
'email' => '[email protected]',
130+
'password' => bcrypt('test123'),
131+
'confirmed_at' => null,
132+
]);
133+
134+
$response = $this->post('/password/email', [
135+
'email' => '[email protected]',
136+
]);
137+
138+
$response->assertSessionHasErrors('confirmation');
139+
140+
Notification::assertNotSentTo($user, ResetPassword::class);
141+
}
121142
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace BeyondCode\EmailConfirmation\Tests\Controllers;
4+
5+
use BeyondCode\EmailConfirmation\Traits\SendsPasswordResetEmails;
6+
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
7+
use Illuminate\Foundation\Bus\DispatchesJobs;
8+
use Illuminate\Foundation\Validation\ValidatesRequests;
9+
use Illuminate\Routing\Controller;
10+
11+
class ForgotPasswordController extends Controller
12+
{
13+
/*
14+
|--------------------------------------------------------------------------
15+
| Password Reset Controller
16+
|--------------------------------------------------------------------------
17+
|
18+
| This controller is responsible for handling password reset emails and
19+
| includes a trait which assists in sending these notifications from
20+
| your application to your users. Feel free to explore this trait.
21+
|
22+
*/
23+
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, SendsPasswordResetEmails;
24+
}

tests/Controllers/LoginController.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111
class LoginController extends Controller
1212
{
1313
/*
14-
|--------------------------------------------------------------------------
15-
| Register Controller
16-
|--------------------------------------------------------------------------
17-
|
18-
| This controller handles the registration of new users as well as their
19-
| validation and creation. By default this controller uses a trait to
20-
| provide this functionality without requiring any additional code.
21-
|
22-
*/
14+
|--------------------------------------------------------------------------
15+
| Login Controller
16+
|--------------------------------------------------------------------------
17+
|
18+
| This controller handles authenticating users for the application and
19+
| redirecting them to your home screen. The controller uses a trait
20+
| to conveniently provide its functionality to your applications.
21+
|
22+
*/
2323
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, AuthenticatesUsers;
2424
}

tests/TestCase.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace BeyondCode\EmailConfirmation\Tests;
44

5+
use BeyondCode\EmailConfirmation\Tests\Controllers\ForgotPasswordController;
56
use Illuminate\Contracts\Http\Kernel;
67
use Illuminate\Session\Middleware\StartSession;
78
use Illuminate\Support\Facades\Route;
@@ -82,5 +83,6 @@ protected function setUpRoutes()
8283
Route::name('login')->post('/login', LoginController::class.'@login');
8384
Route::name('auth.resend_confirmation')->get('/resend', RegisterController::class.'@resendConfirmation');
8485
Route::name('auth.confirm')->get('/register/confirm/{confirmation_code}', RegisterController::class.'@confirm');
86+
Route::name('password.email')->post('/password/email', ForgotPasswordController::class.'@sendResetLinkEmail');
8587
}
8688
}

0 commit comments

Comments
 (0)