├── .gitattributes ├── .gitignore ├── README.md ├── composer.json ├── src ├── Helpers │ ├── CanResetPassword.php │ └── TokenHandler.php ├── Http │ ├── Controllers │ │ └── PasswordController.php │ └── Requests │ │ ├── RecoverPasswordRequest.php │ │ └── ResetPasswordRequest.php ├── Jobs │ ├── SendRecoveredPasswordJob.php │ └── SendResetPasswordTokenJob.php ├── Mails │ ├── PasswordRecoveredMail.php │ └── RestPasswordTokenMail.php ├── Rules │ └── UserExists.php ├── SimplePassportServiceProvider.php ├── Token.php ├── config │ └── simple-passport.php ├── database │ └── migrations │ │ └── 2019_04_06_105400_create_simple_tokens_table.php ├── resources │ ├── lang │ │ └── en │ │ │ ├── forgot-password.php │ │ │ └── recover-password.php │ └── views │ │ ├── forgot-password.blade.php │ │ └── recover-password.blade.php └── routes │ └── api.php └── tests └── GenerateTokenTest.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Due to a time constraint, unfortunately this repository is no longer maintained.

2 |
3 | 4 | # Installation 5 | 1/ Install Laravel passport official package, explained in the documentation [here](https://laravel.com/docs/5.8/passport#installation) 6 | Then install the simple-passport package via composer 7 | 8 | composer require heloufir/simple-passport 9 | 10 | # Configuration 11 | 1/ After installing the package, you need to publish it, by running the command: 12 | 13 | To do it, simply execute the following command 14 | 15 | php artisan vendor:publish --provider=Heloufir\SimplePassport\SimplePassportServiceProvider 16 | 17 | Or follow the steps below: 18 | 19 | php artisan vendor:publish 20 | 21 | You will be asked to choose what **tag** you want to publish (see the image below) 22 | 23 | ![Publish heloufir/simple-passport package](https://lh3.googleusercontent.com/-gmOs-xKPf9I/XG65d1UKb0I/AAAAAAAAEpk/SUOSSF-Mj7AwydQWc8HkvIIIluGg5pXmwCLcBGAs/s0/Publish+simple-passport.png "Publish simple-passport.png") 24 | 25 | Next you just need to choose the tag number to publish and tap **Enter**. 26 | 27 | > For my case I choosed **3** then **Enter** 28 | 29 | ![Package heloufir/simple-passport published](https://lh3.googleusercontent.com/-iUEq5k_GwhM/XG66QrxTV9I/AAAAAAAAEp0/gtScqMDmGy0BamsJe9qik3PdCA3JF7-SACLcBGAs/s0/Package+published.png "Package published.png") 30 | 31 | This is the message you need to see if everything is OK. 32 | 33 | 2/ Now you need to configure the **laravel/passport** package, do it by following the below steps: 34 | 35 | Execute the migration command: 36 | 37 | php artisan migrate 38 | 39 | This will show you the following message: 40 | 41 | Migrating: 2019_04_06_105400_create_simple_tokens_table 42 | Migrated: 2019_04_06_105400_create_simple_tokens_table 43 | 44 | After running this command, add the **Heloufir\SimplePassport\Helpers\CanResetPassword** trait to your **YOUR_NAMESPACE\User** model. This trait will provide a few helper methods: 45 | 46 | ```php 47 | You can override two methods **getEmailField** and **getPasswordField** for providing the names of the fields 63 | 64 | 3/ A configuration file **config/simple-passport.php** will be published. its containing the information such as the user model and more things. 65 | ```php 66 | return [ 67 | 68 | /* 69 | |-------------------------------------------------------------------------- 70 | | Recover url 71 | |-------------------------------------------------------------------------- 72 | | 73 | | This value is the recover password url, where the user will be redirected 74 | | after he clicked on the forgot password email button. 75 | | >> To customize this value please set a new variable into the application 76 | | .env file with the following name: "SP_RECOVER_URL" 77 | | 78 | */ 79 | 'recover_url' => env('SP_RECOVER_URL', 'http://localhost:4200/auth/recover/'), 80 | 81 | /* 82 | |-------------------------------------------------------------------------- 83 | | Mail sender 84 | |-------------------------------------------------------------------------- 85 | | 86 | | This value is the address of the sender, which be displayed in the mail 87 | | sent to the user after he request to recover his password or after his 88 | | password is recovered 89 | | >> To customize this value please set a new variable into the application 90 | | .env file with the following name: "SP_MAIL_FROM" 91 | | 92 | */ 93 | 'mail_from' => env('SP_MAIL_FROM', 'noreply@application.com'), 94 | 95 | /* 96 | |-------------------------------------------------------------------------- 97 | | Recover url 98 | |-------------------------------------------------------------------------- 99 | | 100 | | This value is the name of the send, which be displayed in the mail 101 | | sent to the user after he request to recover his password or after his 102 | | password is recovered 103 | | >> To customize this value please set a new variable into the application 104 | | .env file with the following name: "SP_MAIL_FROM_NAME" 105 | | 106 | */ 107 | 'mail_from_name' => env('SP_MAIL_FROM_NAME', 'Application'), 108 | 109 | /* 110 | |-------------------------------------------------------------------------- 111 | | model 112 | |-------------------------------------------------------------------------- 113 | | 114 | | The model that can use simple-passport features 115 | | 116 | */ 117 | 118 | 'model' => \App\User::class, 119 | 120 | /* 121 | |-------------------------------------------------------------------------- 122 | | after_seconds 123 | |-------------------------------------------------------------------------- 124 | | 125 | | How many seconds before dispatch the jobs to send mails 126 | | 127 | */ 128 | 129 | 'after_seconds' => 10, 130 | ]; 131 | 132 | ``` 133 | 134 | 135 | It's done for the **laravel/passport** configuration, the rest of the configuration is done in the **heloufir/simple-passport** side. 136 | 137 | > So from here you are ready to use **laravel/passport** and **heloufir/simple-passport** packages. 138 | 139 | # Usage 140 | ## Generate token 141 | 1/ You can generate a token for an existing user via a POST HTTP request to http://localhost/oauth/forgot-password containing an **email** field. 142 | 143 | 2/ You can recover the password for an existing user via a PUT HTTP request to http://localhost/oauth/recover-password/some-random-token containing an **email** and new **password** field. 144 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heloufir/simple-passport", 3 | "description": "Simple passport, is a complete implementation of laravel/passport package, containing authentication, forgot password, recovery password, ... and all what you need to start your application that needs a complete authentication system", 4 | "type": "package", 5 | "require": { 6 | "laravel/passport": "^7.2" 7 | }, 8 | "require-dev": { 9 | "fzaninotto/faker": "~1.4", 10 | "phpunit/phpunit": "~4.0" 11 | }, 12 | "license": "MIT", 13 | "authors": [ 14 | { 15 | "name": "EL OUFIR Hatim", 16 | "email": "eloufirhatim@gmail.com" 17 | } 18 | ], 19 | "extra": { 20 | "laravel": { 21 | "providers": [ 22 | "Heloufir\\SimplePassport\\SimplePassportServiceProvider" 23 | ] 24 | } 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Heloufir\\SimplePassport\\": "src/" 29 | } 30 | }, 31 | "minimum-stability": "dev" 32 | } 33 | -------------------------------------------------------------------------------- /src/Helpers/CanResetPassword.php: -------------------------------------------------------------------------------- 1 | hasOne(Token::class); 21 | } 22 | 23 | /** 24 | * Get the email field 25 | * 26 | * @return string 27 | */ 28 | protected static function getEmailField(): string 29 | { 30 | return 'email'; 31 | } 32 | 33 | 34 | /** 35 | * Get the password field 36 | * 37 | * @return string 38 | */ 39 | protected function getPasswordField(): string 40 | { 41 | return 'password'; 42 | } 43 | 44 | /** 45 | * Get the generated token 46 | * 47 | * @return mixed 48 | */ 49 | public function getResetPasswordToken() 50 | { 51 | return $this->simpleTokens->token; 52 | } 53 | 54 | /** 55 | * Set the new password 56 | * 57 | * @param $password 58 | * @return $this 59 | */ 60 | public function setNewPassword($password) 61 | { 62 | $this->{$this->getPasswordField()} = bcrypt($password); 63 | 64 | $this->save(); 65 | 66 | return $this; 67 | } 68 | 69 | /** 70 | * Delete the token after setting up the new password 71 | * 72 | */ 73 | public function forgotToken() 74 | { 75 | $this->simpleTokens()->delete(); 76 | } 77 | 78 | /** 79 | * Token Handler 80 | * 81 | * @param $token 82 | * @return TokenHandler 83 | */ 84 | public function simpleToken($token = null) 85 | { 86 | Token::where('user_id', $this->id)->delete(); 87 | if(is_null($token)){ 88 | return new TokenHandler(Str::random(40), $this); 89 | } 90 | 91 | return new TokenHandler($token, $this); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Helpers/TokenHandler.php: -------------------------------------------------------------------------------- 1 | token = $token; 19 | $this->user = $user; 20 | } 21 | 22 | /** 23 | * Check if the token belongs to the user 24 | * 25 | * @return bool 26 | */ 27 | public function belongs() 28 | { 29 | return optional($this->user->simpleTokens)->token === $this->token; 30 | } 31 | 32 | /** 33 | * Generate the password token 34 | * 35 | * @return mixed 36 | */ 37 | public function generateResetPassword() 38 | { 39 | return $this->user->simpleTokens()->save( 40 | new Token([ 41 | 'token' => $this->token 42 | ]) 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Http/Controllers/PasswordController.php: -------------------------------------------------------------------------------- 1 | getRelatedUser($request); 24 | $user->simpleToken()->generateResetPassword(); 25 | $this->dispatchJobWithDelay( 26 | SendResetPasswordTokenJob::class, $user 27 | ); 28 | return response()->json(['mail_sent' => true, 'errors' => []], 200); 29 | } 30 | 31 | /** 32 | * Recover the password 33 | * 34 | * @param RecoverPasswordRequest $request 35 | * @param string $token 36 | * @return JsonResponse 37 | */ 38 | public function recover(RecoverPasswordRequest $request, string $token): JsonResponse 39 | { 40 | $user = $this->getRelatedUser($request); 41 | Log::alert($user->simpleTokens); 42 | if ($user->simpleTokens == null || $user->simpleTokens->token != $token) { 43 | return response()->json([ 44 | 'password_recovered' => false, 45 | 'error' => 'Token incorrect' 46 | ], 401); 47 | } 48 | $user->setNewPassword($request->get('password')) 49 | ->forgotToken(); 50 | $this->dispatchJobWithDelay( 51 | SendRecoveredPasswordJob::class, $user 52 | ); 53 | return response()->json(['password_recovered' => true, 'errors' => []], 200); 54 | } 55 | 56 | 57 | /** 58 | * Get the related user 59 | * 60 | * @param $request 61 | * @return mixed 62 | */ 63 | protected function getRelatedUser($request) 64 | { 65 | return $request->user_asked; 66 | } 67 | 68 | /** 69 | * @param $class 70 | * @param $user 71 | * @return mixed 72 | */ 73 | protected function dispatchJobWithDelay($class, $user) 74 | { 75 | return $class::dispatch( 76 | $user 77 | )->delay( 78 | now()->addSecond(config('simple-passport.after_seconds')) 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/Http/Requests/RecoverPasswordRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new UserExists], 29 | 'password' => 'required|confirmed' 30 | ]; 31 | } 32 | 33 | /** 34 | * Validation of the the request and attach user to it 35 | * 36 | */ 37 | public function validateResolved() 38 | { 39 | parent::validateResolved(); 40 | 41 | $class = config('simple-passport.model'); 42 | $model = app($class); 43 | request()->request->add([ 44 | 'user_asked' => $class::where($model::getEmailField(), '=', $this->request->get('email'))->first() 45 | ]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Http/Requests/ResetPasswordRequest.php: -------------------------------------------------------------------------------- 1 | ['required', new UserExists] 29 | ]; 30 | } 31 | 32 | /** 33 | * Validation of the the request and attach user to it 34 | * 35 | */ 36 | public function validateResolved() 37 | { 38 | parent::validateResolved(); 39 | 40 | $class = config('simple-passport.model'); 41 | $model = app($class); 42 | request()->request->add([ 43 | 'user_asked' => $class::where($model::getEmailField(), '=', $this->request->get('email'))->first() 44 | ]); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Jobs/SendRecoveredPasswordJob.php: -------------------------------------------------------------------------------- 1 | user = $user; 30 | } 31 | 32 | /** 33 | * Execute the job. 34 | * 35 | * @return void 36 | */ 37 | public function handle() 38 | { 39 | Mail::to($this->user)->send( 40 | new PasswordRecoveredMail($this->user) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Jobs/SendResetPasswordTokenJob.php: -------------------------------------------------------------------------------- 1 | user = $user; 30 | } 31 | 32 | /** 33 | * Execute the job. 34 | * 35 | * @return void 36 | */ 37 | public function handle() 38 | { 39 | Mail::to($this->user)->send( 40 | new RestPasswordTokenMail($this->user) 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Mails/PasswordRecoveredMail.php: -------------------------------------------------------------------------------- 1 | user = $user; 27 | } 28 | 29 | /** 30 | * Build the message. 31 | * 32 | * @return $this 33 | */ 34 | public function build() 35 | { 36 | return $this->view('simple-passport.recover-password') 37 | ->with(['user' => $this->user]) 38 | ->from(config('simple-passport.mail_from'), config('simple-passport.mail_from_name')) 39 | ->subject(trans('simple-passport::recover-password.mail_subject')); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Mails/RestPasswordTokenMail.php: -------------------------------------------------------------------------------- 1 | user = $user; 27 | } 28 | 29 | /** 30 | * Build the message. 31 | * 32 | * @return $this 33 | */ 34 | public function build() 35 | { 36 | return $this->view('simple-passport.forgot-password') 37 | ->with(['user' => $this->user, 'token' => $this->user->simpleTokens->token]) 38 | ->from(config('simple-passport.mail_from'), config('simple-passport.mail_from_name')) 39 | ->subject(trans('simple-passport::forgot-password.mail_subject')); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Rules/UserExists.php: -------------------------------------------------------------------------------- 1 | model = app(config('auth.providers.users.model')); 14 | } 15 | 16 | /** 17 | * Determine if the validation rule passes. 18 | * 19 | * @param string $attribute 20 | * @param mixed $value 21 | * @return bool 22 | */ 23 | public function passes($attribute, $value) 24 | { 25 | return $this->model->where(($this->model->simplePassport ?: 'email'), '=', $value)->count() !== 0; 26 | } 27 | 28 | /** 29 | * Get the validation error message. 30 | * 31 | * @return string 32 | */ 33 | public function message() 34 | { 35 | return trans('validation.exists', ['attribute' => 'user']); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SimplePassportServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->make(PasswordController::class); 20 | } 21 | 22 | /** 23 | * Bootstrap services. 24 | * 25 | * @return void 26 | */ 27 | public function boot() 28 | { 29 | // Register laravel/passport routes 30 | Passport::routes(); 31 | 32 | // Register package routes 33 | $this->loadRoutesFrom(__DIR__ . '/routes/api.php'); 34 | 35 | // Register package migrations 36 | $this->loadMigrationsFrom(__DIR__ . '/database/migrations'); 37 | 38 | // Register package views 39 | $this->loadViewsFrom(__DIR__.'/resources/views', 'simple-passport'); 40 | 41 | // Register package translations 42 | $this->loadTranslationsFrom(__DIR__.'/resources/lang', 'simple-passport'); 43 | 44 | // Publish package sources 45 | $this->publishes([ 46 | __DIR__.'/resources/views' => resource_path('views/simple-passport'), 47 | __DIR__.'/config/simple-passport.php' => config_path('simple-passport.php'), 48 | __DIR__.'/resources/lang' => resource_path('lang/vendor/simple-passport') 49 | ]); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Token.php: -------------------------------------------------------------------------------- 1 | > To customize this value please set a new variable into the application 13 | | .env file with the following name: "SP_RECOVER_URL" 14 | | 15 | */ 16 | 'recover_url' => env('SP_RECOVER_URL', 'http://localhost:4200/auth/recover/'), 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Mail sender 21 | |-------------------------------------------------------------------------- 22 | | 23 | | This value is the address of the sender, which be displayed in the mail 24 | | sent to the user after he request to recover his password or after his 25 | | password is recovered 26 | | >> To customize this value please set a new variable into the application 27 | | .env file with the following name: "SP_MAIL_FROM" 28 | | 29 | */ 30 | 'mail_from' => env('SP_MAIL_FROM', 'noreply@application.com'), 31 | 32 | /* 33 | |-------------------------------------------------------------------------- 34 | | Recover url 35 | |-------------------------------------------------------------------------- 36 | | 37 | | This value is the name of the send, which be displayed in the mail 38 | | sent to the user after he request to recover his password or after his 39 | | password is recovered 40 | | >> To customize this value please set a new variable into the application 41 | | .env file with the following name: "SP_MAIL_FROM_NAME" 42 | | 43 | */ 44 | 'mail_from_name' => env('SP_MAIL_FROM_NAME', 'Application'), 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | model 49 | |-------------------------------------------------------------------------- 50 | | 51 | | The model that can use simple-passport features 52 | | 53 | */ 54 | 55 | 'model' => \App\User::class, 56 | 57 | /* 58 | |-------------------------------------------------------------------------- 59 | | after_seconds 60 | |-------------------------------------------------------------------------- 61 | | 62 | | How many seconds before dispatch the jobs to send mails 63 | | 64 | */ 65 | 66 | 'after_seconds' => 10, 67 | ]; 68 | -------------------------------------------------------------------------------- /src/database/migrations/2019_04_06_105400_create_simple_tokens_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id'); 19 | $table->string('token'); 20 | $table->string('expires_at')->nullable(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('simple_tokens'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/resources/lang/en/forgot-password.php: -------------------------------------------------------------------------------- 1 | 'Forgot your password?', 5 | 'title' => 'Forgot your password?', 6 | 'notification_description' => 'Forgot your password? No worries, take a moment and follow the steps described in this email to recover your password.', 7 | 'description' => 'No worries! Take a moment and follow the steps described in this email to recover your password' 8 | . '
First of all click on the following button:', 9 | 'button' => 'Recover your password' 10 | ]; -------------------------------------------------------------------------------- /src/resources/lang/en/recover-password.php: -------------------------------------------------------------------------------- 1 | 'Password recovered', 5 | 'title' => 'Password recovered', 6 | 'notification_description' => 'Congrats! Your password was successfully recovered. You can now log on using your new password.', 7 | 'description' => 'Congrats! Your password was successfully recovered. You can now log on using your new password.' 8 | ]; -------------------------------------------------------------------------------- /src/resources/views/forgot-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | @lang('simple-passport::forgot-password.title') 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 130 | 131 | 132 | 140 | 141 | 142 | 143 | 144 | 197 | 198 | 199 | 200 | 206 | 207 |
208 | 213 | 214 | 215 |
216 | @lang('simple-passport::forgot-password.notification_description') 217 |
218 | 219 | 220 | 221 | 222 |
223 | ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌  224 |
225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 235 | 236 | 237 | 238 | 239 | 240 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 275 |
276 | 277 | 278 | -------------------------------------------------------------------------------- /src/resources/views/recover-password.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | @lang('simple-passport::recover-password.title') 9 | 10 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 130 | 131 | 132 | 140 | 141 | 142 | 143 | 144 | 197 | 198 | 199 | 200 | 206 | 207 |
208 | 213 | 214 | 215 |
216 | @lang('simple-passport::recover-password.notification_description') 217 |
218 | 219 | 220 | 221 | 222 |
223 | ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌ ‌  224 |
225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 235 | 236 | 237 | 238 | 239 | 240 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 262 |
263 | 264 | 265 | -------------------------------------------------------------------------------- /src/routes/api.php: -------------------------------------------------------------------------------- 1 | name('simple-passport.password.forgot'); 5 | 6 | Route::put('oauth/recover-password/{token}', 'Heloufir\SimplePassport\Http\Controllers\PasswordController@recover') 7 | ->name('simple-passport.password.recover'); -------------------------------------------------------------------------------- /tests/GenerateTokenTest.php: -------------------------------------------------------------------------------- 1 |