├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Krucas │ └── LaravelUserEmailVerification │ │ ├── AuthenticatesAndRegistersUsers.php │ │ ├── AuthenticatesUsers.php │ │ ├── Console │ │ ├── ClearVerificationTokensCommand.php │ │ ├── MakeVerificationCommand.php │ │ └── stubs │ │ │ ├── controllers │ │ │ └── verifycontroller.stub │ │ │ ├── migrations │ │ │ ├── users.stub │ │ │ └── users_verifications.stub │ │ │ └── routes.stub │ │ ├── Contracts │ │ ├── Factory.php │ │ ├── RequiresEmailVerification.php │ │ ├── TokenRepositoryInterface.php │ │ └── VerificationBroker.php │ │ ├── DatabaseTokenRepository.php │ │ ├── Facades │ │ └── Verification.php │ │ ├── RedirectsUsers.php │ │ ├── RegistersUsers.php │ │ ├── RequiresEmailVerification.php │ │ ├── UserEmailVerificationServiceProvider.php │ │ ├── VerificationBroker.php │ │ ├── VerificationBrokerManager.php │ │ └── VerifiesUsers.php ├── config │ └── verification.php ├── translations │ └── en │ │ └── verification.php └── views │ └── auth │ ├── emails │ └── verification.blade.php │ └── verification │ ├── message.blade.php │ └── resend.blade.php └── tests ├── AuthenticatesUsersTest.php ├── DatabaseTokenRepositoryTest.php ├── RedirectsUsersTest.php ├── RegistersUsersTest.php ├── TestCase.php ├── VerificationBrokerTest.php └── VerifiesUsersTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | composer.lock -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.5 5 | - 5.6 6 | - 7.0 7 | - hhvm 8 | 9 | before_script: 10 | - curl -s http://getcomposer.org/installer | php 11 | - php composer.phar install --dev 12 | 13 | script: phpunit -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2016 Edvinas Kručas 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # User email verification for Laravel 5 2 | 3 | 4 | [![Build Status](https://travis-ci.org/edvinaskrucas/laravel-user-email-verification.png?branch=master)](https://travis-ci.org/edvinaskrucas/laravel-user-email-verification) 5 | 6 | --- 7 | 8 | ## Installation 9 | 10 | Require this package in your composer.json by typing in your console: 11 | 12 | ``` 13 | composer require edvinaskrucas/laravel-user-email-verification 14 | ``` 15 | 16 | ### Registering to use it with laravel 17 | 18 | Add following lines to ```app/config/app.php``` 19 | 20 | ServiceProvider array 21 | 22 | ```php 23 | Krucas\LaravelUserEmailVerification\UserEmailVerificationServiceProvider::class, 24 | ``` 25 | 26 | ### Publishing config file 27 | 28 | If you want to edit default config file, just publish it to your app folder. 29 | 30 | php artisan vendor:publish --provider="Krucas\LaravelUserEmailVerification\UserEmailVerificationServiceProvider" --tag="config" 31 | 32 | 33 | ### Publishing translations 34 | 35 | In order to customise translations you need to publish it. 36 | 37 | php artisan vendor:publish --provider="Krucas\LaravelUserEmailVerification\UserEmailVerificationServiceProvider" --tag="translations" 38 | 39 | 40 | ### Publishing views 41 | 42 | Package comes with default views, if you want to edit them, just publish it. 43 | 44 | php artisan vendor:publish --provider="Krucas\LaravelUserEmailVerification\UserEmailVerificationServiceProvider" --tag="views" 45 | 46 | ## Usage 47 | 48 | ### Configuration 49 | 50 | Package comes with several configuration options. 51 | 52 | | Setting | Description | 53 | | --- | --- | 54 | | ```default``` | Default broker driver. | 55 | | ```verify``` | MUST or MUST NOT user validate his account before login. | 56 | | ```repositories``` | Config of all repositories which can be used. | 57 | | ```brokers``` | Config of all brokers which can be used. | 58 | 59 | ### Install default controller, routes and migrations 60 | 61 | ``` 62 | php artisan verification:make 63 | ``` 64 | 65 | Command above will add default ```VerifyController``` to ```app/Http/Controllers/Auth/VerifyController.php``` which 66 | will provide default verification behaviour. 67 | 68 | Also routes will be modified, it will add default routes for verification controller. 69 | 70 | Migrations will add extra columns to ```users``` table to identify if user is verified or not, also 71 | token table will be added to store verification tokens. 72 | 73 | After running command you have to install new migrations, this can be done with this command: 74 | 75 | ``` 76 | php artisan migrate 77 | ``` 78 | 79 | After all these steps you need to adjust default auth controller provided by Laravel, these adjustments 80 | will enable authentication controller to send verification email and will not allow non-verified users to login. 81 | 82 | ### Clear expired tokens 83 | 84 | Package comes with useful command to clear expired tokens, just replace ```{broker}``` with your broker name. 85 | 86 | ``` 87 | php artisan verification:clear-tokens {broker} 88 | ``` 89 | 90 | --- 91 | 92 | More info can be found here: [http://www.krucas.com/2016/04/user-email-verification-for-laravel-5/](http://www.krucas.com/2016/04/user-email-verification-for-laravel-5/) -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "edvinaskrucas/laravel-user-email-verification", 3 | "keywords": ["laravel", "user verification", "email verification", "user email verification"], 4 | "description": "Laravel Package for easier user email verification.", 5 | "authors": [ 6 | { 7 | "name": "Edvinas Kručas", 8 | "email": "edv.krucas@gmail.com" 9 | } 10 | ], 11 | "license": "MIT", 12 | "require": { 13 | "php" : "^5.5|^7.0", 14 | "illuminate/support": "^5.2", 15 | "illuminate/console": "^5.2", 16 | "illuminate/database": "^5.2", 17 | "illuminate/filesystem": "^5.2", 18 | "illuminate/http": "^5.2", 19 | "illuminate/mail": "^5.2" 20 | }, 21 | "require-dev": { 22 | "mockery/mockery": "0.9.*", 23 | "laravel/framework": "^5.2", 24 | "phpunit/phpunit": "^4.8" 25 | }, 26 | "autoload": { 27 | "psr-0": { 28 | "Krucas\\LaravelUserEmailVerification": "src/" 29 | } 30 | }, 31 | "autoload-dev": { 32 | "psr-4": { 33 | "Krucas\\LaravelUserEmailVerification\\Test\\": "tests" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | 17 | 18 | ./tests/ 19 | 20 | 21 | 22 | 23 | ./src/Krucas/ 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/AuthenticatesAndRegistersUsers.php: -------------------------------------------------------------------------------- 1 | isUserEmailVerified()) { 24 | Auth::guard($this->getGuard())->logout(); 25 | 26 | return redirect($this->verificationRedirectPath()); 27 | } 28 | 29 | return redirect()->intended($this->redirectPath()); 30 | } 31 | 32 | /** 33 | * Get the guard to be used during verification. 34 | * 35 | * @return string|null 36 | */ 37 | protected function getGuard() 38 | { 39 | return property_exists($this, 'guard') ? $this->guard : null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/ClearVerificationTokensCommand.php: -------------------------------------------------------------------------------- 1 | laravel['auth.verification']->broker($this->argument('name'))->getRepository()->deleteExpired(); 31 | $this->info('Expired verification tokens cleared!'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/MakeVerificationCommand.php: -------------------------------------------------------------------------------- 1 | composer = $composer; 42 | } 43 | 44 | /** 45 | * Execute the console command. 46 | * 47 | * @return void 48 | */ 49 | public function fire() 50 | { 51 | $this->createMigrations(); 52 | 53 | $this->info('Installed VerifyController'); 54 | 55 | file_put_contents( 56 | app_path('Http/Controllers/Auth/VerifyController.php'), 57 | $this->compileControllerStub() 58 | ); 59 | 60 | $this->info('Updated routes.php'); 61 | 62 | $this->appendRoutes(); 63 | 64 | $this->composer->dumpAutoloads(); 65 | } 66 | 67 | /** 68 | * Create migrations. 69 | * 70 | * @return void 71 | */ 72 | protected function createMigrations() 73 | { 74 | file_put_contents( 75 | $this->createUsersVerificationsMigration(), 76 | file_get_contents(__DIR__.'/stubs/migrations/users_verifications.stub') 77 | ); 78 | 79 | file_put_contents( 80 | $this->createUsersMigration(), 81 | file_get_contents(__DIR__.'/stubs/migrations/users.stub') 82 | ); 83 | 84 | $this->info('Migrations created successfully!'); 85 | } 86 | 87 | /** 88 | * Create a base migration file for the verifications table. 89 | * 90 | * @return string 91 | */ 92 | protected function createUsersVerificationsMigration() 93 | { 94 | $name = 'create_users_verifications_table'; 95 | 96 | $path = $this->laravel->databasePath().'/migrations'; 97 | 98 | return $this->laravel['migration.creator']->create($name, $path); 99 | } 100 | 101 | /** 102 | * Create a base migration file for the users table. 103 | * 104 | * @return string 105 | */ 106 | protected function createUsersMigration() 107 | { 108 | $name = 'add_verified_columns_to_users_table'; 109 | 110 | $path = $this->laravel->databasePath().'/migrations'; 111 | 112 | return $this->laravel['migration.creator']->create($name, $path); 113 | } 114 | 115 | /** 116 | * Compiles the VerifyController stub. 117 | * 118 | * @return string 119 | */ 120 | protected function compileControllerStub() 121 | { 122 | return str_replace( 123 | '{{namespace}}', 124 | $this->getAppNamespace(), 125 | file_get_contents(__DIR__.'/stubs/controllers/verifycontroller.stub') 126 | ); 127 | } 128 | 129 | /** 130 | * Append routes file. 131 | * 132 | * @return void 133 | */ 134 | protected function appendRoutes() 135 | { 136 | $path = app_path('Http/routes.php'); 137 | 138 | file_put_contents( 139 | $path, 140 | file_get_contents($path)."\n\n".file_get_contents(__DIR__.'/stubs/routes.stub') 141 | ); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/stubs/controllers/verifycontroller.stub: -------------------------------------------------------------------------------- 1 | middleware($this->guestMiddleware()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/stubs/migrations/users.stub: -------------------------------------------------------------------------------- 1 | boolean('verified')->default(0)->after('password'); 17 | $table->timestamp('verified_at')->nullable(true)->default(null)->after('verified'); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('users', function (Blueprint $table) { 29 | $table->dropColumn('verified'); 30 | $table->dropColumn('verified_at'); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/stubs/migrations/users_verifications.stub: -------------------------------------------------------------------------------- 1 | string('email')->index(); 17 | $table->string('token')->index(); 18 | $table->timestamp('created_at'); 19 | }); 20 | } 21 | 22 | /** 23 | * Reverse the migrations. 24 | * 25 | * @return void 26 | */ 27 | public function down() 28 | { 29 | Schema::drop('users_verifications'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Console/stubs/routes.stub: -------------------------------------------------------------------------------- 1 | // verification token resend form 2 | Route::get('verify/resend', [ 3 | 'uses' => 'Auth\VerifyController@showResendForm', 4 | 'as' => 'verification.resend', 5 | ]); 6 | 7 | // verification token resend action 8 | Route::post('verify/resend', [ 9 | 'uses' => 'Auth\VerifyController@sendVerificationLinkEmail', 10 | 'as' => 'verification.resend.post', 11 | ]); 12 | 13 | // verification message / user verification 14 | Route::get('verify/{token?}', [ 15 | 'uses' => 'Auth\VerifyController@verify', 16 | 'as' => 'verification.verify', 17 | ]); -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Contracts/Factory.php: -------------------------------------------------------------------------------- 1 | table = $table; 51 | $this->hashKey = $hashKey; 52 | $this->expires = $expires; 53 | $this->connection = $connection; 54 | } 55 | 56 | /** 57 | * Create a new token. 58 | * 59 | * @param \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification $user 60 | * @return string 61 | */ 62 | public function create(Contracts\RequiresEmailVerification $user) 63 | { 64 | $email = $user->getEmailForVerification(); 65 | 66 | $this->deleteExisting($user); 67 | 68 | $token = $this->createNewToken(); 69 | 70 | $this->getTable()->insert($this->getPayload($email, $token)); 71 | 72 | return $token; 73 | } 74 | 75 | /** 76 | * Delete all existing reset tokens from the database. 77 | * 78 | * @param \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification $user 79 | * @return int 80 | */ 81 | protected function deleteExisting(Contracts\RequiresEmailVerification $user) 82 | { 83 | return $this->getTable()->where('email', $user->getEmailForVerification())->delete(); 84 | } 85 | 86 | /** 87 | * Build the record payload for the table. 88 | * 89 | * @param string $email 90 | * @param string $token 91 | * @return array 92 | */ 93 | protected function getPayload($email, $token) 94 | { 95 | return ['email' => $email, 'token' => $token, 'created_at' => new Carbon]; 96 | } 97 | 98 | /** 99 | * Determine if a token record exists and is valid. 100 | * 101 | * @param \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification $user 102 | * @param string $token 103 | * @return bool 104 | */ 105 | public function exists(Contracts\RequiresEmailVerification $user, $token) 106 | { 107 | $email = $user->getEmailForVerification(); 108 | 109 | $token = (array) $this->getTable()->where('email', $email)->where('token', $token)->first(); 110 | 111 | return $token && !$this->tokenExpired($token); 112 | } 113 | 114 | /** 115 | * Determine if the token has expired. 116 | * 117 | * @param array $token 118 | * @return bool 119 | */ 120 | protected function tokenExpired($token) 121 | { 122 | if (!$this->isExpirationEnabled()) { 123 | return false; 124 | } 125 | 126 | $expirationTime = strtotime($token['created_at']) + $this->expires; 127 | 128 | return $expirationTime < $this->getCurrentTime(); 129 | } 130 | 131 | /** 132 | * Get the current UNIX timestamp. 133 | * 134 | * @return int 135 | */ 136 | protected function getCurrentTime() 137 | { 138 | return time(); 139 | } 140 | 141 | /** 142 | * Delete token record. 143 | * 144 | * @param string $token 145 | * @return void 146 | */ 147 | public function delete($token) 148 | { 149 | $this->getTable()->where('token', $token)->delete(); 150 | } 151 | 152 | /** 153 | * Delete expired tokens. 154 | * 155 | * @return void 156 | */ 157 | public function deleteExpired() 158 | { 159 | if ($this->isExpirationEnabled()) { 160 | $expiredAt = Carbon::now()->subSeconds($this->expires); 161 | 162 | $this->getTable()->where('created_at', '<', $expiredAt)->delete(); 163 | } 164 | } 165 | 166 | /** 167 | * Create a new token for the user. 168 | * 169 | * @return string 170 | */ 171 | public function createNewToken() 172 | { 173 | return hash_hmac('sha256', Str::random(40), $this->hashKey); 174 | } 175 | 176 | /** 177 | * Begin a new database query against the table. 178 | * 179 | * @return \Illuminate\Database\Query\Builder 180 | */ 181 | protected function getTable() 182 | { 183 | return $this->connection->table($this->table); 184 | } 185 | 186 | /** 187 | * Determine if token expiration is enabled or disabled. 188 | * 189 | * @return bool 190 | */ 191 | protected function isExpirationEnabled() 192 | { 193 | return $this->expires > 0; 194 | } 195 | 196 | /** 197 | * Get the database connection instance. 198 | * 199 | * @return \Illuminate\Database\ConnectionInterface 200 | */ 201 | public function getConnection() 202 | { 203 | return $this->connection; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/Facades/Verification.php: -------------------------------------------------------------------------------- 1 | verificationRedirectPath; 16 | } 17 | 18 | return property_exists($this, 'verificationRedirectTo') 19 | ? $this->verificationRedirectTo : route('verification.verify', [], false); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/RegistersUsers.php: -------------------------------------------------------------------------------- 1 | validator($request->all()); 23 | 24 | if ($validator->fails()) { 25 | $this->throwValidationException($request, $validator); 26 | } 27 | 28 | $user = $this->create($request->all()); 29 | 30 | $broker = $this->getBroker(); 31 | 32 | $credentials = $request->only('email'); 33 | 34 | Verification::broker($broker)->sendVerificationLink($credentials, function (Message $message) { 35 | $message->subject($this->getEmailSubject()); 36 | }); 37 | 38 | if (config('verification.verify')) { 39 | return redirect($this->verificationRedirectPath()); 40 | } 41 | 42 | Auth::guard($this->getGuard())->login($user); 43 | 44 | return redirect($this->redirectPath()); 45 | } 46 | 47 | /** 48 | * Get the broker to be used during verification process. 49 | * 50 | * @return string|null 51 | */ 52 | public function getBroker() 53 | { 54 | return property_exists($this, 'broker') ? $this->broker : null; 55 | } 56 | 57 | /** 58 | * Get the guard to be used during verification. 59 | * 60 | * @return string|null 61 | */ 62 | protected function getGuard() 63 | { 64 | return property_exists($this, 'guard') ? $this->guard : null; 65 | } 66 | 67 | /** 68 | * Get the e-mail subject line to be used for the reset link email. 69 | * 70 | * @return string 71 | */ 72 | protected function getEmailSubject() 73 | { 74 | return trans('verification::verification.subject'); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/RequiresEmailVerification.php: -------------------------------------------------------------------------------- 1 | email; 15 | } 16 | 17 | /** 18 | * Determine if user is verified or not. 19 | * 20 | * @return bool 21 | */ 22 | public function isUserEmailVerified() 23 | { 24 | return (bool) $this->verified; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/UserEmailVerificationServiceProvider.php: -------------------------------------------------------------------------------- 1 | [VerificationBrokerManager::class, Contracts\Factory::class], 19 | 'auth.verification.broker' => [VerificationBroker::class, Contracts\VerificationBroker::class], 20 | ]; 21 | 22 | /** 23 | * Boot service provider. 24 | * 25 | * @return void 26 | */ 27 | public function boot() 28 | { 29 | $this->loadTranslationsFrom(__DIR__ . '/../../translations', 'verification'); 30 | $this->loadViewsFrom(__DIR__ . '/../../views', 'verification'); 31 | 32 | $this->publishes([ 33 | __DIR__ . '/../../config/verification.php' => config_path('verification.php'), 34 | ], 'config'); 35 | 36 | $this->publishes([ 37 | __DIR__ . '/../../translations' => resource_path('lang/vendor/verification'), 38 | ], 'translations'); 39 | 40 | $this->publishes([ 41 | __DIR__ . '/../../views' => resource_path('views/vendor/verification'), 42 | ], 'views'); 43 | } 44 | 45 | /** 46 | * Register the service provider. 47 | * 48 | * @return void 49 | */ 50 | public function register() 51 | { 52 | $this->mergeConfigFrom(__DIR__ . '/../../config/verification.php', 'verification'); 53 | 54 | $this->app->singleton('auth.verification', function ($app) { 55 | return new VerificationBrokerManager($app); 56 | }); 57 | 58 | $this->app->bind('auth.verification.broker', function ($app) { 59 | return $app->make('auth.verification')->broker(); 60 | }); 61 | 62 | foreach ($this->aliases as $key => $aliases) { 63 | foreach ($aliases as $alias) { 64 | $this->app->alias($key, $alias); 65 | } 66 | } 67 | 68 | $this->registerCommands(); 69 | } 70 | 71 | /** 72 | * Register the related console commands. 73 | * 74 | * @return void 75 | */ 76 | protected function registerCommands() 77 | { 78 | $this->app->singleton('command.verification.make', function ($app) { 79 | return new MakeVerificationCommand($app['composer']); 80 | }); 81 | 82 | $this->app->singleton('command.verification.clear', function ($app) { 83 | return new ClearVerificationTokensCommand(); 84 | }); 85 | 86 | $this->commands('command.verification.make'); 87 | $this->commands('command.verification.clear'); 88 | } 89 | 90 | /** 91 | * Get the services provided by the provider. 92 | * 93 | * @return array 94 | */ 95 | public function provides() 96 | { 97 | return [ 98 | 'command.verification.make', 99 | 'command.verification.clear', 100 | 'auth.verification', 101 | 'auth.verification.broker', 102 | VerificationBrokerManager::class, 103 | Contracts\Factory::class, 104 | VerificationBroker::class, 105 | Contracts\VerificationBroker::class, 106 | ]; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/VerificationBroker.php: -------------------------------------------------------------------------------- 1 | tokens = $tokens; 51 | $this->users = $users; 52 | $this->mailer = $mailer; 53 | $this->emailView = $emailView; 54 | } 55 | 56 | /** 57 | * Send a user verification link. 58 | * 59 | * @param array $credentials 60 | * @param \Closure|null $callback 61 | * @return string 62 | */ 63 | public function sendVerificationLink(array $credentials, Closure $callback = null) 64 | { 65 | $user = $this->getUser($credentials); 66 | 67 | if (is_null($user)) { 68 | return Contracts\VerificationBroker::INVALID_USER; 69 | } 70 | 71 | $token = $this->tokens->create($user); 72 | 73 | $this->emailVerificationLink($user, $token, $callback); 74 | 75 | return Contracts\VerificationBroker::VERIFICATION_LINK_SENT; 76 | } 77 | 78 | /** 79 | * Send the email verification link via e-mail. 80 | * 81 | * @param \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification $user 82 | * @param string $token 83 | * @param \Closure|null $callback 84 | * @return int 85 | */ 86 | public function emailVerificationLink(Contracts\RequiresEmailVerification $user, $token, Closure $callback = null) 87 | { 88 | $view = $this->emailView; 89 | 90 | return $this->mailer->send($view, compact('token', 'user'), function ($message) use ($user, $token, $callback) { 91 | $message->to($user->getEmailForVerification()); 92 | 93 | if (!is_null($callback)) { 94 | call_user_func($callback, $message, $user, $token); 95 | } 96 | }); 97 | } 98 | 99 | /** 100 | * Verify given account. 101 | * 102 | * @param array $credentials 103 | * @param \Closure $callback 104 | * @return mixed 105 | */ 106 | public function verify(array $credentials, Closure $callback) 107 | { 108 | $user = $this->validateVerification($credentials); 109 | 110 | if (!$user instanceof Contracts\RequiresEmailVerification) { 111 | return $user; 112 | } 113 | 114 | call_user_func($callback, $user); 115 | 116 | $this->tokens->delete($credentials['token']); 117 | 118 | return Contracts\VerificationBroker::VERIFIED; 119 | } 120 | 121 | /** 122 | * Validate verification for the given credentials. 123 | * 124 | * @param array $credentials 125 | * @return \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification 126 | */ 127 | protected function validateVerification(array $credentials) 128 | { 129 | if (is_null($user = $this->getUser($credentials))) { 130 | return Contracts\VerificationBroker::INVALID_USER; 131 | } 132 | 133 | if (!$this->tokens->exists($user, $credentials['token'])) { 134 | return Contracts\VerificationBroker::INVALID_TOKEN; 135 | } 136 | 137 | return $user; 138 | } 139 | 140 | /** 141 | * Get the user for the given credentials. 142 | * 143 | * @param array $credentials 144 | * @return \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification 145 | * 146 | * @throws \UnexpectedValueException 147 | */ 148 | public function getUser(array $credentials) 149 | { 150 | $credentials = Arr::except($credentials, ['token']); 151 | 152 | $user = $this->users->retrieveByCredentials($credentials); 153 | 154 | if ($user && !$user instanceof Contracts\RequiresEmailVerification) { 155 | throw new UnexpectedValueException('User must implement RequiresEmailVerification interface.'); 156 | } 157 | 158 | return $user; 159 | } 160 | 161 | /** 162 | * Get the verification token repository implementation. 163 | * 164 | * @return \Krucas\LaravelUserEmailVerification\Contracts\TokenRepositoryInterface 165 | */ 166 | public function getRepository() 167 | { 168 | return $this->tokens; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/VerificationBrokerManager.php: -------------------------------------------------------------------------------- 1 | app = $app; 40 | } 41 | 42 | /** 43 | * Get a verification broker instance by name. 44 | * 45 | * @param string|null $name 46 | * @return mixed 47 | */ 48 | public function broker($name = null) 49 | { 50 | $name = $name ?: $this->getDefaultDriver(); 51 | 52 | return $this->brokers[$name] = $this->get($name); 53 | } 54 | 55 | /** 56 | * Attempt to get the broker from the local cache. 57 | * 58 | * @param string $name 59 | * @return \Krucas\LaravelUserEmailVerification\Contracts\VerificationBroker 60 | */ 61 | protected function get($name) 62 | { 63 | return isset($this->brokers[$name]) ? $this->brokers[$name] : $this->resolve($name); 64 | } 65 | 66 | /** 67 | * Resolve the given broker. 68 | * 69 | * @param string $name 70 | * @return \Krucas\LaravelUserEmailVerification\Contracts\VerificationBroker 71 | */ 72 | protected function resolve($name) 73 | { 74 | $config = $this->getConfig($name); 75 | 76 | if (is_null($config)) { 77 | throw new InvalidArgumentException("Verification broker [{$name}] is not defined."); 78 | } 79 | 80 | if (isset($this->customCreators[$name])) { 81 | return $this->callCustomCreator($name, $config); 82 | } else { 83 | return $this->{'create'.ucfirst($name).'Broker'}($config); 84 | } 85 | } 86 | 87 | /** 88 | * Call a custom driver creator. 89 | * 90 | * @param string $name 91 | * @param array $config 92 | * @return mixed 93 | */ 94 | protected function callCustomCreator($name, array $config) 95 | { 96 | return $this->customCreators[$name]($this->app, $config); 97 | } 98 | 99 | /** 100 | * Create users broker. 101 | * 102 | * @param array $config 103 | * @return \Krucas\LaravelUserEmailVerification\Contracts\VerificationBroker 104 | */ 105 | protected function createUsersBroker(array $config) 106 | { 107 | return new VerificationBroker( 108 | $this->createTokenRepository($this->app['config']['verification.repositories.'.$config['repository']]), 109 | $this->app['auth']->createUserProvider($config['provider']), 110 | $this->app['mailer'], 111 | $config['email'] 112 | ); 113 | } 114 | 115 | /** 116 | * Create token repository 117 | * 118 | * @param array $config 119 | * @return \Krucas\LaravelUserEmailVerification\Contracts\TokenRepositoryInterface 120 | */ 121 | protected function createTokenRepository(array $config) 122 | { 123 | return $this->{'create'.ucfirst($config['driver']).'Repository'}($config); 124 | } 125 | 126 | /** 127 | * Create database token repository. 128 | * 129 | * @param array $config 130 | * @return \Krucas\LaravelUserEmailVerification\Contracts\TokenRepositoryInterface 131 | */ 132 | protected function createDatabaseRepository(array $config) 133 | { 134 | return new DatabaseTokenRepository( 135 | $this->app['db']->connection($config['connection']), 136 | $config['table'], 137 | $this->app['config']['app.key'], 138 | $config['expires'] 139 | ); 140 | } 141 | 142 | /** 143 | * Get the broker configuration. 144 | * 145 | * @param string $name 146 | * @return array 147 | */ 148 | protected function getConfig($name) 149 | { 150 | return $this->app['config']["verification.brokers.{$name}"]; 151 | } 152 | 153 | /** 154 | * Get the default settings repository name. 155 | * 156 | * @return string 157 | */ 158 | public function getDefaultDriver() 159 | { 160 | return $this->app['config']['verification.default']; 161 | } 162 | 163 | /** 164 | * Set the default driver name. 165 | * 166 | * @param string $name 167 | * @return void 168 | */ 169 | public function setDefaultDriver($name) 170 | { 171 | $this->app['config']['verification.default'] = $name; 172 | } 173 | 174 | /** 175 | * Register a custom driver creator Closure. 176 | * 177 | * @param string $driver 178 | * @param \Closure $callback 179 | * @return $this 180 | */ 181 | public function extend($driver, Closure $callback) 182 | { 183 | $this->customCreators[$driver] = $callback; 184 | 185 | return $this; 186 | } 187 | 188 | /** 189 | * Dynamically call the default driver instance. 190 | * 191 | * @param string $method 192 | * @param array $parameters 193 | * @return mixed 194 | */ 195 | public function __call($method, $parameters) 196 | { 197 | return call_user_func_array([$this->broker(), $method], $parameters); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/Krucas/LaravelUserEmailVerification/VerifiesUsers.php: -------------------------------------------------------------------------------- 1 | verify($request, $token); 26 | } 27 | 28 | /** 29 | * Display verify message / verify account. 30 | * 31 | * @param Request $request 32 | * @param null $token 33 | * @return \Illuminate\Http\Response 34 | */ 35 | public function verify(Request $request, $token = null) 36 | { 37 | if (is_null($token)) { 38 | return $this->showVerifyMessage(); 39 | } 40 | 41 | $credentials = ['email' => $request->get('email'), 'token' => $token]; 42 | 43 | $broker = $this->getBroker(); 44 | 45 | $response = Verification::broker($broker)->verify($credentials, function ($user) { 46 | $this->verifyUser($user); 47 | }); 48 | 49 | switch ($response) { 50 | case Contracts\VerificationBroker::VERIFIED: 51 | return $this->getVerificationSuccessResponse($response); 52 | 53 | default: 54 | return $this->getVerificationFailureResponse($request, $response); 55 | } 56 | } 57 | 58 | /** 59 | * Verify user. 60 | * 61 | * @param \Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification $user 62 | * @return void 63 | */ 64 | protected function verifyUser($user) 65 | { 66 | $user->verified = true; 67 | $user->verified_at = new Carbon('now'); 68 | 69 | $user->save(); 70 | 71 | Auth::guard($this->getGuard())->login($user); 72 | } 73 | 74 | /** 75 | * Display verify message. 76 | * 77 | * @return \Illuminate\Http\Response 78 | */ 79 | public function showVerifyMessage() 80 | { 81 | return view('verification::auth.verification.message'); 82 | } 83 | 84 | /** 85 | * Display link resend form. 86 | * 87 | * @return \Illuminate\Http\Response 88 | */ 89 | public function getResend() 90 | { 91 | return $this->showResendForm(); 92 | } 93 | 94 | /** 95 | * Display link resend form. 96 | * 97 | * @return \Illuminate\Http\Response 98 | */ 99 | public function showResendForm() 100 | { 101 | return view('verification::auth.verification.resend'); 102 | } 103 | 104 | /** 105 | * Send a verification link to the given user. 106 | * 107 | * @param \Illuminate\Http\Request $request 108 | * @return \Illuminate\Http\Response 109 | */ 110 | public function postResend(Request $request) 111 | { 112 | return $this->sendVerificationLinkEmail($request); 113 | } 114 | 115 | /** 116 | * Send a verification link to the given user. 117 | * 118 | * @param \Illuminate\Http\Request $request 119 | * @return \Illuminate\Http\Response 120 | */ 121 | public function sendVerificationLinkEmail(Request $request) 122 | { 123 | $this->validate($request, ['email' => 'required|email']); 124 | 125 | $broker = $this->getBroker(); 126 | 127 | $credentials = $request->only('email'); 128 | 129 | $response = Verification::broker($broker)->sendVerificationLink($credentials, function (Message $message) { 130 | $message->subject($this->getEmailSubject()); 131 | }); 132 | 133 | switch ($response) { 134 | case Contracts\VerificationBroker::VERIFICATION_LINK_SENT: 135 | return $this->getResendLinkEmailSuccessResponse($response); 136 | 137 | default: 138 | return $this->getResendLinkEmailFailureResponse($response); 139 | } 140 | } 141 | 142 | /** 143 | * Get the response for after the link could not be sent. 144 | * 145 | * @param string $response 146 | * @return \Symfony\Component\HttpFoundation\Response 147 | */ 148 | protected function getResendLinkEmailFailureResponse($response) 149 | { 150 | return redirect()->back()->withErrors(['email' => trans($response)]); 151 | } 152 | 153 | /** 154 | * Get the response for after the link has been successfully sent. 155 | * 156 | * @param string $response 157 | * @return \Symfony\Component\HttpFoundation\Response 158 | */ 159 | protected function getResendLinkEmailSuccessResponse($response) 160 | { 161 | return redirect()->back()->with('status', trans($response)); 162 | } 163 | 164 | /** 165 | * Get the broker to be used during verification process. 166 | * 167 | * @return string|null 168 | */ 169 | public function getBroker() 170 | { 171 | return property_exists($this, 'broker') ? $this->broker : null; 172 | } 173 | 174 | /** 175 | * Get the guard to be used during verification. 176 | * 177 | * @return string|null 178 | */ 179 | protected function getGuard() 180 | { 181 | return property_exists($this, 'guard') ? $this->guard : null; 182 | } 183 | 184 | /** 185 | * Get the e-mail subject line to be used for the reset link email. 186 | * 187 | * @return string 188 | */ 189 | protected function getEmailSubject() 190 | { 191 | return trans('verification::verification.subject'); 192 | } 193 | 194 | /** 195 | * Get the response for after a successful verification. 196 | * 197 | * @param string $response 198 | * @return \Symfony\Component\HttpFoundation\Response 199 | */ 200 | protected function getVerificationSuccessResponse($response) 201 | { 202 | return redirect($this->verificationRedirectPath())->with('status', trans($response)); 203 | } 204 | 205 | /** 206 | * Get the response for after a failing verification. 207 | * 208 | * @param Request $request 209 | * @param string $response 210 | * @return \Symfony\Component\HttpFoundation\Response 211 | */ 212 | protected function getVerificationFailureResponse(Request $request, $response) 213 | { 214 | return redirect()->route('verification.resend')->withErrors(['status' => trans($response)]); 215 | } 216 | 217 | /** 218 | * Get the guest middleware for the application. 219 | */ 220 | public function guestMiddleware() 221 | { 222 | $guard = $this->getGuard(); 223 | 224 | return $guard ? 'guest:'.$guard : 'guest'; 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /src/config/verification.php: -------------------------------------------------------------------------------- 1 | env('VERIFICATION_DRIVER', 'users'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Default Verification Value 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This option determines if user MUST verify 24 | | his account before login or not. 25 | | 26 | */ 27 | 28 | 'verify' => true, 29 | 30 | /* 31 | |-------------------------------------------------------------------------- 32 | | Repositories Configuration 33 | |-------------------------------------------------------------------------- 34 | | 35 | | Here you may configure the driver information for each repository that 36 | | is used by your application. A default configuration has been added 37 | | for each back-end shipped with this package. You are free to add more. 38 | | 39 | */ 40 | 41 | 'repositories' => [ 42 | 43 | 'database' => [ 44 | 'driver' => 'database', 45 | 'connection' => env('DB_CONNECTION', 'mysql'), 46 | 'table' => 'users_verifications', 47 | 'expires' => 0, 48 | ], 49 | 50 | ], 51 | 52 | /* 53 | |-------------------------------------------------------------------------- 54 | | Brokers Configuration 55 | |-------------------------------------------------------------------------- 56 | | 57 | | Here you may configure brokers. 58 | | A default configuration has been added 59 | | for each back-end shipped with this package. You are free to add more. 60 | | 61 | */ 62 | 63 | 'brokers' => [ 64 | 65 | 'users' => [ 66 | 'provider' => 'users', 67 | 'repository' => 'database', 68 | 'email' => 'verification::auth.emails.verification', 69 | ], 70 | 71 | ], 72 | 73 | ]; 74 | -------------------------------------------------------------------------------- /src/translations/en/verification.php: -------------------------------------------------------------------------------- 1 | 'We have e-mailed your verification link!', 17 | 'token' => 'This verification token is invalid.', 18 | 'user' => "We can't find a user with that e-mail address.", 19 | 'subject' => 'You account verification link', 20 | 21 | ]; 22 | -------------------------------------------------------------------------------- /src/views/auth/emails/verification.blade.php: -------------------------------------------------------------------------------- 1 | Click here to verify your account: {{ $link }} -------------------------------------------------------------------------------- /src/views/auth/verification/message.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | 4 | @section('content') 5 |
6 |
7 |
8 |
9 |
Verify Account
10 |
11 | In order to verify your account, you have to click on the link in your inbox. 12 |
13 | 16 |
17 |
18 |
19 |
20 | @endsection 21 | -------------------------------------------------------------------------------- /src/views/auth/verification/resend.blade.php: -------------------------------------------------------------------------------- 1 | @extends('layouts.app') 2 | 3 | 4 | @section('content') 5 |
6 |
7 |
8 |
9 |
Verify Account
10 |
11 | @if (session('status')) 12 |
13 | {{ session('status') }} 14 |
15 | @endif 16 | 17 | @if ($errors->has('status')) 18 |
19 | {{ $errors->first('status') }} 20 |
21 | @endif 22 | 23 |
24 | {!! csrf_field() !!} 25 | 26 |
27 | 28 | 29 |
30 | 31 | 32 | @if ($errors->has('email')) 33 | 34 | {{ $errors->first('email') }} 35 | 36 | @endif 37 |
38 |
39 | 40 |
41 |
42 | 45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 | @endsection 54 | -------------------------------------------------------------------------------- /tests/AuthenticatesUsersTest.php: -------------------------------------------------------------------------------- 1 | getUserMock(); 19 | $user->shouldReceive('isUserEmailVerified')->andReturn(false); 20 | 21 | $this->laravelContainer->shouldReceive('make')->with('config', [])->andReturn($config = $this->getConfigMock()); 22 | $config->shouldReceive('get')->with('verification.verify', null)->andReturn(true); 23 | 24 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 25 | $redirect = $this->getRedirectMock() 26 | ); 27 | $redirect->shouldReceive('to')->once()->andReturn('redirect'); 28 | 29 | Auth::shouldReceive('guard')->once()->andReturn($guard = m::mock('Illuminate\Contracts\Auth\Guard')); 30 | $guard->shouldReceive('logout')->once(); 31 | 32 | $this->laravelContainer->shouldReceive('make')->with('url', [])->andReturn($url = $this->getUrlMock()); 33 | $url->shouldReceive('route')->andReturn('redirect'); 34 | 35 | $trait = $this->getObjectForTrait('Krucas\LaravelUserEmailVerification\AuthenticatesUsers'); 36 | 37 | $this->assertEquals('redirect', $trait->authenticated($this->getRequestMock(), $user)); 38 | } 39 | 40 | public function testShouldRedirectIntendedOnConfigEnabledUserVerified() 41 | { 42 | $user = $this->getUserMock(); 43 | $user->shouldReceive('isUserEmailVerified')->andReturn(true); 44 | 45 | $this->laravelContainer->shouldReceive('make')->with('config', [])->andReturn($config = $this->getConfigMock()); 46 | $config->shouldReceive('get')->with('verification.verify', null)->andReturn(true); 47 | 48 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 49 | $redirect = $this->getRedirectMock() 50 | ); 51 | $redirect->shouldReceive('intended')->once()->andReturn('intended'); 52 | 53 | $trait = $this->getObjectForTrait('Krucas\LaravelUserEmailVerification\AuthenticatesUsers'); 54 | 55 | $this->assertEquals('intended', $trait->authenticated($this->getRequestMock(), $user)); 56 | } 57 | 58 | public function testShouldRedirectIntendedOnConfigDisabled() 59 | { 60 | $this->laravelContainer->shouldReceive('make')->with('config', [])->andReturn($config = $this->getConfigMock()); 61 | $config->shouldReceive('get')->with('verification.verify', null)->andReturn(false); 62 | 63 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 64 | $redirect = $this->getRedirectMock() 65 | ); 66 | $redirect->shouldReceive('intended')->once()->andReturn('intended'); 67 | 68 | $trait = $this->getObjectForTrait('Krucas\LaravelUserEmailVerification\AuthenticatesUsers'); 69 | 70 | $this->assertEquals('intended', $trait->authenticated($this->getRequestMock(), $this->getUserMock())); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/DatabaseTokenRepositoryTest.php: -------------------------------------------------------------------------------- 1 | getRepository($mocks = $this->getMocks()); 19 | 20 | $user = $this->getUserMock(); 21 | $user->shouldReceive('getEmailForVerification')->andReturn('mail'); 22 | 23 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 24 | $table->shouldReceive('where')->once()->with('email', 'mail')->andReturn($query = $this->getQueryBuilderMock()); 25 | $query->shouldReceive('delete')->once(); 26 | $table->shouldReceive('insert')->once(); 27 | 28 | $repository->create($user); 29 | } 30 | 31 | public function testExistsShouldReturnTrue() 32 | { 33 | $repository = $this->getRepository($mocks = $this->getMocks()); 34 | 35 | $user = $this->getUserMock(); 36 | $user->shouldReceive('getEmailForVerification')->andReturn('mail'); 37 | 38 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 39 | $table->shouldReceive('where')->once()->with('email', 'mail')->andReturn($query = $this->getQueryBuilderMock()); 40 | $query->shouldReceive('where')->once()->with('token', 'token')->andReturn( 41 | $entryQuery = $this->getQueryBuilderMock() 42 | ); 43 | $entryQuery->shouldReceive('first')->andReturn(['created' => time()]); 44 | 45 | $this->assertTrue($repository->exists($user, 'token')); 46 | } 47 | 48 | public function testExistsShouldReturnFalseOnNotFound() 49 | { 50 | $repository = $this->getRepository($mocks = $this->getMocks()); 51 | 52 | $user = $this->getUserMock(); 53 | $user->shouldReceive('getEmailForVerification')->andReturn('mail'); 54 | 55 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 56 | $table->shouldReceive('where')->once()->with('email', 'mail')->andReturn($query = $this->getQueryBuilderMock()); 57 | $query->shouldReceive('where')->once()->with('token', 'token')->andReturn( 58 | $entryQuery = $this->getQueryBuilderMock() 59 | ); 60 | $entryQuery->shouldReceive('first')->andReturnNull(); 61 | 62 | $this->assertFalse($repository->exists($user, 'token')); 63 | } 64 | 65 | public function testExistsOnExpiredToken() 66 | { 67 | $mocks = $this->getMocks(); 68 | $mocks['expires'] = 1; 69 | 70 | $repository = $this->getRepository($mocks); 71 | 72 | $user = $this->getUserMock(); 73 | $user->shouldReceive('getEmailForVerification')->andReturn('mail'); 74 | 75 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 76 | $table->shouldReceive('where')->once()->with('email', 'mail')->andReturn($query = $this->getQueryBuilderMock()); 77 | $query->shouldReceive('where')->once()->with('token', 'token')->andReturn( 78 | $entryQuery = $this->getQueryBuilderMock() 79 | ); 80 | $entryQuery->shouldReceive('first')->andReturn(['created_at' => (new Carbon('-1 day'))->format('Y-m-d H:i:s')]); 81 | 82 | $this->assertFalse($repository->exists($user, 'token')); 83 | } 84 | 85 | public function testExistsOnNonExpiredToken() 86 | { 87 | $mocks = $this->getMocks(); 88 | $mocks['expires'] = 100; 89 | 90 | $repository = $this->getRepository($mocks); 91 | 92 | $user = $this->getUserMock(); 93 | $user->shouldReceive('getEmailForVerification')->andReturn('mail'); 94 | 95 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 96 | $table->shouldReceive('where')->once()->with('email', 'mail')->andReturn($query = $this->getQueryBuilderMock()); 97 | $query->shouldReceive('where')->once()->with('token', 'token')->andReturn( 98 | $entryQuery = $this->getQueryBuilderMock() 99 | ); 100 | $entryQuery->shouldReceive('first')->andReturn(['created_at' => (new Carbon())->format('Y-m-d H:i:s')]); 101 | 102 | $this->assertTrue($repository->exists($user, 'token')); 103 | } 104 | 105 | public function testDeleteShouldDeleteToken() 106 | { 107 | $repository = $this->getRepository($mocks = $this->getMocks()); 108 | 109 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 110 | $table->shouldReceive('where')->once()->with('token', 'token')->andReturn($query = $this->getQueryBuilderMock()); 111 | $query->shouldReceive('delete')->once(); 112 | 113 | $repository->delete('token'); 114 | } 115 | 116 | public function testDeleteShouldDeleteExpiredTokens() 117 | { 118 | $mocks = $this->getMocks(); 119 | $mocks['expires'] = 100; 120 | $repository = $this->getRepository($mocks); 121 | 122 | $mocks['connection']->shouldReceive('table')->andReturn($table = $this->getQueryBuilderMock()); 123 | $table->shouldReceive('where')->once()->andReturn($query = $this->getQueryBuilderMock()); 124 | $query->shouldReceive('delete')->once(); 125 | 126 | $repository->deleteExpired(); 127 | } 128 | 129 | public function testDeleteShouldSkipDeleteExpiredIfDisabled() 130 | { 131 | $repository = $this->getRepository($mocks = $this->getMocks()); 132 | 133 | $mocks['connection']->shouldReceive('table')->never(); 134 | 135 | $repository->deleteExpired(); 136 | } 137 | 138 | protected function getRepository($mocks) 139 | { 140 | return new DatabaseTokenRepository($mocks['connection'], $mocks['table'], $mocks['hashKey'], $mocks['expires']); 141 | } 142 | 143 | protected function getMocks() 144 | { 145 | $mocks = [ 146 | 'connection' => m::mock('Illuminate\Database\ConnectionInterface'), 147 | 'table' => 'verifications', 148 | 'hashKey' => 'hasKey', 149 | 'expires' => 0, 150 | ]; 151 | 152 | return $mocks; 153 | } 154 | 155 | protected function getUserMock() 156 | { 157 | return m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification'); 158 | } 159 | 160 | protected function getQueryBuilderMock() 161 | { 162 | return m::mock('Illuminate\Database\Query\Builder'); 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /tests/RedirectsUsersTest.php: -------------------------------------------------------------------------------- 1 | getObjectForTrait('Krucas\LaravelUserEmailVerification\RedirectsUsers'); 18 | $trait->verificationRedirectPath = 'pathFrom'; 19 | 20 | $this->assertEquals('pathFrom', $trait->verificationRedirectPath()); 21 | } 22 | 23 | public function testVerificationRedirectToFromProperty() 24 | { 25 | $trait = $this->getObjectForTrait('Krucas\LaravelUserEmailVerification\RedirectsUsers'); 26 | $trait->verificationRedirectTo = 'pathTo'; 27 | 28 | $this->assertEquals('pathTo', $trait->verificationRedirectPath()); 29 | } 30 | 31 | public function testVerificationRedirectPathFromRoute() 32 | { 33 | $this->laravelContainer->shouldReceive('make')->with('url', [])->andReturn($url = $this->getUrlMock()); 34 | $url->shouldReceive('route')->andReturn('routePath'); 35 | 36 | $trait = $this->getObjectForTrait('Krucas\LaravelUserEmailVerification\RedirectsUsers'); 37 | 38 | $this->assertEquals('routePath', $trait->verificationRedirectPath()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tests/RegistersUsersTest.php: -------------------------------------------------------------------------------- 1 | getTraitMock(['validator', 'create', 'verificationRedirectPath']); 20 | $trait->expects($this->once())->method('validator')->will($this->returnValue( 21 | $validator = m::mock('stdClass') 22 | )); 23 | $trait->expects($this->once())->method('verificationRedirectPath')->will($this->returnValue('verifyPath')); 24 | $validator->shouldReceive('fails')->once()->andReturn(false); 25 | 26 | $request = $this->getRequestMock(); 27 | $request->shouldReceive('all')->andReturn(['email' => 'mail']); 28 | $request->shouldReceive('only')->with('email')->andReturn(['email' => 'mail']); 29 | 30 | Verification::shouldReceive('broker')->once()->andReturn($broker = $this->getBrokerMock()); 31 | $broker->shouldReceive('sendVerificationLink')->once(); 32 | 33 | $this->laravelContainer->shouldReceive('make')->with('config', [])->andReturn($config = $this->getConfigMock()); 34 | $config->shouldReceive('get')->with('verification.verify', null)->andReturn(true); 35 | 36 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 37 | $redirect = $this->getRedirectMock() 38 | ); 39 | $redirect->shouldReceive('to')->once()->with('verifyPath', 302, [], null)->andReturn('redirect'); 40 | 41 | $this->assertEquals('redirect', $trait->register($request)); 42 | } 43 | 44 | public function testRegisterShouldRedirectToRedirectPath() 45 | { 46 | 47 | $trait = $this->getTraitMock(['validator', 'create', 'redirectPath']); 48 | $trait->expects($this->once())->method('validator')->will($this->returnValue( 49 | $validator = m::mock('stdClass') 50 | )); 51 | $trait->expects($this->once())->method('redirectPath')->will($this->returnValue('redirectPath')); 52 | $validator->shouldReceive('fails')->once()->andReturn(false); 53 | 54 | $request = $this->getRequestMock(); 55 | $request->shouldReceive('all')->andReturn(['email' => 'mail']); 56 | $request->shouldReceive('only')->with('email')->andReturn(['email' => 'mail']); 57 | 58 | Verification::shouldReceive('broker')->once()->andReturn($broker = $this->getBrokerMock()); 59 | $broker->shouldReceive('sendVerificationLink')->once(); 60 | 61 | $this->laravelContainer->shouldReceive('make')->with('config', [])->andReturn($config = $this->getConfigMock()); 62 | $config->shouldReceive('get')->with('verification.verify', null)->andReturn(false); 63 | 64 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 65 | $redirect = $this->getRedirectMock() 66 | ); 67 | $redirect->shouldReceive('to')->once()->with('redirectPath', 302, [], null)->andReturn('redirect'); 68 | 69 | Auth::shouldReceive('guard')->andReturn($guard = $this->getGuardMock()); 70 | $guard->shouldReceive('login')->once(); 71 | 72 | $this->assertEquals('redirect', $trait->register($request)); 73 | } 74 | 75 | protected function getTraitMock($methods = []) 76 | { 77 | return $this->getMockForTrait( 78 | 'Krucas\LaravelUserEmailVerification\RegistersUsers', 79 | [], 80 | '', 81 | true, 82 | true, 83 | true, 84 | $methods 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | laravelContainer = m::mock('Illuminate\Contracts\Container\Container'); 23 | Container::setInstance($this->laravelContainer); 24 | } 25 | 26 | protected function getConfigMock() 27 | { 28 | return m::mock('Illuminate\Contracts\Config\Repository'); 29 | } 30 | 31 | protected function getRedirectMock() 32 | { 33 | return m::mock('Illuminate\Routing\Redirector'); 34 | } 35 | 36 | protected function getUrlMock() 37 | { 38 | return m::mock('Illuminate\Contracts\Routing\UrlGenerator'); 39 | } 40 | 41 | protected function getRequestMock() 42 | { 43 | return m::mock('Illuminate\Http\Request'); 44 | } 45 | 46 | protected function getUserMock() 47 | { 48 | return m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification'); 49 | } 50 | 51 | protected function getBrokerMock() 52 | { 53 | return m::mock('Krucas\LaravelUserEmailVerification\Contracts\VerificationBroker'); 54 | } 55 | 56 | protected function getGuardMock() 57 | { 58 | return m::mock('Illuminate\Contracts\Auth\Guard'); 59 | } 60 | 61 | protected function getViewMock() 62 | { 63 | return m::mock('Illuminate\Contracts\View\Factory'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/VerificationBrokerTest.php: -------------------------------------------------------------------------------- 1 | getMocks(); 18 | $broker = m::mock('Krucas\LaravelUserEmailVerification\VerificationBroker[getUser]', array_values($mocks)); 19 | $broker->shouldReceive('getUser')->once()->andReturnNull(); 20 | $this->assertEquals(VerificationBroker::INVALID_USER, $broker->sendVerificationLink(['credentials'])); 21 | } 22 | 23 | /** 24 | * @expectedException \UnexpectedValueException 25 | */ 26 | public function testGetUserThrowsExceptionIfUserDoesntImplementRequiresEmailVerification() 27 | { 28 | $broker = $this->getBroker($mocks = $this->getMocks()); 29 | $mocks['users']->shouldReceive('retrieveByCredentials')->once()->with(['foo'])->andReturn('bar'); 30 | $broker->getUser(['foo']); 31 | } 32 | 33 | public function testUserIsRetrievedByCredentials() 34 | { 35 | $broker = $this->getBroker($mocks = $this->getMocks()); 36 | $mocks['users']->shouldReceive('retrieveByCredentials')->once()->with(['foo'])->andReturn( 37 | $user = m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification') 38 | ); 39 | $this->assertEquals($user, $broker->getUser(['foo'])); 40 | } 41 | 42 | public function testBrokerCreatesTokenAndRedirectsWithoutError() 43 | { 44 | $mocks = $this->getMocks(); 45 | $broker = m::mock( 46 | 'Krucas\LaravelUserEmailVerification\VerificationBroker[emailVerificationLink]', 47 | array_values($mocks) 48 | ); 49 | $mocks['users']->shouldReceive('retrieveByCredentials')->once()->with(['foo'])->andReturn( 50 | $user = m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification') 51 | ); 52 | $mocks['tokens']->shouldReceive('create')->once()->with($user)->andReturn('token'); 53 | $callback = function () {}; 54 | $broker->shouldReceive('emailVerificationLink')->once()->with($user, 'token', $callback); 55 | $this->assertEquals(VerificationBroker::VERIFICATION_LINK_SENT, $broker->sendVerificationLink( 56 | ['foo'], 57 | $callback 58 | )); 59 | } 60 | 61 | public function testMailerIsCalledWithProperViewTokenAndCallback() 62 | { 63 | unset($_SERVER['__verification.verify.test']); 64 | $broker = $this->getBroker($mocks = $this->getMocks()); 65 | $callback = function ($message, $user) { 66 | $_SERVER['__verification.verify.test'] = true; 67 | }; 68 | $user = m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification'); 69 | $mocks['mailer']->shouldReceive('send')->once()->with( 70 | 'verifyAccountView', 71 | ['token' => 'token', 'user' => $user], 72 | m::type('Closure') 73 | )->andReturnUsing(function ($view, $data, $callback) { 74 | return $callback; 75 | }); 76 | $user->shouldReceive('getEmailForVerification')->once()->andReturn('email'); 77 | $message = m::mock('StdClass'); 78 | $message->shouldReceive('to')->once()->with('email'); 79 | $result = $broker->emailVerificationLink($user, 'token', $callback); 80 | call_user_func($result, $message); 81 | $this->assertTrue($_SERVER['__verification.verify.test']); 82 | } 83 | 84 | public function testRedirectIsReturnedByVerificationWhenUserCredentialsInvalid() 85 | { 86 | $broker = $this->getBroker($mocks = $this->getMocks()); 87 | $mocks['users']->shouldReceive('retrieveByCredentials')->once()->with(['creds'])->andReturn(null); 88 | $this->assertEquals(VerificationBroker::INVALID_USER, $broker->verify(['creds'], function () {})); 89 | } 90 | 91 | public function testRedirectReturnedByVerificationWhenRecordDoesntExistInTable() 92 | { 93 | $creds = ['token' => 'token']; 94 | $broker = $this->getBroker($mocks = $this->getMocks()); 95 | $mocks['users'] 96 | ->shouldReceive('retrieveByCredentials') 97 | ->once() 98 | ->with(array_except($creds, ['token'])) 99 | ->andReturn($user = m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification')); 100 | $mocks['tokens']->shouldReceive('exists')->with($user, 'token')->andReturn(false); 101 | $this->assertEquals(VerificationBroker::INVALID_TOKEN, $broker->verify($creds, function () {})); 102 | } 103 | 104 | public function testVerificationRemovesRecordOnTokenTableAndCallsCallback() 105 | { 106 | unset($_SERVER['__verification.verify.test']); 107 | $broker = $this->getMock( 108 | 'Krucas\LaravelUserEmailVerification\VerificationBroker', 109 | ['validateVerification'], 110 | array_values($mocks = $this->getMocks()) 111 | ); 112 | $broker 113 | ->expects($this->once()) 114 | ->method('validateVerification') 115 | ->will( 116 | $this->returnValue( 117 | $user = m::mock('Krucas\LaravelUserEmailVerification\Contracts\RequiresEmailVerification') 118 | ) 119 | ); 120 | $mocks['tokens']->shouldReceive('delete')->once()->with('token'); 121 | $callback = function ($user) { 122 | $_SERVER['__verification.verify.test'] = compact('user'); 123 | return 'foo'; 124 | }; 125 | $this->assertEquals(VerificationBroker::VERIFIED, $broker->verify(['token' => 'token'], $callback)); 126 | $this->assertEquals(['user' => $user], $_SERVER['__verification.verify.test']); 127 | } 128 | 129 | protected function getBroker($mocks) 130 | { 131 | return new VerificationBroker( 132 | $mocks['tokens'], 133 | $mocks['users'], 134 | $mocks['mailer'], 135 | $mocks['view'] 136 | ); 137 | } 138 | 139 | protected function getMocks() 140 | { 141 | $mocks = [ 142 | 'tokens' => m::mock('Krucas\LaravelUserEmailVerification\Contracts\TokenRepositoryInterface'), 143 | 'users' => m::mock('Illuminate\Contracts\Auth\UserProvider'), 144 | 'mailer' => m::mock('Illuminate\Contracts\Mail\Mailer'), 145 | 'view' => 'verifyAccountView', 146 | ]; 147 | 148 | return $mocks; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /tests/VerifiesUsersTest.php: -------------------------------------------------------------------------------- 1 | getTraitMock(); 22 | 23 | $this->laravelContainer->shouldReceive('make')->with(Factory::class, [])->andReturn( 24 | $view = $this->getViewMock() 25 | ); 26 | $view->shouldReceive('make')->with('verification::auth.verification.message', [], [])->andReturn('view'); 27 | 28 | $this->assertEquals('view', $trait->getVerify($this->getRequestMock(), null)); 29 | } 30 | 31 | public function testVerifyShouldShowSuccessForm() 32 | { 33 | $trait = $this->getTraitMock(['verificationRedirectPath']); 34 | $trait->expects($this->once())->method('verificationRedirectPath')->will($this->returnValue('path')); 35 | 36 | Verification::shouldReceive('broker')->andReturn($broker = $this->getBrokerMock()); 37 | $broker->shouldReceive('verify')->once()->andReturn(VerificationBroker::VERIFIED); 38 | 39 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 40 | $redirect = $this->getRedirectMock() 41 | ); 42 | $redirect->shouldReceive('to')->once()->with('path', 302, [], null)->andReturn($response = m::mock('stdClass')); 43 | $response->shouldReceive('with')->once()->with('status', 'translated')->andReturn('redirect'); 44 | 45 | $this->laravelContainer->shouldReceive('make')->with('translator', [])->andReturn( 46 | $translator = m::mock('stdClass') 47 | ); 48 | $translator->shouldReceive('trans')->andReturn('translated'); 49 | 50 | $request = $this->getRequestMock(); 51 | $request->shouldReceive('get'); 52 | 53 | $this->assertEquals('redirect', $trait->getVerify($request, 'token')); 54 | } 55 | 56 | public function testVerifyShouldShowFailureForm() 57 | { 58 | $trait = $this->getTraitMock(); 59 | 60 | Verification::shouldReceive('broker')->andReturn($broker = $this->getBrokerMock()); 61 | $broker->shouldReceive('verify')->once()->andReturn(VerificationBroker::INVALID_TOKEN); 62 | 63 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 64 | $redirect = $this->getRedirectMock() 65 | ); 66 | $redirect->shouldReceive('route')->once()->with('verification.resend')->andReturn( 67 | $response = m::mock('stdClass') 68 | ); 69 | $response->shouldReceive('withErrors')->once()->with(['status' => 'translated'])->andReturn('redirect'); 70 | 71 | $this->laravelContainer->shouldReceive('make')->with('translator', [])->andReturn( 72 | $translator = m::mock('stdClass') 73 | ); 74 | $translator->shouldReceive('trans')->andReturn('translated'); 75 | 76 | $request = $this->getRequestMock(); 77 | $request->shouldReceive('get'); 78 | 79 | $this->assertEquals('redirect', $trait->getVerify($request, 'token')); 80 | } 81 | 82 | public function testShowResendForm() 83 | { 84 | $trait = $this->getTraitMock(); 85 | 86 | $this->laravelContainer->shouldReceive('make')->with(Factory::class, [])->andReturn( 87 | $view = $this->getViewMock() 88 | ); 89 | $view->shouldReceive('make')->with('verification::auth.verification.resend', [], [])->andReturn('view'); 90 | 91 | $this->assertEquals('view', $trait->getResend()); 92 | } 93 | 94 | public function testSendVerificationLinkShowSuccess() 95 | { 96 | $trait = $this->getTraitMock(['validate']); 97 | $trait->expects($this->once())->method('validate'); 98 | 99 | $request = $this->getRequestMock(); 100 | $request->shouldReceive('only')->with('email')->andReturn(['email' => 'mail']); 101 | 102 | Verification::shouldReceive('broker')->andReturn($broker = $this->getBrokerMock()); 103 | $broker->shouldReceive('sendVerificationLink')->once()->andReturn(VerificationBroker::VERIFICATION_LINK_SENT); 104 | 105 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 106 | $redirect = $this->getRedirectMock() 107 | ); 108 | $redirect->shouldReceive('back')->andReturn($response = m::mock('stdClass')); 109 | $response->shouldReceive('with')->with('status', 'translated')->andReturn('redirect'); 110 | 111 | $this->laravelContainer->shouldReceive('make')->with('translator', [])->andReturn( 112 | $translator = m::mock('stdClass') 113 | ); 114 | $translator->shouldReceive('trans')->andReturn('translated'); 115 | 116 | $this->assertEquals('redirect', $trait->postResend($request)); 117 | } 118 | 119 | public function testSendVerificationLinkShowFailure() 120 | { 121 | $trait = $this->getTraitMock(['validate']); 122 | $trait->expects($this->once())->method('validate'); 123 | 124 | $request = $this->getRequestMock(); 125 | $request->shouldReceive('only')->with('email')->andReturn(['email' => 'mail']); 126 | 127 | Verification::shouldReceive('broker')->andReturn($broker = $this->getBrokerMock()); 128 | $broker->shouldReceive('sendVerificationLink')->once()->andReturn(VerificationBroker::INVALID_USER); 129 | 130 | $this->laravelContainer->shouldReceive('make')->with('redirect', [])->andReturn( 131 | $redirect = $this->getRedirectMock() 132 | ); 133 | $redirect->shouldReceive('back')->andReturn($response = m::mock('stdClass')); 134 | $response->shouldReceive('withErrors')->with(['email' => 'translated'])->andReturn('redirect'); 135 | 136 | $this->laravelContainer->shouldReceive('make')->with('translator', [])->andReturn( 137 | $translator = m::mock('stdClass') 138 | ); 139 | $translator->shouldReceive('trans')->andReturn('translated'); 140 | 141 | $this->assertEquals('redirect', $trait->postResend($request)); 142 | } 143 | 144 | protected function getTraitMock($methods = []) 145 | { 146 | return $this->getMockForTrait( 147 | 'Krucas\LaravelUserEmailVerification\VerifiesUsers', 148 | [], 149 | '', 150 | true, 151 | true, 152 | true, 153 | $methods 154 | ); 155 | } 156 | } 157 | --------------------------------------------------------------------------------