├── phpstan.neon.dist ├── src ├── Contracts │ ├── AuthenticatableTwoFactorContract.php │ ├── PasswordResetBrokerContract.php │ ├── EmailVerificationBrokerFactoryContract.php │ ├── CanResetPasswordContract.php │ ├── CanVerifyEmailContract.php │ ├── CanVerifyPhoneContract.php │ └── EmailVerificationBrokerContract.php ├── Traits │ ├── AuthenticatableTwoFactor.php │ ├── CanResetPassword.php │ ├── HasHashables.php │ ├── CanVerifyEmail.php │ └── CanVerifyPhone.php ├── Support │ └── helpers.php ├── Console │ └── Commands │ │ └── PublishCommand.php ├── Providers │ └── AuthServiceProvider.php └── Services │ ├── PasswordResetBrokerManager.php │ ├── EmailVerificationBrokerManager.php │ ├── EmailVerificationBroker.php │ └── PasswordResetBroker.php ├── LICENSE ├── CONTRIBUTING.md ├── composer.json ├── README.md ├── CODE_OF_CONDUCT.md └── CHANGELOG.md /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - ./vendor/nunomaduro/larastan/extension.neon 3 | parameters: 4 | level: 5 5 | paths: 6 | - src 7 | -------------------------------------------------------------------------------- /src/Contracts/AuthenticatableTwoFactorContract.php: -------------------------------------------------------------------------------- 1 | two_factor; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Contracts/PasswordResetBrokerContract.php: -------------------------------------------------------------------------------- 1 | email; 15 | } 16 | 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function sendPasswordResetNotification(string $token, int $expiration): void 21 | { 22 | ! $this->passwordResetNotificationClass 23 | || $this->notify(new $this->passwordResetNotificationClass($token, $expiration)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Contracts/CanResetPasswordContract.php: -------------------------------------------------------------------------------- 1 | hashables ?? null; 17 | } 18 | 19 | /** 20 | * Set the hashables associated with the model. 21 | * 22 | * @param array $hashables 23 | * 24 | * @return $this 25 | */ 26 | public function setHashables(array $hashables) 27 | { 28 | $this->hashables = $hashables; 29 | 30 | return $this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Traits/CanVerifyEmail.php: -------------------------------------------------------------------------------- 1 | email; 15 | } 16 | 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function hasVerifiedEmail(): bool 21 | { 22 | return ! is_null($this->email_verified_at); 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function sendEmailVerificationNotification(string $token, int $expiration): void 29 | { 30 | ! $this->emailVerificationNotificationClass 31 | || $this->notify(new $this->emailVerificationNotificationClass($token, $expiration)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Contracts/CanVerifyEmailContract.php: -------------------------------------------------------------------------------- 1 | phone; 15 | } 16 | 17 | /** 18 | * {@inheritdoc} 19 | */ 20 | public function getCountryForVerification(): ?string 21 | { 22 | return $this->country_code ? country($this->country_code)->getCallingCode() : null; 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | public function hasVerifiedPhone(): bool 29 | { 30 | return ! is_null($this->phone_verified_at); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function sendPhoneVerificationNotification(string $method, bool $force): void 37 | { 38 | ! $this->phoneVerificationNotificationClass 39 | || $this->notify(new $this->phoneVerificationNotificationClass($method, $force)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2021, Rinvex LLC, 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Console/Commands/PublishCommand.php: -------------------------------------------------------------------------------- 1 | alert($this->description); 35 | 36 | collect($this->option('resource') ?: ['config'])->each(function ($resource) { 37 | $this->call('vendor:publish', ['--tag' => "rinvex/auth::{$resource}", '--force' => $this->option('force')]); 38 | }); 39 | 40 | $this->line(''); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | commands($this->commands); 33 | 34 | // Register the password reset broker manager 35 | $this->app->singleton('auth.password', function ($app) { 36 | return new PasswordResetBrokerManager($app); 37 | }); 38 | 39 | // Register the verification broker manager 40 | $this->app->singleton('rinvex.auth.emailverification', function ($app) { 41 | return new EmailVerificationBrokerManager($app); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Contracts/EmailVerificationBrokerContract.php: -------------------------------------------------------------------------------- 1 | ..` 13 | - `..` 14 | 15 | And constructed with the following guidelines: 16 | 17 | - Breaking backward compatibility bumps the major and resets the minor and patch. 18 | - New additions without breaking backward compatibility bump the minor and reset the patch. 19 | - Bug fixes and misc changes bump the patch. 20 | 21 | 22 | ## Pull Requests 23 | 24 | The pull request process differs for new features and bugs. 25 | 26 | Pull requests for bugs may be sent without creating any proposal issue. If you believe that you know of a solution for a bug that has been filed, please leave a comment detailing your proposed fix or create a pull request with the fix mentioning that issue id. 27 | 28 | 29 | ## Coding Standards 30 | 31 | This project follows the FIG PHP Standards Recommendations compliant with the [PSR-1: Basic Coding Standard](http://www.php-fig.org/psr/psr-1/), [PSR-2: Coding Style Guide](http://www.php-fig.org/psr/psr-2/) and [PSR-4: Autoloader](http://www.php-fig.org/psr/psr-4/) to ensure a high level of interoperability between shared PHP code. If you notice any compliance oversights, please send a patch via pull request. 32 | 33 | 34 | ## Feature Requests 35 | 36 | If you have a proposal or a feature request, you may create an issue with `[Proposal]` in the title. 37 | 38 | The proposal should also describe the new feature, as well as implementation ideas. The proposal will then be reviewed and either approved or denied. Once a proposal is approved, a pull request may be created implementing the new feature. 39 | 40 | 41 | ## Git Flow 42 | 43 | This project follows [Git-Flow](http://nvie.com/posts/a-successful-git-branching-model/), and as such has `master` (latest stable releases), `develop` (latest WIP development) and X.Y support branches (when there's multiple major versions). 44 | 45 | Accordingly all pull requests MUST be sent to the `develop` branch. 46 | 47 | > **Note:** Pull requests which do not follow these guidelines will be closed without any further notice. 48 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rinvex/laravel-auth", 3 | "description": "Rinvex Auth is a powerful authentication, authorization and verification package built on top of Laravel. It provides developers with Role Based Access Control, TwoFactor Authentication, Social Authentication, compatible with Laravel’s standard API and fully featured all-in-one solution out of the box.", 4 | "type": "library", 5 | "keywords": [ 6 | "authentication", 7 | "authorization", 8 | "verification", 9 | "activation", 10 | "abilities", 11 | "phone", 12 | "sms", 13 | "call", 14 | "users", 15 | "security", 16 | "twofactor", 17 | "laravel", 18 | "rinvex", 19 | "rbac", 20 | "totp", 21 | "acl", 22 | "roles" 23 | ], 24 | "license": "MIT", 25 | "homepage": "https://rinvex.com", 26 | "support": { 27 | "email": "help@rinvex.com", 28 | "issues": "https://github.com/rinvex/laravel-auth/issues", 29 | "source": "https://github.com/rinvex/laravel-auth", 30 | "docs": "https://github.com/rinvex/laravel-auth/README.md" 31 | }, 32 | "authors": [ 33 | { 34 | "name": "Rinvex LLC", 35 | "homepage": "https://rinvex.com", 36 | "email": "help@rinvex.com" 37 | }, 38 | { 39 | "name": "Abdelrahman Omran", 40 | "homepage": "https://omranic.com", 41 | "email": "me@omranic.com", 42 | "role": "Project Lead" 43 | }, 44 | { 45 | "name": "The Generous Laravel Community", 46 | "homepage": "https://github.com/rinvex/laravel-auth/contributors" 47 | } 48 | ], 49 | "require": { 50 | "php": "^8.1.0", 51 | "illuminate/console": "^10.0.0 || ^11.0.0", 52 | "illuminate/contracts": "^10.0.0 || ^11.0.0", 53 | "rinvex/countries": "^9.0.0", 54 | "rinvex/laravel-support": "^7.0.0", 55 | "symfony/console": "^6.2.0" 56 | }, 57 | "require-dev": { 58 | "codedungeon/phpunit-result-printer": "^0.32.0", 59 | "phpunit/phpunit": "^10.1.0" 60 | }, 61 | "autoload": { 62 | "files": [ 63 | "src/Support/helpers.php" 64 | ], 65 | "psr-4": { 66 | "Rinvex\\Auth\\": "src" 67 | } 68 | }, 69 | "autoload-dev": { 70 | "psr-4": { 71 | "Rinvex\\Auth\\Tests\\": "tests" 72 | } 73 | }, 74 | "scripts": { 75 | "test": "vendor/bin/phpunit" 76 | }, 77 | "config": { 78 | "sort-packages": true, 79 | "preferred-install": "dist", 80 | "optimize-autoloader": true 81 | }, 82 | "extra": { 83 | "laravel": { 84 | "providers": [ 85 | "Rinvex\\Auth\\Providers\\AuthServiceProvider" 86 | ] 87 | } 88 | }, 89 | "minimum-stability": "dev", 90 | "prefer-stable": true 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rinvex Auth 2 | 3 | **Rinvex Auth** is a powerful authentication, authorization and verification package built on top of Laravel. It provides developers with Role Based Access Control, TwoFactor Authentication, Social Authentication, compatible with Laravel’s standard API and fully featured all-in-one solution out of the box. 4 | 5 | [![Packagist](https://img.shields.io/packagist/v/rinvex/laravel-auth.svg?label=Packagist&style=flat-square)](https://packagist.org/packages/rinvex/laravel-auth) 6 | [![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/rinvex/laravel-auth.svg?label=Scrutinizer&style=flat-square)](https://scrutinizer-ci.com/g/rinvex/laravel-auth/) 7 | [![Travis](https://img.shields.io/travis/rinvex/laravel-auth.svg?label=TravisCI&style=flat-square)](https://travis-ci.org/rinvex/laravel-auth) 8 | [![StyleCI](https://styleci.io/repos/66008159/shield)](https://styleci.io/repos/66008159) 9 | [![License](https://img.shields.io/packagist/l/rinvex/laravel-auth.svg?label=License&style=flat-square)](https://github.com/rinvex/laravel-auth/blob/develop/LICENSE) 10 | 11 | 12 | ## Notice 13 | 14 | Documentation to be written! 15 | 16 | This package usually used with [rinvex/cortex](https://github.com/rinvex/cortex), so it may not be that useful alone or outside that application layer. 17 | 18 | 19 | ## Changelog 20 | 21 | Refer to the [Changelog](CHANGELOG.md) for a full history of the project. 22 | 23 | 24 | ## Support 25 | 26 | The following support channels are available at your fingertips: 27 | 28 | - [Chat on Slack](https://bit.ly/rinvex-slack) 29 | - [Help on Email](mailto:help@rinvex.com) 30 | - [Follow on Twitter](https://twitter.com/rinvex) 31 | 32 | 33 | ## Contributing & Protocols 34 | 35 | Thank you for considering contributing to this project! The contribution guide can be found in [CONTRIBUTING.md](CONTRIBUTING.md). 36 | 37 | Bug reports, feature requests, and pull requests are very welcome. 38 | 39 | - [Versioning](CONTRIBUTING.md#versioning) 40 | - [Pull Requests](CONTRIBUTING.md#pull-requests) 41 | - [Coding Standards](CONTRIBUTING.md#coding-standards) 42 | - [Feature Requests](CONTRIBUTING.md#feature-requests) 43 | - [Git Flow](CONTRIBUTING.md#git-flow) 44 | 45 | 46 | ## Security Vulnerabilities 47 | 48 | If you discover a security vulnerability within this project, please send an e-mail to [help@rinvex.com](help@rinvex.com). All security vulnerabilities will be promptly addressed. 49 | 50 | 51 | ## About Rinvex 52 | 53 | Rinvex is a software solutions startup, specialized in integrated enterprise solutions for SMEs established in Alexandria, Egypt since June 2016. We believe that our drive The Value, The Reach, and The Impact is what differentiates us and unleash the endless possibilities of our philosophy through the power of software. We like to call it Innovation At The Speed Of Life. That’s how we do our share of advancing humanity. 54 | 55 | 56 | ## License 57 | 58 | This software is released under [The MIT License (MIT)](LICENSE). 59 | 60 | (c) 2016-2022 Rinvex LLC, Some rights reserved. 61 | -------------------------------------------------------------------------------- /src/Services/PasswordResetBrokerManager.php: -------------------------------------------------------------------------------- 1 | app = $app; 35 | } 36 | 37 | /** 38 | * Attempt to get the broker from the local cache. 39 | * 40 | * @param string $name 41 | * 42 | * @return \Rinvex\Auth\Contracts\PasswordResetBrokerContract 43 | */ 44 | public function broker($name = null): PasswordResetBrokerContract 45 | { 46 | $name = $name ?: $this->getDefaultDriver(); 47 | 48 | return $this->brokers[$name] ?? $this->brokers[$name] = $this->resolve($name); 49 | } 50 | 51 | /** 52 | * Resolve the given broker. 53 | * 54 | * @param string $name 55 | * 56 | * @throws \InvalidArgumentException 57 | * 58 | * @return \Rinvex\Auth\Contracts\PasswordResetBrokerContract 59 | */ 60 | protected function resolve($name): PasswordResetBrokerContract 61 | { 62 | $config = $this->getConfig($name); 63 | 64 | if (is_null($config)) { 65 | throw new InvalidArgumentException("Password resetter [{$name}] is not defined."); 66 | } 67 | 68 | return new PasswordResetBroker( 69 | $this->app['auth']->createUserProvider($config['provider']), 70 | $this->app['config']['app.key'], 71 | $config['expire'] 72 | ); 73 | } 74 | 75 | /** 76 | * Get the password reset broker configuration. 77 | * 78 | * @param string $name 79 | * 80 | * @return array 81 | */ 82 | protected function getConfig($name): array 83 | { 84 | return $this->app['config']["auth.passwords.{$name}"]; 85 | } 86 | 87 | /** 88 | * Get the default password reset broker name. 89 | * 90 | * @return string 91 | */ 92 | public function getDefaultDriver(): string 93 | { 94 | return $this->app['config']['auth.defaults.passwords']; 95 | } 96 | 97 | /** 98 | * Set the default password reset broker name. 99 | * 100 | * @param string $name 101 | * 102 | * @return void 103 | */ 104 | public function setDefaultDriver($name): void 105 | { 106 | $this->app['config']['auth.defaults.passwords'] = $name; 107 | } 108 | 109 | /** 110 | * Dynamically call the default driver instance. 111 | * 112 | * @param string $method 113 | * @param array $parameters 114 | * 115 | * @return mixed 116 | */ 117 | public function __call($method, $parameters) 118 | { 119 | return $this->broker()->{$method}(...$parameters); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [help@rinvex.com](mailto:help@rinvex.com). All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /src/Services/EmailVerificationBrokerManager.php: -------------------------------------------------------------------------------- 1 | app = $app; 35 | } 36 | 37 | /** 38 | * Attempt to get the broker from the local cache. 39 | * 40 | * @param string $name 41 | * 42 | * @return \Rinvex\Auth\Contracts\EmailVerificationBrokerContract 43 | */ 44 | public function broker($name = null): EmailVerificationBrokerContract 45 | { 46 | $name = $name ?: $this->getDefaultDriver(); 47 | 48 | return $this->brokers[$name] ?? $this->brokers[$name] = $this->resolve($name); 49 | } 50 | 51 | /** 52 | * Resolve the given broker. 53 | * 54 | * @param string $name 55 | * 56 | * @throws \InvalidArgumentException 57 | * 58 | * @return \Rinvex\Auth\Contracts\EmailVerificationBrokerContract 59 | */ 60 | protected function resolve($name): EmailVerificationBrokerContract 61 | { 62 | $config = $this->getConfig($name); 63 | 64 | if (is_null($config)) { 65 | throw new InvalidArgumentException("Email verification broker [{$name}] is not defined."); 66 | } 67 | 68 | return new EmailVerificationBroker( 69 | $this->app['auth']->createUserProvider($config['provider']), 70 | $this->app['config']['app.key'], 71 | $config['expire'] 72 | ); 73 | } 74 | 75 | /** 76 | * Get the email verification broker configuration. 77 | * 78 | * @param string $name 79 | * 80 | * @return array 81 | */ 82 | protected function getConfig($name): array 83 | { 84 | return $this->app['config']["auth.emails.{$name}"]; 85 | } 86 | 87 | /** 88 | * Get the default email verification broker name. 89 | * 90 | * @return string 91 | */ 92 | public function getDefaultDriver(): string 93 | { 94 | return $this->app['config']['auth.defaults.emails']; 95 | } 96 | 97 | /** 98 | * Set the default email verification broker name. 99 | * 100 | * @param string $name 101 | * 102 | * @return void 103 | */ 104 | public function setDefaultDriver($name): void 105 | { 106 | $this->app['config']['auth.defaults.emails'] = $name; 107 | } 108 | 109 | /** 110 | * Dynamically call the default driver instance. 111 | * 112 | * @param string $method 113 | * @param array $parameters 114 | * 115 | * @return mixed 116 | */ 117 | public function __call($method, $parameters) 118 | { 119 | return $this->broker()->{$method}(...$parameters); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/Services/EmailVerificationBroker.php: -------------------------------------------------------------------------------- 1 | key = $key; 49 | $this->users = $users; 50 | $this->expiration = $expiration; 51 | } 52 | 53 | /** 54 | * {@inheritdoc} 55 | */ 56 | public function sendVerificationLink(array $credentials): string 57 | { 58 | // First we will check to see if we found a user at the given credentials and 59 | // if we did not we will redirect back to this current URI with a piece of 60 | // "flash" data in the session to indicate to the developers the errors. 61 | if (is_null($user = $this->getUser($credentials))) { 62 | return static::INVALID_USER; 63 | } 64 | 65 | $expiration = Carbon::now()->addMinutes($this->expiration)->timestamp; 66 | 67 | // Once we have the verification token, we are ready to send the message out to 68 | // this user with a link to verify their email. We will then redirect back to 69 | // the current URI having nothing set in the session to indicate any errors 70 | $user->sendEmailVerificationNotification($this->createToken($user, $expiration), $expiration); 71 | 72 | return static::LINK_SENT; 73 | } 74 | 75 | /** 76 | * {@inheritdoc} 77 | */ 78 | public function verify(array $credentials, Closure $callback) 79 | { 80 | // If the responses from the validate method is not a user instance, we will 81 | // assume that it is a redirect and simply return it from this method and 82 | // the user is properly redirected having an error message on the post. 83 | $user = $this->validateVerification($credentials); 84 | 85 | if (! $user instanceof CanVerifyEmailContract) { 86 | return $user; 87 | } 88 | 89 | // Once the email has been verified, we'll call the given 90 | // callback, then we'll delete the token and return. 91 | // in their persistent storage. 92 | $callback($user); 93 | 94 | return static::EMAIL_VERIFIED; 95 | } 96 | 97 | /** 98 | * Get the user for the given credentials. 99 | * 100 | * @param array $credentials 101 | * 102 | * @throws \UnexpectedValueException 103 | * 104 | * @return \Rinvex\Auth\Contracts\CanVerifyEmailContract|null 105 | */ 106 | public function getUser(array $credentials): ?CanVerifyEmailContract 107 | { 108 | $user = $this->users->retrieveByCredentials(Arr::only($credentials, ['email'])); 109 | 110 | if ($user && ! $user instanceof CanVerifyEmailContract) { 111 | throw new UnexpectedValueException('User must implement CanVerifyEmailContract interface.'); 112 | } 113 | 114 | return $user; 115 | } 116 | 117 | /** 118 | * Create a new email verification token for the given user. 119 | * 120 | * @param \Rinvex\Auth\Contracts\CanVerifyEmailContract $user 121 | * @param int $expiration 122 | * 123 | * @return string 124 | */ 125 | public function createToken(CanVerifyEmailContract $user, $expiration): string 126 | { 127 | $payload = $this->buildPayload($user, $user->getEmailForVerification(), $expiration); 128 | 129 | return hash_hmac('sha256', $payload, $this->getKey()); 130 | } 131 | 132 | /** 133 | * Validate the given email verification token. 134 | * 135 | * @param \Rinvex\Auth\Contracts\CanVerifyEmailContract $user 136 | * @param array $credentials 137 | * 138 | * @return bool 139 | */ 140 | public function validateToken(CanVerifyEmailContract $user, array $credentials): bool 141 | { 142 | $payload = $this->buildPayload($user, $credentials['email'], $credentials['expiration']); 143 | 144 | return hash_equals($credentials['token'], hash_hmac('sha256', $payload, $this->getKey())); 145 | } 146 | 147 | /** 148 | * Validate the given expiration timestamp. 149 | * 150 | * @param int $expiration 151 | * 152 | * @return bool 153 | */ 154 | public function validateTimestamp($expiration): bool 155 | { 156 | return Carbon::now()->createFromTimestamp($expiration)->isFuture(); 157 | } 158 | 159 | /** 160 | * Return the application key. 161 | * 162 | * @return string 163 | */ 164 | public function getKey(): string 165 | { 166 | if (Str::startsWith($this->key, 'base64:')) { 167 | return base64_decode(mb_substr($this->key, 7)); 168 | } 169 | 170 | return $this->key; 171 | } 172 | 173 | /** 174 | * Returns the payload string containing. 175 | * 176 | * @param \Rinvex\Auth\Contracts\CanVerifyEmailContract $user 177 | * @param string $email 178 | * @param int $expiration 179 | * 180 | * @return string 181 | */ 182 | protected function buildPayload(CanVerifyEmailContract $user, $email, $expiration): string 183 | { 184 | return implode(';', [ 185 | $email, 186 | $expiration, 187 | $user->getKey(), 188 | $user->password, 189 | ]); 190 | } 191 | 192 | /** 193 | * Validate an email verification for the given credentials. 194 | * 195 | * @param array $credentials 196 | * 197 | * @return \Rinvex\Auth\Contracts\CanVerifyEmailContract|string 198 | */ 199 | protected function validateVerification(array $credentials) 200 | { 201 | if (is_null($user = $this->getUser($credentials))) { 202 | return static::INVALID_USER; 203 | } 204 | 205 | if (! $this->validateToken($user, $credentials)) { 206 | return static::INVALID_TOKEN; 207 | } 208 | 209 | if (! $this->validateTimestamp($credentials['expiration'])) { 210 | return static::EXPIRED_TOKEN; 211 | } 212 | 213 | return $user; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/Services/PasswordResetBroker.php: -------------------------------------------------------------------------------- 1 | key = $key; 49 | $this->users = $users; 50 | $this->expiration = $expiration; 51 | } 52 | 53 | /** 54 | * Send a password reset link to a user. 55 | * 56 | * @param array $credentials 57 | * @param \Closure|null $callback 58 | * 59 | * @return string 60 | */ 61 | public function sendResetLink(array $credentials, Closure $callback = null) 62 | { 63 | // First we will check to see if we found a user at the given credentials and 64 | // if we did not we will redirect back to this current URI with a piece of 65 | // "flash" data in the session to indicate to the developers the errors. 66 | $user = $this->getUser($credentials); 67 | 68 | if (is_null($user)) { 69 | return static::INVALID_USER; 70 | } 71 | 72 | $expiration = Carbon::now()->addMinutes($this->expiration)->timestamp; 73 | 74 | // Once we have the reset token, we are ready to send the message out to this 75 | // user with a link to reset their password. We will then redirect back to 76 | // the current URI having nothing set in the session to indicate errors. 77 | $user->sendPasswordResetNotification($this->createToken($user, $expiration), $expiration); 78 | 79 | return static::RESET_LINK_SENT; 80 | } 81 | 82 | /** 83 | * Reset the password for the given token. 84 | * 85 | * @param array $credentials 86 | * @param \Closure $callback 87 | * 88 | * @return mixed 89 | */ 90 | public function reset(array $credentials, Closure $callback) 91 | { 92 | $user = $this->validateReset($credentials); 93 | 94 | // If the responses from the validate method is not a user instance, we will 95 | // assume that it is a redirect and simply return it from this method and 96 | // the user is properly redirected having an error message on the post. 97 | if (! $user instanceof CanResetPasswordContract) { 98 | return $user; 99 | } 100 | 101 | $password = $credentials['password']; 102 | 103 | // Once the reset has been validated, we'll call the given callback with the 104 | // new password. This gives the user an opportunity to store the password 105 | // in their persistent storage. 106 | $callback($user, $password); 107 | 108 | return static::PASSWORD_RESET; 109 | } 110 | 111 | /** 112 | * Get the user for the given credentials. 113 | * 114 | * @param array $credentials 115 | * 116 | * @throws \UnexpectedValueException 117 | * 118 | * @return \Rinvex\Auth\Contracts\CanResetPasswordContract|null 119 | */ 120 | public function getUser(array $credentials): ?CanResetPasswordContract 121 | { 122 | $user = $this->users->retrieveByCredentials(Arr::only($credentials, ['email'])); 123 | 124 | if ($user && ! $user instanceof CanResetPasswordContract) { 125 | throw new UnexpectedValueException('User must implement CanResetPassword interface.'); 126 | } 127 | 128 | return $user; 129 | } 130 | 131 | /** 132 | * Create a new password reset token for the given user. 133 | * 134 | * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user 135 | * @param int $expiration 136 | * 137 | * @return string 138 | */ 139 | public function createToken(CanResetPasswordContract $user, $expiration): string 140 | { 141 | $payload = $this->buildPayload($user, $user->getEmailForPasswordReset(), $expiration); 142 | 143 | return hash_hmac('sha256', $payload, $this->getKey()); 144 | } 145 | 146 | /** 147 | * Validate the given password reset token. 148 | * 149 | * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user 150 | * @param array $credentials 151 | * 152 | * @return bool 153 | */ 154 | public function validateToken(CanResetPasswordContract $user, array $credentials): bool 155 | { 156 | $payload = $this->buildPayload($user, $credentials['email'], $credentials['expiration']); 157 | 158 | return hash_equals($credentials['token'], hash_hmac('sha256', $payload, $this->getKey())); 159 | } 160 | 161 | /** 162 | * Validate the given expiration timestamp. 163 | * 164 | * @param int $expiration 165 | * 166 | * @return bool 167 | */ 168 | public function validateTimestamp($expiration): bool 169 | { 170 | return Carbon::now()->createFromTimestamp($expiration)->isFuture(); 171 | } 172 | 173 | /** 174 | * Return the application key. 175 | * 176 | * @return string 177 | */ 178 | public function getKey(): string 179 | { 180 | if (Str::startsWith($this->key, 'base64:')) { 181 | return base64_decode(mb_substr($this->key, 7)); 182 | } 183 | 184 | return $this->key; 185 | } 186 | 187 | /** 188 | * Returns the payload string containing. 189 | * 190 | * @param \Rinvex\Auth\Contracts\CanResetPasswordContract $user 191 | * @param string $email 192 | * @param int $expiration 193 | * 194 | * @return string 195 | */ 196 | protected function buildPayload(CanResetPasswordContract $user, $email, $expiration): string 197 | { 198 | return implode(';', [ 199 | $email, 200 | $expiration, 201 | $user->getKey(), 202 | $user->password, 203 | ]); 204 | } 205 | 206 | /** 207 | * Validate a password reset for the given credentials. 208 | * 209 | * @param array $credentials 210 | * 211 | * @return \Illuminate\Contracts\Auth\CanResetPassword|string 212 | */ 213 | protected function validateReset(array $credentials) 214 | { 215 | if (is_null($user = $this->getUser($credentials))) { 216 | return static::INVALID_USER; 217 | } 218 | 219 | if (! $this->validateToken($user, $credentials)) { 220 | return static::INVALID_TOKEN; 221 | } 222 | 223 | if (! $this->validateTimestamp($credentials['expiration'])) { 224 | return static::EXPIRED_TOKEN; 225 | } 226 | 227 | return $user; 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Rinvex Auth Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | This project adheres to [Semantic Versioning](CONTRIBUTING.md). 6 | 7 | 8 | ## [v7.1.2] - 2023-07-03 9 | - Update composer dependencies 10 | - Replace Carbon\Carbon with Illuminate\Support\Carbon 11 | 12 | ## [v7.1.1] - 2023-06-29 13 | - Drop config resource publishing since there's no config for this package 14 | 15 | ## [v7.1.0] - 2023-05-02 16 | - Add support for Laravel v11, and drop support for Laravel v9 17 | - Upgrade laravel/socialite to v5.6 from v5.5 18 | - Update phpunit to v10.1 from v9.5 19 | 20 | ## [v7.0.0] - 2023-01-09 21 | - Tweak artisan commands registration 22 | - Drop PHP v8.0 support and update composer dependencies 23 | - Utilize PHP 8.1 attributes feature for artisan commands 24 | 25 | ## [v6.1.0] - 2022-02-14 26 | - Update composer dependencies to Laravel v9 27 | 28 | ## [v6.0.0] - 2021-08-22 29 | - Drop PHP v7 support, and upgrade rinvex package dependencies to next major version 30 | - Update composer dependencies 31 | - Upgrade to GitHub-native Dependabot (#165) 32 | 33 | ## [v5.0.3] - 2021-02-28 34 | - Move email verification config to default project config/auth.php file 35 | 36 | ## [v5.0.2] - 2021-01-18 37 | - fix reset password declaration compatibility (#163) 38 | - Enable StyleCI risky mode 39 | 40 | ## [v5.0.1] - 2020-12-25 41 | - Add support for PHP v8 42 | 43 | ## [v5.0.0] - 2020-12-22 44 | - Upgrade to Laravel v8 45 | 46 | ## [v4.1.0] - 2020-06-15 47 | - Update validation rules 48 | - Drop using rinvex/laravel-cacheable from core packages for more flexibility 49 | - Caching should be handled on the application layer, not enforced from the core packages 50 | - Drop PHP 7.2 & 7.3 support from travis 51 | 52 | ## [v4.0.7] - 2020-04-12 53 | - Fix ServiceProvider registerCommands method compatibility 54 | 55 | ## [v4.0.6] - 2020-04-09 56 | - Tweak artisan command registration 57 | - Refactor publish command and allow multiple resource values 58 | 59 | ## [v4.0.5] - 2020-04-04 60 | - Fix namespace issue 61 | 62 | ## [v4.0.4] - 2020-04-04 63 | - Enforce consistent artisan command tag namespacing 64 | - Enforce consistent package namespace 65 | - Drop laravel/helpers usage as it's no longer used 66 | 67 | ## [v4.0.3] - 2020-03-20 68 | - Convert into bigInteger database fields 69 | - Add shortcut -f (force) for artisan publish commands 70 | - Fix migrations path 71 | 72 | ## [v4.0.2] - 2020-03-15 73 | - Fix composer package laravel-notification-channels/authy version 74 | 75 | ## [v4.0.1] - 2020-03-15 76 | - Fix wrong package version laravelcollective/html 77 | 78 | ## [v4.0.0] - 2020-03-15 79 | - Upgrade to Laravel v7.1.x & PHP v7.4.x 80 | 81 | ## [v3.0.4] - 2020-03-13 82 | - Tweak TravisCI config 83 | - Remove indirect composer dependency 84 | - Drop using global helpers 85 | - Update StyleCI config 86 | 87 | ## [v3.0.3] - 2019-09-24 88 | - Add missing laravel/helpers composer package 89 | 90 | ## [v3.0.2] - 2019-09-23 91 | - Fix outdated package version 92 | 93 | ## [v3.0.1] - 2019-09-23 94 | - Fix outdated package version 95 | 96 | ## [v3.0.0] - 2019-09-23 97 | - Upgrade to Laravel v6 and update dependencies 98 | 99 | ## [v2.2.0] - 2019-08-03 100 | - Update composer dependenices 101 | 102 | ## [v2.1.1] - 2019-06-03 103 | - Enforce latest composer package versions 104 | 105 | ## [v2.1.0] - 2019-06-02 106 | - Update composer deps 107 | - Drop PHP 7.1 travis test 108 | - Tweak service provider publishes functionality and console tools 109 | 110 | ## [v2.0.1] - 2019-03-03 111 | - Update composer dependencies 112 | 113 | ## [v2.0.0] - 2019-03-03 114 | - Rename environment variable QUEUE_DRIVER to QUEUE_CONNECTION 115 | - Require PHP 7.2 & Laravel 5.8 116 | - Apply PHPUnit 8 updates 117 | - Utilize support helpers 118 | 119 | ## [v1.0.2] - 2018-12-22 120 | - Update composer dependencies 121 | - Add PHP 7.3 support to travis 122 | 123 | ## [v1.0.1] - 2018-10-05 124 | - Fix wrong composer package version constraints 125 | 126 | ## [v1.0.0] - 2018-10-01 127 | - Enforce Consistency 128 | - Support Laravel 5.7+ 129 | - Rename package to rinvex/laravel-auth 130 | 131 | ## [v0.0.9] - 2018-09-22 132 | - Update travis php versions 133 | - Drop useless composer dependencies 134 | - Typehint notifications method parameters 135 | - Move country validation rule to rinvex/.country package 136 | - Move language validation rule to rinvex/languages package 137 | - Allow brokers to return null on user retrieval 138 | - Use global helper functions instead of class based methods 139 | - Drop StyleCI multi-language support (paid feature now!) 140 | - Update composer dependencies 141 | - Prepare and tweak testing configuration 142 | - Update StyleCI options 143 | - Update PHPUnit options 144 | - Update default email verification brokers config 145 | 146 | ## [v0.0.8] - 2018-02-18 147 | - Update composer dependencies 148 | - Drop Laravel 5.5 support 149 | 150 | ## [v0.0.7] - 2018-02-17 151 | - Major refactor with complete package re-write 152 | - Remove frontend stuff (use cortex/fort for that) 153 | - Use Carbon global helper 154 | - Skip credential validation on social login 155 | - Change social account user id datatype 156 | - Remove frontend stuff & exception handler (user cortex/fort or your app level layer for this) 157 | - Add missing namespaces 158 | - Use ->getKeyName() and ->getKey() methods instead of ->id 159 | - Refactor authorization layer for simplicity & intuitiveness 160 | - Rename job_title to title 161 | - Typehint method returns 162 | - Drop useless model contracts (models already swappable through IoC) 163 | - Move policies & SeedComand to cortex/fort 164 | - Move login throttling to cortex/fort 165 | - Separate TwoFactor into new trait rather than SessionGuard 166 | - Move session persistence mode check to EvenHandler from SessionGuard 167 | - Drop the custom SessionGuard in favor for Laravel's vanilla 168 | - Drop email verification success message 169 | - Move TwoFactorAuthenticatesUsers trait from cortex/fort 170 | - Remove useless TwoFactor config options 171 | - Simplify event listeners 172 | - Simplify registration welcome email 173 | - Move events subscriber to cortex/fort 174 | - Merge GetsMiddleware trait into AbstractController in cortex/foundation 175 | - Fix nullable return type phone & country for verification 176 | - Move middleware & notifications to cortex/fort 177 | - Move HasHashables trait from rinvex/laravel-support 178 | - Simplify IoC binding 179 | - Convert genders database storage to explicit male/female instead of m/f 180 | - Refactor parseAbilities & parseRoles 181 | - Fix wrong parameter names 182 | - Fix cacheable issues 183 | - Add force option to artisan commands 184 | - Refactor abilities to use silber/bouncer 185 | - Refactor user_id to polymorphic relation 186 | - Simplify rinvex/fort config options 187 | - Move GenericException to cortex/foundation from rinvex/fort 188 | - Refactor package and move app-layer features to cortex/fort 189 | - Rename rinvex/fort to rinvex/laravel-auth 190 | 191 | ## [v0.0.6] - 2018-01-04 192 | - Support Laravel 5.5 193 | - Major refactor with better Laravel integrity 194 | - Push composer dependencies forward 195 | - Fix gender validation rules 196 | - Fix last activity middleware update issue for guests 197 | - Redirect user to email verification form after changing email if required 198 | - Redirect newly registered users to email verification form 199 | - Separate userarea stuff from frontend with appropriate isolation [major refactor] 200 | - Remove TowFactor json mutator/accessor in favor to attribute casting 201 | - Enforce attribute casting 202 | - Enforce validation rules 203 | - Enforce eloquent builder usage consistency 204 | - Call fill->save() rather than create() for explicit calls 205 | - Add country and language validators 206 | - Use collection higher order messages for simpler and clearer code 207 | - Add request validation and authorization functionality for controllers 208 | - Fix date validation rules 209 | - Fix two_factor validation rule 210 | - Tweak and fix exception handler 211 | - Tag published resources 212 | - Update user last_activity via raw query not eloquent to avoid triggering events and thus flushing cache 213 | - Clean data resource files 214 | - Add role users mutator attribute for easy assigning 215 | - Fix php artisan make:auth issues 216 | - Fix dashboard online users issues 217 | - Fix make:auth views generation extension issues 218 | - Use antonioribeiro/google2fa rather than our custom TOTP (close #132) 219 | - Remove outdated console commands 220 | - Remove wrong userarea route prefix 221 | - Update composer dependencies versions 222 | - Refactor GenericException and exception handler (close #126) 223 | - Add query scopes for user active/inactive states 224 | - Add user activate/deactivate model helpers 225 | - Fix routes registration 226 | - Enforce consistent slug formats 227 | - Move prepareUniqueRule validation method to it's own support trait 228 | - Add migration command for easy installation 229 | - Move seeder traits from cortex/fort 230 | - Remove translatable fields from auto casting (It's already casted to array by translatable trait) 231 | - Use custom slug separator rather than attribute mutator 232 | - Use IoC bound model instead of the explicitly hardcoded 233 | - Bind model alias into IoC container 234 | - Use IoC bound model rather than hard coding 235 | - Move seeder helper from rinvex/laravel-support 236 | - Tweak service provider and enforce consistency 237 | - Program to an interface not implementation for flexible model swapping 238 | - Assure unique slugs generated at all times 239 | - Eliminate reset form eliminate button 240 | - Use default dash slug separator 241 | - Trim Model name if ModelContract given 242 | - Handle default translation through overridden HasTranslations trait 243 | - Refactor access areas 244 | - Override MakeAuthCommand conditionally (config option) 245 | - Rename adminarea ability / route / phrase 246 | - Convert seeds to artisan command 247 | - Enforce strict abilities/roles assignment (users can't assign roles or grant abilities they don't already have) 248 | - Add missing guard 249 | - Move slug auto generation to the custom HasSlug trait 250 | - Merge and rename guestarea & memberarea to frontarea 251 | - Remove FormRequest override in favor for native prepareForValidation feature 252 | - Update frontarea route names 253 | - Add Rollback Console Command 254 | 255 | ## [v0.0.5] - 2017-06-29 256 | - Major refactor with better Laravel integrity 257 | - Refactor FormRequests and Validation Rules 258 | - Refactor Crypto-based email verification 259 | - Refactor Crypto-based password resets 260 | - Refactor TwoFactor Authentication 261 | - Refactor Session Persistence 262 | - Add NoHttpCache middleware 263 | - Tweak and harden security 264 | - BACKWORK INCOMPATIBILITY 265 | - Add Laravel 5.5 support 266 | 267 | ## [v0.0.4] - 2017-03-08 268 | - Major rewrite with better Laravel integrity 269 | - Total frontend isolation and auth:make generation 270 | - System wide bug fixes, feature enhancements, and stability 271 | 272 | ## [v0.0.3] - 2017-02-11 273 | - Major changes and wide enhancements 274 | 275 | ## [v0.0.2] - 2016-12-19 276 | - Fix multiple bugs & issues 277 | 278 | ## v0.0.1 - 2016-12-19 279 | - Tag first release 280 | 281 | [v7.1.2]: https://github.com/rinvex/laravel-auth/compare/v7.1.1...v7.1.2 282 | [v7.1.1]: https://github.com/rinvex/laravel-auth/compare/v7.1.0...v7.1.1 283 | [v7.1.0]: https://github.com/rinvex/laravel-auth/compare/v7.0.0...v7.1.0 284 | [v7.0.0]: https://github.com/rinvex/laravel-auth/compare/v6.1.0...v7.0.0 285 | [v6.1.0]: https://github.com/rinvex/laravel-auth/compare/v6.0.0...v6.1.0 286 | [v6.0.0]: https://github.com/rinvex/laravel-auth/compare/v5.0.3...v6.0.0 287 | [v5.0.3]: https://github.com/rinvex/laravel-auth/compare/v5.0.2...v5.0.3 288 | [v5.0.2]: https://github.com/rinvex/laravel-auth/compare/v5.0.1...v5.0.2 289 | [v5.0.1]: https://github.com/rinvex/laravel-auth/compare/v5.0.0...v5.0.1 290 | [v5.0.0]: https://github.com/rinvex/laravel-auth/compare/v4.1.0...v5.0.0 291 | [v4.1.0]: https://github.com/rinvex/laravel-auth/compare/v4.0.7...v4.1.0 292 | [v4.0.7]: https://github.com/rinvex/laravel-auth/compare/v4.0.6...v4.0.7 293 | [v4.0.6]: https://github.com/rinvex/laravel-auth/compare/v4.0.5...v4.0.6 294 | [v4.0.5]: https://github.com/rinvex/laravel-auth/compare/v4.0.4...v4.0.5 295 | [v4.0.4]: https://github.com/rinvex/laravel-auth/compare/v4.0.3...v4.0.4 296 | [v4.0.3]: https://github.com/rinvex/laravel-auth/compare/v4.0.2...v4.0.3 297 | [v4.0.2]: https://github.com/rinvex/laravel-auth/compare/v4.0.1...v4.0.2 298 | [v4.0.1]: https://github.com/rinvex/laravel-auth/compare/v4.0.0...v4.0.1 299 | [v4.0.0]: https://github.com/rinvex/laravel-auth/compare/v3.0.4...v4.0.0 300 | [v3.0.4]: https://github.com/rinvex/laravel-auth/compare/v3.0.3...v3.0.4 301 | [v3.0.3]: https://github.com/rinvex/laravel-auth/compare/v3.0.2...v3.0.3 302 | [v3.0.2]: https://github.com/rinvex/laravel-auth/compare/v3.0.1...v3.0.2 303 | [v3.0.1]: https://github.com/rinvex/laravel-auth/compare/v3.0.0...v3.0.1 304 | [v3.0.0]: https://github.com/rinvex/laravel-auth/compare/v2.2.0...v3.0.0 305 | [v2.2.0]: https://github.com/rinvex/laravel-auth/compare/v2.1.1...v2.2.0 306 | [v2.1.1]: https://github.com/rinvex/laravel-auth/compare/v2.1.0...v2.1.1 307 | [v2.1.0]: https://github.com/rinvex/laravel-auth/compare/v2.0.1...v2.1.0 308 | [v2.0.1]: https://github.com/rinvex/laravel-auth/compare/v2.0.0...v2.0.1 309 | [v2.0.0]: https://github.com/rinvex/laravel-auth/compare/v1.0.2...v2.0.0 310 | [v1.0.2]: https://github.com/rinvex/laravel-auth/compare/v1.0.1...v1.0.2 311 | [v1.0.1]: https://github.com/rinvex/laravel-auth/compare/v1.0.0...v1.0.1 312 | [v1.0.0]: https://github.com/rinvex/laravel-auth/compare/v0.0.9...v1.0.0 313 | [v0.0.9]: https://github.com/rinvex/laravel-auth/compare/v0.0.8...v0.0.9 314 | [v0.0.8]: https://github.com/rinvex/laravel-auth/compare/v0.0.7...v0.0.8 315 | [v0.0.7]: https://github.com/rinvex/laravel-auth/compare/v0.0.6...v0.0.7 316 | [v0.0.6]: https://github.com/rinvex/laravel-auth/compare/v0.0.5...v0.0.6 317 | [v0.0.5]: https://github.com/rinvex/laravel-auth/compare/v0.0.4...v0.0.5 318 | [v0.0.4]: https://github.com/rinvex/laravel-auth/compare/v0.0.3...v0.0.4 319 | [v0.0.3]: https://github.com/rinvex/laravel-auth/compare/v0.0.2...v0.0.3 320 | [v0.0.2]: https://github.com/rinvex/laravel-auth/compare/v0.0.1...v0.0.2 321 | --------------------------------------------------------------------------------