Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add email confirmation #5301

Merged
merged 32 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a00bcef
add email verification
pxpm Aug 28, 2023
977276b
Apply fixes from StyleCI
StyleCIBot Aug 28, 2023
0c65162
add email confirmation
pxpm Aug 29, 2023
2f86b6e
Apply fixes from StyleCI
StyleCIBot Aug 29, 2023
5e83e25
Update src/app/Http/Controllers/Auth/VerifyEmailController.php
pxpm Aug 29, 2023
84969da
Update src/config/backpack/base.php
pxpm Aug 29, 2023
99de011
Update src/resources/lang/en/base.php
pxpm Aug 29, 2023
f75314c
Update src/resources/lang/en/base.php
pxpm Aug 29, 2023
14ee78e
dont alias signed and verified middlewares
pxpm Aug 29, 2023
e4f74e0
Merge branch 'add-email-confirmation' of https://github.com/Laravel-B…
pxpm Aug 29, 2023
cd092b7
small fixes
pxpm Aug 29, 2023
f60f722
Merge branch 'main' into add-email-confirmation
pxpm Aug 30, 2023
fa36181
add verification to middleware aliases
pxpm Aug 30, 2023
52c1341
Apply fixes from StyleCI
StyleCIBot Aug 30, 2023
15fe548
fixes
pxpm Aug 30, 2023
669823c
Merge branch 'add-email-confirmation' of https://github.com/Laravel-B…
pxpm Aug 30, 2023
fbd760a
Apply fixes from StyleCI
StyleCIBot Aug 30, 2023
1c36a61
Update src/app/Http/Controllers/Auth/VerifyEmailController.php
pxpm Aug 30, 2023
6e21330
add invokable
pxpm Aug 30, 2023
7ea81fa
move to function
pxpm Aug 30, 2023
25bc08c
Apply fixes from StyleCI
StyleCIBot Aug 30, 2023
d2d8958
return early
pxpm Aug 30, 2023
d78729c
Apply fixes from StyleCI
StyleCIBot Aug 30, 2023
3dc83ee
Apply suggestions from code review
pxpm Aug 31, 2023
252bfa6
Apply fixes from StyleCI
StyleCIBot Aug 31, 2023
431b24e
Merge branch 'main' into add-email-confirmation
pxpm Aug 31, 2023
0910fc2
Apply suggestions from code review
pxpm Aug 31, 2023
489c327
Merge branch 'add-email-confirmation' of https://github.com/Laravel-B…
pxpm Aug 31, 2023
2e912ac
fixes
pxpm Aug 31, 2023
d2a0f59
fix
pxpm Aug 31, 2023
b0c2c50
Apply fixes from StyleCI
StyleCIBot Aug 31, 2023
cbb430a
Update src/config/backpack/base.php
tabacitu Aug 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/BackpackServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Backpack\CRUD;

use Backpack\Basset\Facades\Basset;
use Backpack\CRUD\app\Http\Middleware\EnsureEmailVerification;
use Backpack\CRUD\app\Http\Middleware\ThrottlePasswordRecovery;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
use Backpack\CRUD\app\Library\Database\DatabaseSchema;
Expand Down Expand Up @@ -129,6 +130,11 @@ public function registerMiddlewareGroup(Router $router)
if (config('backpack.base.setup_password_recovery_routes')) {
$router->aliasMiddleware('backpack.throttle.password.recovery', ThrottlePasswordRecovery::class);
}

// register the email verification middleware, if the developer enabled it in the config.
if (config('backpack.base.setup_email_verification_routes', false) && config('backpack.base.setup_email_verification_middleware', true)) {
$router->pushMiddlewareToGroup($middleware_key, EnsureEmailVerification::class);
}
}

public function publishFiles()
Expand Down
13 changes: 10 additions & 3 deletions src/app/Http/Controllers/Auth/RegisterController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Validator;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
Expand Down Expand Up @@ -64,7 +65,7 @@ protected function validator(array $data)
* Create a new user instance after a valid registration.
*
* @param array $data
* @return User
* @return \Illuminate\Contracts\Auth\Authenticatable
*/
protected function create(array $data)
{
Expand Down Expand Up @@ -99,7 +100,7 @@ public function showRegistrationForm()
* Handle a registration request for the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\Response|\Illuminate\Contracts\View\View
*/
public function register(Request $request)
{
Expand All @@ -113,6 +114,12 @@ public function register(Request $request)
$user = $this->create($request->all());

event(new Registered($user));
if (config('backpack.base.setup_email_verification_routes')) {
Cookie::queue('backpack_email_verification', $user->{config('backpack.base.email_column')}, 30);

return redirect(route('verification.notice'));
}

$this->guard()->login($user);

return redirect($this->redirectPath());
Expand Down
88 changes: 88 additions & 0 deletions src/app/Http/Controllers/Auth/VerifyEmailController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

namespace Backpack\CRUD\app\Http\Controllers\Auth;

use Backpack\CRUD\app\Http\Requests\EmailVerificationRequest;
use Backpack\CRUD\app\Library\Auth\UserFromCookie;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Prologue\Alerts\Facades\Alert;

class VerifyEmailController extends Controller
{
public null|string $redirectTo = null;

/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
if (! app('router')->getMiddleware()['signed'] ?? null) {
throw new Exception('Missing "signed" alias middleware in App/Http/Kernel.php. More info: https://backpackforlaravel.com/docs/6.x/base-how-to#enable-email-verification-in-backpack-routes');
}

$this->middleware('signed')->only('verifyEmail');
tabacitu marked this conversation as resolved.
Show resolved Hide resolved
$this->middleware('throttle:'.config('backpack.base.email_verification_throttle_access'))->only('resendVerificationEmail');

if (! backpack_users_have_email()) {
abort(500, trans('backpack::base.no_email_column'));
}
// where to redirect after the email is verified
$this->redirectTo = $this->redirectTo ?? backpack_url('dashboard');
}

public function emailVerificationRequired(Request $request): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
{
$this->getUserOrRedirect($request);

return view(backpack_view('auth.verify-email'));
}

/**
* Verify the user's email address.
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function verifyEmail(EmailVerificationRequest $request)
{
$this->getUserOrRedirect($request);

$request->fulfill();

return redirect($this->redirectTo);
}

/**
* Resend the email verification notification.
*/
public function resendVerificationEmail(Request $request): \Illuminate\Http\RedirectResponse
{
$user = $this->getUserOrRedirect($request);

if (is_a($user, \Illuminate\Http\RedirectResponse::class)) {
return $user;
}

$user->sendEmailVerificationNotification();
Alert::success('Email verification link sent successfully.')->flash();

return back()->with('status', 'verification-link-sent');
}

private function getUser(Request $request): ?\Illuminate\Contracts\Auth\MustVerifyEmail
{
return $request->user(backpack_guard_name()) ?? (new UserFromCookie())();
}

private function getUserOrRedirect(Request $request): \Illuminate\Contracts\Auth\MustVerifyEmail|\Illuminate\Http\RedirectResponse
{
if ($user = $this->getUser($request)) {
return $user;
}

return redirect()->route('backpack.auth.login');
}
}
pxpm marked this conversation as resolved.
Show resolved Hide resolved
39 changes: 39 additions & 0 deletions src/app/Http/Middleware/EnsureEmailVerification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Backpack\CRUD\app\Http\Middleware;

use Closure;
use Exception;
use Throwable;

class EnsureEmailVerification
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// if the route is one in the verification process, do nothing
if (in_array($request->route()->getName(), ['verification.notice', 'verification.verify', 'verification.send'])) {
return $next($request);
}

// the Laravel middleware needs the user resolver to be set with the backpack guard
$userResolver = $request->getUserResolver();
$request->setUserResolver(function () use ($userResolver) {
return $userResolver(backpack_guard_name());
});

try {
$verifiedMiddleware = new (app('router')->getMiddleware()['verified'])();
} catch(Throwable) {
throw new Exception('Missing "verified" alias middleware in App/Http/Kernel.php. More info: https://backpackforlaravel.com/docs/6.x/base-how-to#enable-email-verification-in-backpack-routes');
}

return $verifiedMiddleware->handle($request, $next);
}
}
14 changes: 14 additions & 0 deletions src/app/Http/Requests/EmailVerificationRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Backpack\CRUD\app\Http\Requests;

use Backpack\CRUD\app\Library\Auth\UserFromCookie;
use Illuminate\Foundation\Auth\EmailVerificationRequest as OriginalEmailVerificationRequest;

class EmailVerificationRequest extends OriginalEmailVerificationRequest
{
public function user($guard = null)
{
return parent::user(backpack_guard_name()) ?? (new UserFromCookie())();
}
}
26 changes: 26 additions & 0 deletions src/app/Library/Auth/AuthenticatesUsers.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Validation\ValidationException;

trait AuthenticatesUsers
Expand Down Expand Up @@ -47,6 +48,10 @@ public function login(Request $request)
}

if ($this->attemptLogin($request)) {
if (config('backpack.base.setup_email_verification_routes', false)) {
return $this->logoutIfEmailNotVerified($request);
}

return $this->sendLoginResponse($request);
}

Expand Down Expand Up @@ -199,4 +204,25 @@ protected function guard()
{
return Auth::guard();
}

private function logoutIfEmailNotVerified(Request $request): Response|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
{
tabacitu marked this conversation as resolved.
Show resolved Hide resolved
$user = $this->guard()->user();

// if the user is already verified, do nothing
if ($user->email_verified_at) {
return $this->sendLoginResponse($request);
}
// user is not yet verified, log him out
$this->guard()->logout();

// add a cookie for 30m to remember the email address that needs to be verified
Cookie::queue('backpack_email_verification', $user->{config('backpack.base.email_column')}, 30);

if ($request->wantsJson()) {
return new Response('Email verification required', 403);
}

return redirect(route('verification.notice'));
}
}
17 changes: 17 additions & 0 deletions src/app/Library/Auth/UserFromCookie.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Backpack\CRUD\app\Library\Auth;

use Illuminate\Support\Facades\Cookie;

class UserFromCookie
{
public function __invoke(): ?\Illuminate\Contracts\Auth\MustVerifyEmail
{
if (Cookie::has('backpack_email_verification')) {
return config('backpack.base.user_model_fqn')::where(config('backpack.base.email_column'), Cookie::get('backpack_email_verification'))->first();
}

return null;
}
}
15 changes: 15 additions & 0 deletions src/config/backpack/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,21 @@
// (you then need to manually define the routes in your web.php)
'setup_password_recovery_routes' => true,

// Set this to true if you would like to enable email verification for your user model.
// Make sure your user model implements the MustVerifyEmail contract and your database
// table contains the `email_verified_at` column. Read the following before enabling:
// https://backpackforlaravel.com/docs/6.x/base-how-to#enable-email-verification-in-backpack-routes
'setup_email_verification_routes' => false,

// When email verification is enabled, automatically add the Verified middleware to Backpack routes?
// Set false if you want to use your own Verified middleware in `middleware_class`.
'setup_email_verification_middleware' => true,

// How many times in any given time period should the user be allowed to
// request a new verification email?
// Defaults to 1,10 - 1 time in 10 minutes.
'email_verification_throttle_access' => '3,15',

/*
|--------------------------------------------------------------------------
| Security
Expand Down
6 changes: 6 additions & 0 deletions src/resources/lang/en/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,10 @@
'throttled' => 'You have already requested a password reset recently. Please check your email. If you do not receive our email, please retry later.',
'throttled_request' => 'You have exceeded the limit of tries. Please wait a few minutes and try again.',

'verify_email' => [
'email_verification' => 'Email Verification',
'verification_link_sent' => 'A verification link has been sent to your email address.',
'email_verification_required' => 'Please verify your email address, by clicking on the link we\'ve sent you.',
'resend_verification_link' => 'Resend link',
],
];
2 changes: 1 addition & 1 deletion src/resources/views/ui/errors/layout.blade.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{{-- show error using sidebar layout if logged in AND on an admin page; otherwise use a blank page --}}
@extends(backpack_view(backpack_user() && backpack_theme_config('layout') ? 'layouts.'.backpack_theme_config('layout') : 'errors.blank'))
{{-- show error using sidebar layout if looged in AND on an admin page; otherwise use a blank page --}}

@section('content')
<div class="row">
Expand Down
6 changes: 6 additions & 0 deletions src/routes/backpack/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ function () {
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('backpack.auth.password.reset.token');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('backpack.auth.password.email')->middleware('backpack.throttle.password.recovery:'.config('backpack.base.password_recovery_throttle_access'));
}

if (config('backpack.base.setup_email_verification_routes', false)) {
Route::get('email/verify', 'Auth\VerifyEmailController@emailVerificationRequired')->name('verification.notice');
Route::get('email/verify/{id}/{hash}', 'Auth\VerifyEmailController@verifyEmail')->name('verification.verify');
Route::post('email/verification-notification', 'Auth\VerifyEmailController@resendVerificationEmail')->name('verification.send');
}
}

// if not otherwise configured, setup the dashboard routes
Expand Down