├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .gitlab-ci.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── changelog.md
├── composer.json
├── config
└── awesio-auth.php
├── database
├── factories
│ ├── TwoFactorFactory.php
│ ├── UserFactory.php
│ └── UserSocialFactory.php
└── migrations
│ ├── create_countries_table.php.stub
│ ├── create_two_factor_table.php.stub
│ └── create_users_social_table.php.stub
├── docs
└── index.md
├── phpunit.xml.dist
├── src
├── Auth.php
├── AuthServiceProvider.php
├── Contracts
│ └── Auth.php
├── Controllers
│ ├── Controller.php
│ ├── ForgotPasswordController.php
│ ├── LoginController.php
│ ├── RegisterController.php
│ ├── ResetPasswordController.php
│ ├── SocialLoginController.php
│ ├── Traits
│ │ ├── AuthenticatesUsersWith2FA.php
│ │ └── RedirectsTo.php
│ ├── TwoFactorController.php
│ ├── TwoFactorLoginController.php
│ └── VerificationController.php
├── Facades
│ └── Auth.php
├── Listeners
│ └── EventSubscriber.php
├── Middlewares
│ └── SocialAuthentication.php
├── Models
│ ├── Country.php
│ ├── Traits
│ │ ├── HasSocialAuthentication.php
│ │ ├── HasTwoFactorAuthentication.php
│ │ ├── SendsEmailVerification.php
│ │ └── SendsPasswordReset.php
│ ├── TwoFactor.php
│ └── UserSocial.php
├── Repositories
│ ├── Contracts
│ │ └── UserRepository.php
│ └── EloquentUserRepository.php
├── Requests
│ ├── TwoFactorStoreRequest.php
│ └── TwoFactorVerifyRequest.php
├── Rules
│ ├── ValidPhone.php
│ └── ValidTwoFactorToken.php
├── Seeds
│ └── CountryTableSeeder.php
├── Services
│ ├── AuthyTwoFactor.php
│ ├── Contracts
│ │ ├── SocialProvidersManager.php
│ │ └── TwoFactor.php
│ └── SocialiteProvidersManager.php
└── helpers.php
├── tests
├── Feature
│ ├── AuthRoutesTest.php
│ ├── ForgotPasswordTest.php
│ ├── LoginTest.php
│ ├── RegisterTest.php
│ ├── ResetPasswordTest.php
│ ├── SocialLoginTest.php
│ ├── TwoFactorLoginTest.php
│ ├── TwoFactorTest.php
│ └── VerificationTest.php
├── Stubs
│ ├── TwoFactor.php
│ └── User.php
├── TestCase.php
└── Unit
│ ├── Listeners
│ └── EventSubscriberTest.php
│ ├── Models
│ └── UserSocialTest.php
│ ├── Repositories
│ └── UserRepositoryTest.php
│ └── Services
│ └── AuthyTest.php
├── views
├── auth
│ ├── login.blade.php
│ ├── passwords
│ │ ├── email.blade.php
│ │ └── reset.blade.php
│ ├── register.blade.php
│ └── verify.blade.php
├── layouts
│ └── app.blade.php
└── twofactor
│ ├── index.blade.php
│ └── verify.blade.php
└── xdebug.ini
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | patreon: awesdotio
4 | open_collective: awesdotio
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Tell us what happens instead of the expected behavior
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | Your issue may already be reported!
11 | Please search on the [issue tracker](../) before creating one.
12 |
13 | ## Expected Behavior
14 |
15 |
16 |
17 | ## Current Behavior
18 |
19 |
20 |
21 | ## Possible Solution
22 |
23 |
24 |
25 | ## Steps to Reproduce (for bugs)
26 |
27 |
28 | 1.
29 | 2.
30 | 3.
31 | 4.
32 |
33 | ## Context
34 |
35 |
36 |
37 | ## Your Environment
38 |
39 | * Version used:
40 | * Browser Name and version:
41 | * Operating System and version (desktop or mobile):
42 | * Link to your project:
43 |
44 | ## System GA
45 | [](https://github.com/awes-io/issues)
46 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | Your issue may already be reported!
11 | Please search on the [issue tracker](../) before creating one.
12 |
13 | ## Expected Behavior
14 |
15 |
16 |
17 | ## Current Behavior
18 |
19 |
20 |
21 | ## Possible Solution
22 |
23 |
24 |
25 |
26 | ## System GA
27 | [](https://github.com/awes-io/issues)
28 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | A similar PR may already be submitted!
2 | Please search among the [Pull request](../) before creating one.
3 |
4 | Thanks for submitting a pull request! Please provide enough information so that others can review your pull request:
5 |
6 |
7 | ## Summary
8 |
9 |
10 |
11 | This PR fixes/implements the following **bugs/features**
12 |
13 | * [ ] Bug 1
14 | * [ ] Bug 2
15 | * [ ] Feature 1
16 | * [ ] Feature 2
17 | * [ ] Breaking changes
18 |
19 |
20 |
21 | Explain the **motivation** for making this change. What existing problem does the pull request solve?
22 |
23 |
24 |
25 | ## Test plan (required)
26 |
27 | Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI.
28 |
29 |
30 |
31 | ## Code formatting
32 |
33 |
34 |
35 | ## Closing issues
36 |
37 |
38 | Fixes #
39 |
40 | ## System GA
41 | [](https://github.com/awes-io/issues)
42 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vendor
2 | composer.lock
3 | .idea
4 | /tests/report
5 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | image: awescodehub/php7.2-fpm-gitlab
2 |
3 | cache:
4 | paths:
5 | - vendor/
6 |
7 | before_script:
8 | # Install all project dependencies
9 | - composer install
10 |
11 | # Run our tests
12 | test:
13 | only:
14 | - master
15 | - dev
16 | script:
17 | - vendor/bin/phpunit --configuration phpunit.xml.dist --coverage-text --colors=never
18 | coverage: '/^\s*Methods:\s*\d+.\d+\%\s*\(\d+\/\d+\)/'
19 |
--------------------------------------------------------------------------------
/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, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and 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 contact@awes.io. 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 https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Awes.io
2 |
3 | Want to contribute to Awes.io? We provide a Contribution Guide to help you get started.
4 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM php:7.2-fpm
2 |
3 | RUN apt-get update && apt-get install -y \
4 | locales git unzip curl openssl ssh libz-dev \
5 | libfreetype6-dev libmcrypt-dev libxml2-dev
6 |
7 | RUN pecl install igbinary xdebug \
8 | && docker-php-ext-install -j$(nproc) mbstring zip bcmath soap \
9 | && docker-php-ext-enable igbinary xdebug
10 |
11 | RUN dpkg-reconfigure locales \
12 | && locale-gen C.UTF-8 \
13 | && /usr/sbin/update-locale LANG=C.UTF-8
14 |
15 | # intl
16 | RUN apt-get update \
17 | && apt-get install -y libicu-dev \
18 | && docker-php-ext-configure intl \
19 | && docker-php-ext-install intl
20 |
21 | RUN echo 'en_US.UTF-8 UTF-8' >> /etc/locale.gen && locale-gen
22 |
23 | ENV LC_ALL C.UTF-8
24 | ENV LANG en_US.UTF-8
25 | ENV LANGUAGE en_US.UTF-8
26 |
27 | RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
28 |
29 | COPY ./xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini
30 |
31 | RUN sed -i "s/xdebug.remote_autostart=0/xdebug.remote_autostart=1/" /usr/local/etc/php/conf.d/xdebug.ini && \
32 | sed -i "s/xdebug.remote_enable=0/xdebug.remote_enable=1/" /usr/local/etc/php/conf.d/xdebug.ini && \
33 | sed -i "s/xdebug.cli_color=0/xdebug.cli_color=1/" /usr/local/etc/php/conf.d/xdebug.ini
34 |
35 | # Clean up
36 | RUN apt-get clean && \
37 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
38 | rm /var/log/lastlog /var/log/faillog
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017-present, Awescode GmbH (www.awescode.de)
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Authentication
8 |
9 | Laravel Authentication package with built-in two-factor (Authy) and social authentication (Socialite).
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | ##
48 |
49 |
50 |
51 |
52 |
53 | ## Table of Contents
54 |
55 | - Installation
56 | - Configuration
57 | - Social and two-factor authentication
58 | - Email verification & resetting passwords
59 | - Usage
60 | - Testing
61 |
62 | ## Installation
63 |
64 | Via Composer
65 |
66 | ``` bash
67 | $ composer require awes-io/auth
68 | ```
69 |
70 | The package will automatically register itself.
71 |
72 | You can publish migrations:
73 |
74 | ```bash
75 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="migrations"
76 | ```
77 |
78 | After migrations have been published you can create required db tables by running:
79 |
80 | ```bash
81 | php artisan migrate
82 | ```
83 |
84 | Publish views:
85 |
86 | ```bash
87 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="views"
88 | ```
89 |
90 | ## Configuration
91 |
92 | Publish config file:
93 |
94 | ```bash
95 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="config"
96 | ```
97 |
98 | You can disable additional features by commenting them out:
99 |
100 | ```php
101 | 'enabled' => [
102 | 'social',
103 | // 'two_factor',
104 | // 'email_verification',
105 | ],
106 | ```
107 |
108 | Add new socialite services:
109 |
110 | ```php
111 | 'services' => [
112 | 'github' => [
113 | 'name' => 'GitHub'
114 | ],
115 | ...
116 | ],
117 | 'github' => [
118 | 'client_id' => env('GITHUB_CLIENT_ID'),
119 | ...
120 | ],
121 | ```
122 |
123 | And configure redirect paths:
124 |
125 | ```php
126 | 'redirects' => [
127 | 'login' => '/twofactor',
128 | 'reset_password' => '/',
129 | ...
130 | ],
131 | ```
132 |
133 | ### Social and two-factor authentication
134 |
135 | Several .env variables required if additional modules were enabled in config:
136 |
137 | ```php
138 | # SOCIALITE GITHUB
139 | GITHUB_CLIENT_ID=
140 | GITHUB_CLIENT_SECRET=
141 | GITHUB_REDIRECT_URL=http://auth.test/login/github/callback
142 |
143 | # TWO FACTOR AUTHY
144 | AUTHY_SECRET=
145 | ```
146 |
147 | If you enabled social and/or two factor authentication add respective traits to User model class:
148 |
149 | ```php
150 | use AwesIO\Auth\Models\Traits\HasSocialAuthentication;
151 | use AwesIO\Auth\Models\Traits\HasTwoFactorAuthentication;
152 |
153 | class User extends Authenticatable
154 | {
155 | use HasSocialAuthentication, HasTwoFactorAuthentication;
156 | }
157 | ```
158 |
159 | ### Email verification & resetting passwords
160 |
161 | To use email verification functionality and to reset passwords, add `SendsEmailVerification` and `SendsPasswordReset` traits:
162 |
163 | ```php
164 | use AwesIO\Auth\Models\Traits\SendsPasswordReset;
165 | use AwesIO\Auth\Models\Traits\SendsEmailVerification;
166 |
167 | class User extends Authenticatable
168 | {
169 | use SendsEmailVerification, SendsPasswordReset;
170 | }
171 | ```
172 |
173 | ## Usage
174 |
175 | Add to routes/web.php:
176 |
177 | ```php
178 | AwesAuth::routes();
179 | ```
180 |
181 | You can disable registration:
182 |
183 | ```php
184 | AwesAuth::routes(['register' => false]);
185 | ```
186 |
187 | Package will register several routes.
188 |
189 | ##### Besides default authentication routes, it will add:
190 | * Socialite routes
191 | * `'login.social'`
192 | * `'login/{service}/callback'`
193 | * Two factor authentication setup routes
194 | * `'twofactor.index'`
195 | * `'twofactor.store'`
196 | * `'twofactor.destroy'`
197 | * `'twofactor.verify'`
198 | * Two factor authentication login routes
199 | * `'login.twofactor.index'`
200 | * `'login.twofactor.verify'`
201 | * Email verification routes
202 | * `'verification.resend'`
203 | * `'verification.code.verify'`
204 | * `'verification.code'`
205 | * `'verification.verify'`
206 |
207 | ## Testing
208 |
209 | You can run the tests with:
210 |
211 | ```bash
212 | composer test
213 | ```
214 |
215 | ## Contributing
216 |
217 | Please see [contributing.md](contributing.md) for details and a todolist.
218 |
219 | ## Credits
220 |
221 | - [Galymzhan Begimov](https://github.com/begimov)
222 | - [All Contributors](contributing.md)
223 |
224 | ## License
225 |
226 | [MIT](http://opensource.org/licenses/MIT)
227 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to `LocalizationHelper` will be documented in this file.
4 |
5 | ## Version 1.0
6 |
7 | ### Added
8 | - Everything
9 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "awes-io/auth",
3 | "description": "Laravel Authentication package with built-in two-factor (Authy) and social authentication (Socialite).",
4 | "type": "library",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Awescode GmbH",
9 | "email": "info@awescode.de",
10 | "homepage": "https://www.awescode.de",
11 | "role": "Owner"
12 | },
13 | {
14 | "name": "Galymzhan Begimov",
15 | "email": "begimov@gmail.com",
16 | "homepage": "https://github.com/begimov"
17 | }
18 | ],
19 | "support": {
20 | "email": "support@awescode.de"
21 | },
22 | "homepage": "https://github.com/awes-io/auth",
23 | "keywords": ["laravel", "auth", "authentication", "authorisation", "authorization", "registration", "two factor authentication", "2fa", "authy", "socialite"],
24 | "require": {
25 | "illuminate/support": "~5|~6",
26 | "laravel/socialite": "^4.0",
27 | "guzzlehttp/guzzle": "^6.3"
28 |
29 | },
30 | "require-dev": {
31 | "phpunit/phpunit": "~7.0",
32 | "mockery/mockery": "^1.1",
33 | "orchestra/testbench": "~3.0",
34 | "sempro/phpunit-pretty-print": "^1.0"
35 | },
36 | "autoload": {
37 | "psr-4": {
38 | "AwesIO\\Auth\\": "src/"
39 | }
40 | },
41 | "autoload-dev": {
42 | "psr-4": {
43 | "AwesIO\\Auth\\Tests\\": "tests/"
44 | }
45 | },
46 | "scripts": {
47 | "test": "vendor/bin/phpunit --colors=always --configuration phpunit.xml.dist --debug"
48 | },
49 | "extra": {
50 | "laravel": {
51 | "providers": [
52 | "AwesIO\\Auth\\AuthServiceProvider"
53 | ],
54 | "aliases": {
55 | "AwesAuth": "AwesIO\\Auth\\Facades\\Auth"
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/config/awesio-auth.php:
--------------------------------------------------------------------------------
1 | [
11 | 'social', 'two_factor', 'email_verification'
12 | ],
13 |
14 | /*
15 | |--------------------------------------------------------------------------
16 | | Socialite related parameters
17 | |--------------------------------------------------------------------------
18 | */
19 | 'socialite' => [
20 |
21 | /*
22 | |--------------------------------------------------------------------------
23 | | Available socialite services
24 | |--------------------------------------------------------------------------
25 | */
26 | 'services' => [
27 | 'github' => [
28 | 'name' => 'GitHub'
29 | ]
30 | ],
31 |
32 | /*
33 | |--------------------------------------------------------------------------
34 | | Services credentials
35 | |--------------------------------------------------------------------------
36 | */
37 | 'github' => [
38 | 'client_id' => env('GITHUB_CLIENT_ID'),
39 | 'client_secret' => env('GITHUB_CLIENT_SECRET'),
40 | 'redirect' => env('GITHUB_REDIRECT_URL'),
41 | ],
42 |
43 | ],
44 |
45 | /*
46 | |--------------------------------------------------------------------------
47 | | Two factor authentication parameters
48 | |--------------------------------------------------------------------------
49 | */
50 | 'two_factor' => [
51 |
52 | 'authy' => [
53 | 'secret' => env('AUTHY_SECRET')
54 | ]
55 | ],
56 |
57 | /*
58 | |--------------------------------------------------------------------------
59 | | Redirects
60 | |--------------------------------------------------------------------------
61 | */
62 | 'redirects' => [
63 | 'login' => '/twofactor',
64 | 'register' => '/twofactor',
65 | 'reset_password' => '/',
66 | 'social_login' => '/twofactor',
67 | 'twofactor_login' => '/twofactor',
68 | 'email_verification' => '/twofactor',
69 | 'forgot_password' => '/password/reset',
70 | 'twofactor' => '/',
71 | ],
72 |
73 | 'mailables' => [
74 | // 'email_verification' => AwesIO\Mail\Mail\EmailConfirmation::class,
75 | // 'reset_password' => AwesIO\Mail\Mail\ResetPassword::class,
76 | ]
77 | ];
78 |
--------------------------------------------------------------------------------
/database/factories/TwoFactorFactory.php:
--------------------------------------------------------------------------------
1 | define(\AwesIO\Auth\Models\TwoFactor::class, function (Faker $faker) {
17 | return [
18 | 'phone' => '999 99 99',
19 | 'dial_code' => '+7'
20 | ];
21 | });
22 |
--------------------------------------------------------------------------------
/database/factories/UserFactory.php:
--------------------------------------------------------------------------------
1 | define(\AwesIO\Auth\Tests\Stubs\User::class, function (Faker $faker) {
17 | return [
18 | 'name' => $faker->name,
19 | 'email' => $faker->unique()->safeEmail,
20 | 'email_verified_at' => now(),
21 | 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
22 | 'remember_token' => str_random(10),
23 | ];
24 | });
25 |
--------------------------------------------------------------------------------
/database/factories/UserSocialFactory.php:
--------------------------------------------------------------------------------
1 | define(\AwesIO\Auth\Models\UserSocial::class, function (Faker $faker) {
17 | return [
18 | 'social_id' => uniqid(),
19 | 'user_id' => 1,
20 | 'service' => 'github',
21 | ];
22 | });
23 |
--------------------------------------------------------------------------------
/database/migrations/create_countries_table.php.stub:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->string('name');
18 | $table->string('code', 2);
19 | $table->string('dial_code');
20 | });
21 | }
22 |
23 | /**
24 | * Reverse the migrations.
25 | *
26 | * @return void
27 | */
28 | public function down()
29 | {
30 | Schema::drop('countries');
31 | }
32 | }
--------------------------------------------------------------------------------
/database/migrations/create_two_factor_table.php.stub:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->integer('user_id')->unsigned()->index();
18 | $table->string('identifier')->nullable();
19 | $table->string('phone');
20 | $table->string('dial_code');
21 | $table->boolean('verified')->default(false);
22 | $table->timestamps();
23 |
24 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
25 | });
26 | }
27 |
28 | /**
29 | * Reverse the migrations.
30 | *
31 | * @return void
32 | */
33 | public function down()
34 | {
35 | Schema::drop('two_factor');
36 | }
37 | }
--------------------------------------------------------------------------------
/database/migrations/create_users_social_table.php.stub:
--------------------------------------------------------------------------------
1 | increments('id');
17 | $table->integer('user_id')->unsigned()->index();
18 | $table->string('social_id');
19 | $table->string('service');
20 | $table->timestamps();
21 |
22 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
23 | });
24 | }
25 |
26 | /**
27 | * Reverse the migrations.
28 | *
29 | * @return void
30 | */
31 | public function down()
32 | {
33 | Schema::drop('users_social');
34 | }
35 | }
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | Authentication
2 |
3 | Laravel Authentication package with built-in two-factor (Authy) and social authentication (Socialite).
4 |
5 | ## Table of Contents
6 |
7 | - Installation
8 | - Configuration
9 | - Social and two-factor authentication
10 | - Email verification & resetting passwords
11 | - Usage
12 | - Testing
13 |
14 | ## Installation
15 |
16 | Via Composer
17 |
18 | ``` bash
19 | $ composer require awes-io/auth
20 | ```
21 |
22 | The package will automatically register itself.
23 |
24 | You can publish migrations:
25 |
26 | ```bash
27 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="migrations"
28 | ```
29 |
30 | After migrations have been published you can create required db tables by running:
31 |
32 | ```bash
33 | php artisan migrate
34 | ```
35 |
36 | Publish views:
37 |
38 | ```bash
39 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="views"
40 | ```
41 |
42 | ## Configuration
43 |
44 | Publish config file:
45 |
46 | ```bash
47 | php artisan vendor:publish --provider="AwesIO\Auth\AuthServiceProvider" --tag="config"
48 | ```
49 |
50 | You can disable additional features by commenting them out:
51 |
52 | ```php
53 | 'enabled' => [
54 | 'social',
55 | // 'two_factor',
56 | // 'email_verification',
57 | ],
58 | ```
59 |
60 | Add new socialite services:
61 |
62 | ```php
63 | 'services' => [
64 | 'github' => [
65 | 'name' => 'GitHub'
66 | ],
67 | ...
68 | ],
69 | 'github' => [
70 | 'client_id' => env('GITHUB_CLIENT_ID'),
71 | ...
72 | ],
73 | ```
74 |
75 | And configure redirect paths:
76 |
77 | ```php
78 | 'redirects' => [
79 | 'login' => '/twofactor',
80 | 'reset_password' => '/',
81 | ...
82 | ],
83 | ```
84 |
85 | ### Social and two-factor authentication
86 |
87 | Several .env variables required if additional modules were enabled in config:
88 |
89 | ```php
90 | # SOCIALITE GITHUB
91 | GITHUB_CLIENT_ID=
92 | GITHUB_CLIENT_SECRET=
93 | GITHUB_REDIRECT_URL=http://auth.test/login/github/callback
94 |
95 | # TWO FACTOR AUTHY
96 | AUTHY_SECRET=
97 | ```
98 |
99 | If you enabled social and/or two factor authentication add respective traits to User model class:
100 |
101 | ```php
102 | use AwesIO\Auth\Models\Traits\HasSocialAuthentication;
103 | use AwesIO\Auth\Models\Traits\HasTwoFactorAuthentication;
104 |
105 | class User extends Authenticatable
106 | {
107 | use HasSocialAuthentication, HasTwoFactorAuthentication;
108 | }
109 | ```
110 |
111 | ### Email verification & resetting passwords
112 |
113 | To use email verification functionality and to reset passwords, add `SendsEmailVerification` and `SendsPasswordReset` traits:
114 |
115 | ```php
116 | use AwesIO\Auth\Models\Traits\SendsPasswordReset;
117 | use AwesIO\Auth\Models\Traits\SendsEmailVerification;
118 |
119 | class User extends Authenticatable
120 | {
121 | use SendsEmailVerification, SendsPasswordReset;
122 | }
123 | ```
124 |
125 | ## Usage
126 |
127 | Add to routes/web.php:
128 |
129 | ```php
130 | AwesAuth::routes();
131 | ```
132 |
133 | You can disable registration:
134 |
135 | ```php
136 | AwesAuth::routes(['register' => false]);
137 | ```
138 |
139 | Package will register several routes.
140 |
141 | ##### Besides default authentication routes, it will add:
142 | * Socialite routes
143 | * `'login.social'`
144 | * `'login/{service}/callback'`
145 | * Two factor authentication setup routes
146 | * `'twofactor.index'`
147 | * `'twofactor.store'`
148 | * `'twofactor.destroy'`
149 | * `'twofactor.verify'`
150 | * Two factor authentication login routes
151 | * `'login.twofactor.index'`
152 | * `'login.twofactor.verify'`
153 | * Email verification routes
154 | * `'verification.resend'`
155 | * `'verification.code.verify'`
156 | * `'verification.code'`
157 | * `'verification.verify'`
158 |
159 | ## Testing
160 |
161 | You can run the tests with:
162 |
163 | ```bash
164 | composer test
165 | ```
166 |
167 | ## Contributing
168 |
169 | Please see [contributing.md](contributing.md) for details and a todolist.
170 |
171 | ## Credits
172 |
173 | - [Galymzhan Begimov](https://github.com/begimov)
174 | - [All Contributors](contributing.md)
175 |
176 | ## License
177 |
178 | [MIT](http://opensource.org/licenses/MIT)
179 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
14 | ./tests/
15 |
16 |
17 |
18 |
19 | src/
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/Auth.php:
--------------------------------------------------------------------------------
1 | router = $router;
16 | }
17 |
18 | /**
19 | * Register routes.
20 | *
21 | * @return void
22 | */
23 | public function routes(array $params = [])
24 | {
25 | // Authentication Routes...
26 | $this->loginRoutes();
27 |
28 | // Registration Routes...
29 | if ($params['register'] ?? true) {
30 | $this->registrationRoutes();
31 | }
32 |
33 | // Reset password Routes...
34 | $this->resetPasswordRoutes();
35 |
36 | // Socialite authentication Routes...
37 | if ($this->isSocialEnabled()) {
38 | $this->socialiteRoutes();
39 | }
40 |
41 | // Two factor authentication Routes...
42 | if ($this->isTwoFactorEnabled()) {
43 | $this->twoFactorRoutes();
44 | }
45 |
46 | // Email verification Routes...
47 | if ($this->isEmailVerificationEnabled()) {
48 | $this->emailVerificationRoutes();
49 | }
50 | }
51 |
52 | /**
53 | * Check if socialite authentication eneabled in config
54 | *
55 | * @return boolean
56 | */
57 | public function isSocialEnabled()
58 | {
59 | return in_array('social', config('awesio-auth.enabled'));
60 | }
61 |
62 | /**
63 | * Check if two factor authentication eneabled in config
64 | *
65 | * @return boolean
66 | */
67 | public function isTwoFactorEnabled()
68 | {
69 | return in_array('two_factor', config('awesio-auth.enabled'));
70 | }
71 |
72 | /**
73 | * Check if two factor authentication eneabled in config
74 | *
75 | * @return boolean
76 | */
77 | public function isEmailVerificationEnabled()
78 | {
79 | return in_array('email_verification', config('awesio-auth.enabled'));
80 | }
81 |
82 | /**
83 | * Login routes.
84 | *
85 | * @return void
86 | */
87 | protected function loginRoutes()
88 | {
89 | $this->router->get(
90 | 'login',
91 | '\AwesIO\Auth\Controllers\LoginController@showLoginForm'
92 | )->name('login');
93 |
94 | $this->router->post(
95 | 'login',
96 | '\AwesIO\Auth\Controllers\LoginController@login'
97 | );
98 |
99 | $this->router->any(
100 | 'logout',
101 | '\AwesIO\Auth\Controllers\LoginController@logout'
102 | )->name('logout');
103 | }
104 |
105 | /**
106 | * Registration routes.
107 | *
108 | * @return void
109 | */
110 | protected function registrationRoutes()
111 | {
112 | $this->router->get(
113 | 'register',
114 | '\AwesIO\Auth\Controllers\RegisterController@showRegistrationForm'
115 | )->name('register');
116 |
117 | $this->router->post(
118 | 'register',
119 | '\AwesIO\Auth\Controllers\RegisterController@register'
120 | );
121 | }
122 |
123 | /**
124 | * Reset password routes.
125 | *
126 | * @return void
127 | */
128 | protected function resetPasswordRoutes()
129 | {
130 | $this->router->get(
131 | 'password/reset',
132 | '\AwesIO\Auth\Controllers\ForgotPasswordController@showLinkRequestForm'
133 | )->name('password.request');
134 |
135 | $this->router->post(
136 | 'password/email',
137 | '\AwesIO\Auth\Controllers\ForgotPasswordController@sendResetLinkEmail'
138 | )->name('password.email');
139 |
140 | $this->router->get(
141 | 'password/reset/{token}',
142 | '\AwesIO\Auth\Controllers\ResetPasswordController@showResetForm'
143 | )->name('password.reset');
144 |
145 | $this->router->post(
146 | 'password/reset',
147 | '\AwesIO\Auth\Controllers\ResetPasswordController@reset'
148 | )->name('password.update');
149 | }
150 |
151 | /**
152 | * Socialite routes.
153 | *
154 | * @return void
155 | */
156 | protected function socialiteRoutes()
157 | {
158 | $this->router->middleware(['guest', SocialAuthentication::class])
159 | ->group(function () {
160 |
161 | $this->router->get(
162 | 'login/{service}',
163 | '\AwesIO\Auth\Controllers\SocialLoginController@redirect'
164 | )->name('login.social');
165 |
166 | $this->router->get(
167 | 'login/{service}/callback',
168 | '\AwesIO\Auth\Controllers\SocialLoginController@callback'
169 | );
170 | });
171 | }
172 |
173 | /**
174 | * Two factor routes.
175 | *
176 | * @return void
177 | */
178 | protected function twoFactorRoutes()
179 | {
180 | // Setting up and verifying 2FA routes
181 | $this->router->middleware(['auth'])
182 | ->group(function () {
183 |
184 | $this->router->get(
185 | 'twofactor',
186 | '\AwesIO\Auth\Controllers\TwoFactorController@index'
187 | )->name('twofactor.index');
188 |
189 | $this->router->post(
190 | 'twofactor',
191 | '\AwesIO\Auth\Controllers\TwoFactorController@store'
192 | )->name('twofactor.store');
193 |
194 | $this->router->post(
195 | 'twofactor/verify',
196 | '\AwesIO\Auth\Controllers\TwoFactorController@verify'
197 | )->name('twofactor.verify');
198 |
199 | $this->router->delete(
200 | 'twofactor',
201 | '\AwesIO\Auth\Controllers\TwoFactorController@destroy'
202 | )->name('twofactor.destroy');
203 | });
204 |
205 | // 2FA login routes
206 | $this->router->middleware(['guest'])
207 | ->group(function () {
208 |
209 | $this->router->get(
210 | 'login/twofactor/verify',
211 | '\AwesIO\Auth\Controllers\TwoFactorLoginController@index'
212 | )->name('login.twofactor.index');
213 |
214 | $this->router->post(
215 | 'login/twofactor/verify',
216 | '\AwesIO\Auth\Controllers\TwoFactorLoginController@verify'
217 | )->name('login.twofactor.verify');
218 | });
219 | }
220 |
221 | /**
222 | * Email verification routes.
223 | *
224 | * @return void
225 | */
226 | protected function emailVerificationRoutes()
227 | {
228 | $this->router->get(
229 | 'email/verify',
230 | '\AwesIO\Auth\Controllers\VerificationController@show'
231 | )->name('verification.code');
232 |
233 | $this->router->post(
234 | 'email/verify',
235 | '\AwesIO\Auth\Controllers\VerificationController@verifyCode'
236 | )->name('verification.code.verify');
237 |
238 | $this->router->get(
239 | 'email/verify/{id}',
240 | '\AwesIO\Auth\Controllers\VerificationController@verify'
241 | )->name('verification.verify');
242 |
243 | $this->router->get(
244 | 'email/resend',
245 | '\AwesIO\Auth\Controllers\VerificationController@resend'
246 | )->name('verification.resend')->middleware('throttle:1,0.2');
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/src/AuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | loadViewsFrom(__DIR__.'/../views', 'awesio-auth');
29 |
30 | $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
31 |
32 | $this->publishes([
33 | __DIR__.'/../config/awesio-auth.php' => config_path('awesio-auth.php'),
34 | ], 'config');
35 |
36 | $this->bootMigrationsPublishing();
37 |
38 | $this->publishes([
39 | __DIR__.'/../views' => resource_path('views/vendor/awesio-auth'),
40 | ], 'views');
41 |
42 | Event::subscribe(EventSubscriber::class);
43 | }
44 |
45 | /**
46 | * Register any application services.
47 | *
48 | * @return void
49 | */
50 | public function register()
51 | {
52 | $this->mergeConfigFrom(__DIR__.'/../config/awesio-auth.php', 'awesio-auth');
53 |
54 | $this->app->singleton(AuthContract::class, Auth::class);
55 |
56 | $this->registerRepositories();
57 |
58 | $this->registerServices();
59 |
60 | $this->registerHelpers();
61 | }
62 |
63 | /**
64 | * Register and bind package repositories
65 | *
66 | * @return void
67 | */
68 | protected function registerRepositories()
69 | {
70 | $this->app->bind(UserRepository::class, EloquentUserRepository::class);
71 | }
72 |
73 | /**
74 | * Register and bind package services
75 | *
76 | * @return void
77 | */
78 | protected function registerServices()
79 | {
80 | $this->app->bind(SocialProvidersManager::class, SocialiteProvidersManager::class);
81 |
82 | $this->app->singleton(TwoFactor::class, function () {
83 | return new AuthyTwoFactor(new Client());
84 | });
85 | }
86 |
87 | /**
88 | * Prepare migrations for publication
89 | *
90 | * @return void
91 | */
92 | protected function bootMigrationsPublishing()
93 | {
94 | $this->publishes([
95 | __DIR__.'/../database/migrations/create_countries_table.php.stub'
96 | => database_path('migrations/'.date('Y_m_d_His', time()).'_create_countries_table.php'),
97 | __DIR__.'/../database/migrations/create_two_factor_table.php.stub'
98 | => database_path('migrations/'.date('Y_m_d_His', time()).'_create_two_factor_table.php'),
99 | __DIR__.'/../database/migrations/create_users_social_table.php.stub'
100 | => database_path('migrations/'.date('Y_m_d_His', time()).'_create_users_social_table.php'),
101 | ], 'migrations');
102 | }
103 |
104 | /**
105 | * Register helpers file
106 | */
107 | public function registerHelpers()
108 | {
109 | if (file_exists($file = __DIR__ . '/helpers.php')) {
110 | require $file;
111 | }
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/src/Contracts/Auth.php:
--------------------------------------------------------------------------------
1 | middleware('guest');
33 | }
34 |
35 | /**
36 | * Display the form to request a password reset link.
37 | *
38 | * @return \Illuminate\Http\Response
39 | */
40 | public function showLinkRequestForm()
41 | {
42 | return view('awesio-auth::auth.passwords.email');
43 | }
44 |
45 | /**
46 | * Get the response for a successful password reset link.
47 | *
48 | * @param \Illuminate\Http\Request $request
49 | * @param string $response
50 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
51 | */
52 | protected function sendResetLinkResponse(Request $request, $response)
53 | {
54 | if ($request->ajax()) {
55 | return $this->ajaxRedirectTo($request);
56 | }
57 |
58 | return redirect($this->redirectPath())
59 | ->with('status', trans($response));
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Controllers/LoginController.php:
--------------------------------------------------------------------------------
1 | middleware('guest')->except('logout');
29 | }
30 |
31 | /**
32 | * Show the application's login form.
33 | *
34 | * @return \Illuminate\Http\Response
35 | */
36 | public function showLoginForm()
37 | {
38 | return view('awesio-auth::auth.login');
39 | }
40 |
41 | /**
42 | * The user has been authenticated.
43 | *
44 | * @param \Illuminate\Http\Request $request
45 | * @param mixed $user
46 | * @return mixed
47 | */
48 | protected function authenticated(Request $request, $user)
49 | {
50 | if ($this->isTwoFactorEnabled($user)) {
51 | return $this->handleTwoFactorAuthentication($request, $user);
52 | }
53 |
54 | if ($request->ajax()) {
55 | return $this->ajaxRedirectTo($request);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Controllers/RegisterController.php:
--------------------------------------------------------------------------------
1 | middleware('guest');
33 |
34 | $this->users = $users;
35 | }
36 |
37 | /**
38 | * Show the application registration form.
39 | *
40 | * @return \Illuminate\Http\Response
41 | */
42 | public function showRegistrationForm()
43 | {
44 | return view('awesio-auth::auth.register');
45 | }
46 |
47 | /**
48 | * Get a validator for an incoming registration request.
49 | *
50 | * @param array $data
51 | * @return \Illuminate\Contracts\Validation\Validator
52 | */
53 | protected function validator(array $data)
54 | {
55 | return Validator::make($data, [
56 | 'name' => ['required', 'string', 'max:255'],
57 | 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
58 | 'password' => ['required', 'string', 'min:6', 'confirmed'],
59 | ]);
60 | }
61 |
62 | /**
63 | * Create a new user instance after a valid registration.
64 | *
65 | * @param array $data
66 | * @return \App\User
67 | */
68 | protected function create(array $data)
69 | {
70 | return $this->users->store([
71 | 'name' => $data['name'],
72 | 'email' => $data['email'],
73 | 'password' => Hash::make($data['password']),
74 | ]);
75 | }
76 |
77 | /**
78 | * The user has been registered.
79 | *
80 | * @param \Illuminate\Http\Request $request
81 | * @param mixed $user
82 | * @return mixed
83 | */
84 | protected function registered(Request $request, $user)
85 | {
86 | if ($request->ajax()) {
87 | return $this->ajaxRedirectTo($request);
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/Controllers/ResetPasswordController.php:
--------------------------------------------------------------------------------
1 | middleware('guest');
39 | }
40 |
41 | /**
42 | * Display the password reset view for the given token.
43 | *
44 | * If no token is present, display the link request form.
45 | *
46 | * @param \Illuminate\Http\Request $request
47 | * @param string|null $token
48 | * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
49 | */
50 | public function showResetForm(Request $request, $token = null)
51 | {
52 | return view('awesio-auth::auth.passwords.reset')->with(
53 | ['token' => $token, 'email' => $request->email]
54 | );
55 | }
56 |
57 | /**
58 | * Get the response for a successful password reset.
59 | *
60 | * @param \Illuminate\Http\Request $request
61 | * @param string $response
62 | * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
63 | */
64 | protected function sendResetResponse(Request $request, $response)
65 | {
66 | if ($request->ajax()) {
67 | return $this->ajaxRedirectTo($request);
68 | }
69 |
70 | return redirect($this->redirectPath())
71 | ->with('status', trans($response));
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/Controllers/SocialLoginController.php:
--------------------------------------------------------------------------------
1 | users = $users;
49 |
50 | $this->provider = $provider;
51 | }
52 |
53 | /**
54 | * Redirect to OAuth provider authentication page
55 | *
56 | * @param \Illuminate\Http\Request $request
57 | * @param string $service
58 | * @return void
59 | */
60 | public function redirect(Request $request, $service)
61 | {
62 | return $this->provider
63 | ->buildProvider($service)
64 | ->redirect();
65 | }
66 |
67 | /**
68 | * Obtain the user information from OAuth provider
69 | *
70 | * @param \Illuminate\Http\Request $request
71 | * @param string $service
72 | * @return void
73 | */
74 | public function callback(Request $request, $service)
75 | {
76 | $serviceUser = $this->provider
77 | ->buildProvider($service)
78 | ->user();
79 |
80 | $user = $this->users
81 | ->getUserBySocial($serviceUser, $service);
82 |
83 | if (! $user) {
84 | $user = $this->createUser($serviceUser);
85 | }
86 |
87 | if (! $user->hasSocial($service)) {
88 | $this->createUsersSocial($user, $serviceUser, $service);
89 | }
90 |
91 | Auth::login($user);
92 |
93 | return $this->authenticated($request, $user)
94 | ?: redirect()->intended($this->redirectPath());
95 | }
96 |
97 | /**
98 | * Create new user using service credentials
99 | *
100 | * @param \Laravel\Socialite\Two\User $serviceUser
101 | * @return \App\User
102 | */
103 | protected function createUser($serviceUser)
104 | {
105 | return $this->users->store([
106 | 'password' => bcrypt(str_random(40)),
107 | 'name' => $serviceUser->getName() ?: $serviceUser->getNickname(),
108 | 'email' => $serviceUser->getEmail()
109 | ]);
110 | }
111 |
112 | /**
113 | * Create new user's social record using service credentials
114 | *
115 | * @param \App\User $user
116 | * @param \Laravel\Socialite\Two\User $serviceUser
117 | * @param string $service
118 | * @return \AwesIO\Auth\Models\UserSocial
119 | */
120 | protected function createUsersSocial($user, $serviceUser, $service)
121 | {
122 | return $user->social()->create([
123 | 'social_id' => $serviceUser->getId(),
124 | 'service' => $service
125 | ]);
126 | }
127 |
128 | /**
129 | * The user has been authenticated.
130 | *
131 | * @param \Illuminate\Http\Request $request
132 | * @param mixed $user
133 | * @return mixed
134 | */
135 | protected function authenticated(Request $request, $user)
136 | {
137 | if ($this->isTwoFactorEnabled($user)) {
138 | return $this->handleTwoFactorAuthentication($request, $user);
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/Controllers/Traits/AuthenticatesUsersWith2FA.php:
--------------------------------------------------------------------------------
1 | isTwoFactorEnabled();
14 | }
15 |
16 | /**
17 | * Handle 2FA, stores user data in session, logs out and redirects
18 | *
19 | * @param Request $request
20 | * @param $user
21 | * @return \Illuminate\Http\Response
22 | */
23 | protected function handleTwoFactorAuthentication(Request $request, $user)
24 | {
25 | $request->session()->put('two_factor', (object) [
26 | 'user_id' => $user->id,
27 | 'remember' => $request->has('remember')
28 | ]);
29 |
30 | Auth::logout();
31 |
32 | if ($request->ajax()) {
33 | return response()->json([
34 | 'redirectUrl' => route('login.twofactor.index', [], false)
35 | ]);
36 | }
37 |
38 | return redirect()->route('login.twofactor.index');
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Controllers/Traits/RedirectsTo.php:
--------------------------------------------------------------------------------
1 | 'login',
14 | 'AwesIO\Auth\Controllers\RegisterController' => 'register',
15 | 'AwesIO\Auth\Controllers\ResetPasswordController' => 'reset_password',
16 | 'AwesIO\Auth\Controllers\SocialLoginController' => 'social_login',
17 | 'AwesIO\Auth\Controllers\TwoFactorLoginController' => 'twofactor_login',
18 | 'AwesIO\Auth\Controllers\VerificationController' => 'email_verification',
19 | 'AwesIO\Auth\Controllers\ForgotPasswordController' => 'forgot_password',
20 | 'AwesIO\Auth\Controllers\TwoFactorController' => 'twofactor',
21 | ];
22 |
23 | /**
24 | * Redirect to path which set in config or to default one
25 | *
26 | * @return string
27 | */
28 | protected function redirectTo()
29 | {
30 | return $this->getRedirectToUrl()
31 | ?: (property_exists($this, 'redirectTo') ? $this->redirectTo : '/');
32 | }
33 |
34 | protected function ajaxRedirectTo($request)
35 | {
36 | return response()->json([
37 | 'redirectUrl' => redirect()
38 | ->intended($this->getRedirectToUrl())
39 | ->getTargetUrl()
40 | ]);
41 | }
42 |
43 | protected function getRedirectToUrl()
44 | {
45 | return config('awesio-auth.redirects.' . $this->redirectMappings[get_class($this)]);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Controllers/TwoFactorController.php:
--------------------------------------------------------------------------------
1 | user()->isTwoFactorPending()) {
32 | $qrCode = app()->make(TwoFactor::class)->qrCode(auth()->user());
33 | }
34 |
35 | // $countries = Country::all();
36 |
37 | return view('awesio-auth::twofactor.index', compact('countries', 'qrCode'));
38 | }
39 |
40 | /**
41 | * Store new user's two factor record
42 | *
43 | * @param \AwesIO\Auth\Requests\TwoFactorStoreRequest $request
44 | * @param \AwesIO\Auth\Services\Contracts\TwoFactor $twoFactor
45 | * @return void
46 | */
47 | public function store(TwoFactorStoreRequest $request, TwoFactor $twoFactor)
48 | {
49 | $codeAndPhone = preg_split('/\s/', $request->phone, 2);
50 |
51 | $user = $request->user();
52 |
53 | $user->twoFactor()->create([
54 | 'phone' => $codeAndPhone[1],
55 | 'dial_code' => $codeAndPhone[0],
56 | ]);
57 |
58 | if ($response = $twoFactor->register($user)) {
59 | $user->twoFactor()->update([
60 | 'identifier' => $response->user->id
61 | ]);
62 | }
63 | return $this->twofactored($request) ?: back();
64 | }
65 |
66 | /**
67 | * Verify two factor token
68 | *
69 | * @param \AwesIO\Auth\Requests\TwoFactorVerifyRequest $request
70 | * @return \Illuminate\Http\Response
71 | */
72 | public function verify(TwoFactorVerifyRequest $request)
73 | {
74 | $request->user()->twoFactor()->update([
75 | 'verified' => true
76 | ]);
77 |
78 | return $this->twofactored($request) ?: back();
79 | }
80 |
81 | /**
82 | * Remove user from two factor service and application's db
83 | *
84 | * @param \Illuminate\Http\Request $request
85 | * @param \AwesIO\Auth\Services\Contracts\TwoFactor $twoFactor
86 | * @return void
87 | */
88 | public function destroy(Request $request, TwoFactor $twoFactor)
89 | {
90 | if ($twoFactor->remove($user = $request->user())) {
91 |
92 | $user->twoFactor()->delete();
93 | }
94 | return $this->twofactored($request) ?: back();
95 | }
96 |
97 | protected function twofactored(Request $request)
98 | {
99 | if ($request->ajax()) {
100 | return $this->ajaxRedirectTo($request);
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/Controllers/TwoFactorLoginController.php:
--------------------------------------------------------------------------------
1 | user()->id, session('two_factor')->remember);
41 |
42 | session()->forget('two_factor');
43 |
44 | return $this->authenticated($request)
45 | ?: redirect()->intended($this->redirectPath());
46 | }
47 |
48 | /**
49 | * The user has been authenticated.
50 | *
51 | * @param \Illuminate\Http\Request $request
52 | * @param mixed $user
53 | * @return mixed
54 | */
55 | protected function authenticated(Request $request)
56 | {
57 | if ($request->ajax()) {
58 | return $this->ajaxRedirectTo($request);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Controllers/VerificationController.php:
--------------------------------------------------------------------------------
1 | middleware('auth');
42 | $this->middleware('signed')->only('verify');
43 | $this->middleware('throttle:6,1')->only('verify', 'resend');
44 | }
45 |
46 | /**
47 | * Show the email verification by code page.
48 | *
49 | * @param \Illuminate\Http\Request $request
50 | * @return \Illuminate\Http\Response
51 | */
52 | public function show(Request $request)
53 | {
54 | return $request->user()->hasVerifiedEmail()
55 | ? redirect($this->redirectPath())
56 | : view('awesio-auth::auth.verify');
57 | }
58 |
59 | public function verifyCode(Request $request)
60 | {
61 | $code = Session::get('email_verification');
62 |
63 | if (! $this->isValidCode($code, $request->code)) {
64 | abort(403, "Verification code is invalid or has been expired");
65 | }
66 |
67 | Session::forget('email_verification');
68 |
69 | $request->user()->markEmailAsVerified();
70 | }
71 |
72 | /**
73 | * Mark the authenticated user's email address as verified.
74 | *
75 | * @param \Illuminate\Http\Request $request
76 | * @return \Illuminate\Http\Response
77 | * @throws \Illuminate\Auth\Access\AuthorizationException
78 | */
79 | public function verify(Request $request)
80 | {
81 | if ($request->route('id') != $request->user()->getKey()) {
82 | throw new AuthorizationException;
83 | }
84 |
85 | if ($request->user()->hasVerifiedEmail()) {
86 | return redirect($this->redirectPath());
87 | }
88 |
89 | if ($request->user()->markEmailAsVerified()) {
90 | event(new Verified($request->user()));
91 | }
92 |
93 | return redirect($this->redirectPath())->with('verified', true);
94 | }
95 |
96 | /**
97 | * Resend the email verification notification.
98 | *
99 | * @param \Illuminate\Http\Request $request
100 | * @return \Illuminate\Http\Response
101 | */
102 | public function resend(Request $request)
103 | {
104 | if ($request->user()->hasVerifiedEmail()) {
105 | return redirect($this->redirectPath());
106 | }
107 |
108 | $request->user()->sendEmailVerificationNotification();
109 |
110 | // return back()->with('resent', true);
111 | }
112 |
113 | protected function isValidCode($stored, $recieved)
114 | {
115 | return !empty($stored)
116 | && $stored['code'] == $recieved
117 | && now()->lt($stored['expire']);
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/src/Facades/Auth.php:
--------------------------------------------------------------------------------
1 | listen(
13 | 'Illuminate\Auth\Events\Registered',
14 | static::class.'@handleRegistered'
15 | );
16 | }
17 |
18 | public function handleRegistered($event)
19 | {
20 | if (AwesAuth::isEmailVerificationEnabled()
21 | && ! $event->user->hasVerifiedEmail()) {
22 |
23 | $event->user->sendEmailVerificationNotification();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/src/Middlewares/SocialAuthentication.php:
--------------------------------------------------------------------------------
1 | isServiceAvailable($request->service)) {
19 | return redirect()->route('login');
20 | }
21 | return $next($request);
22 | }
23 |
24 | /**
25 | * Check if socialite service is enabled in config
26 | *
27 | * @param string $service
28 | * @return boolean
29 | */
30 | protected function isServiceAvailable($service)
31 | {
32 | return in_array(
33 | strtolower($service),
34 | array_keys(config('awesio-auth.socialite.services'))
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Models/Country.php:
--------------------------------------------------------------------------------
1 | hasMany(UserSocial::class);
17 | }
18 |
19 | /**
20 | * Check if has any \AwesIO\Auth\Models\UserSocial relationships.
21 | *
22 | * @param string $service
23 | * @return boolean
24 | */
25 | public function hasSocial($service)
26 | {
27 | return $this->social->where('service', $service)->count() > 0;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Models/Traits/HasTwoFactorAuthentication.php:
--------------------------------------------------------------------------------
1 | hasOne(TwoFactor::class);
17 | }
18 |
19 | /**
20 | * Check if two-factor auth is enabled
21 | *
22 | * @return boolean
23 | */
24 | public function isTwoFactorEnabled()
25 | {
26 | return (bool) optional($this->twoFactor)->isVerified();
27 | }
28 |
29 | /**
30 | * Check if two-factor verification pending
31 | *
32 | * @return boolean
33 | */
34 | public function isTwoFactorPending()
35 | {
36 | if (! $this->twoFactor) {
37 | return false;
38 | }
39 | return ! $this->twoFactor->isVerified();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Models/Traits/SendsEmailVerification.php:
--------------------------------------------------------------------------------
1 | generateVerificationCode();
19 |
20 | $expire = now()->addMinutes(60);
21 |
22 | if ($mailable = config('awesio-auth.mailables.email_verification')) {
23 |
24 | VerifyEmail::toMailUsing(
25 | function ($notifiable) use ($mailable, $code, $expire) {
26 |
27 | $url = URL::temporarySignedRoute(
28 | 'verification.verify',
29 | $expire,
30 | ['id' => $notifiable->getKey()]
31 | );
32 |
33 | return (new $mailable($code, $url))
34 | ->to($notifiable->email);
35 | }
36 | );
37 | }
38 | Session::put(
39 | 'email_verification',
40 | ['code' => $code, 'expire' => $expire]
41 | );
42 |
43 | $this->notify(new VerifyEmail);
44 | }
45 |
46 | /**
47 | * Generate six digits verification code
48 | *
49 | * @throws \Exception
50 | */
51 | public function generateVerificationCode()
52 | {
53 | $code = '';
54 |
55 | for ($i = 0; $i < 6; $i++) {
56 | $code .= random_int(0, 9);
57 | }
58 |
59 | return $code;
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Models/Traits/SendsPasswordReset.php:
--------------------------------------------------------------------------------
1 | to($notifiable->email);
27 | }
28 | );
29 | }
30 | $this->notify(new ResetPasswordNotification($token));
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Models/TwoFactor.php:
--------------------------------------------------------------------------------
1 | user->twoFactor)->delete();
31 | });
32 | }
33 |
34 | /**
35 | * Define an inverse \App\User relationship.
36 | *
37 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
38 | */
39 | public function user()
40 | {
41 | return $this->belongsTo(getModelForGuard(config('auth.defaults.guard')));
42 | }
43 |
44 | /**
45 | * Determine if two factor auth is verified
46 | *
47 | * @return boolean
48 | */
49 | public function isVerified()
50 | {
51 | return $this->verified;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/Models/UserSocial.php:
--------------------------------------------------------------------------------
1 | belongsTo(getModelForGuard(config('auth.defaults.guard')));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Repositories/Contracts/UserRepository.php:
--------------------------------------------------------------------------------
1 | getEmail())
19 | ->orWhereHas('social',
20 | function ($query) use ($serviceUser, $service) {
21 | $query->where('social_id', $serviceUser->getId())->where('service', $service);
22 | }
23 | )->first();
24 | }
25 |
26 | /**
27 | * Create new user
28 | *
29 | * @param array $data
30 | * @return
31 | */
32 | public function store(array $data)
33 | {
34 | return getModelForGuard(config('auth.defaults.guard'))::create($data);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/Requests/TwoFactorStoreRequest.php:
--------------------------------------------------------------------------------
1 | ['required', new ValidPhone],
29 | // 'dial_code' => 'required|exists:countries,dial_code',
30 | ];
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Requests/TwoFactorVerifyRequest.php:
--------------------------------------------------------------------------------
1 | twoFactor = $twoFactor;
21 | }
22 |
23 | /**
24 | * Determine if the user is authorized to make this request.
25 | *
26 | * @return bool
27 | */
28 | public function authorize()
29 | {
30 | return true;
31 | }
32 |
33 | /**
34 | * Get the validation rules that apply to the request.
35 | *
36 | * @return array
37 | */
38 | public function rules()
39 | {
40 | if ($data = session('two_factor')) {
41 | $this->setUserResolver($this->userResolver($data));
42 | }
43 |
44 | return [
45 | 'token' => [
46 | 'required',
47 | new ValidTwoFactorToken($this->user(), $this->twoFactor)
48 | ],
49 | ];
50 | }
51 |
52 | /**
53 | * Get custom user resolver
54 | *
55 | * @param object $data
56 | * @return \Closure
57 | */
58 | protected function userResolver($data)
59 | {
60 | return function () use ($data) {
61 | return getModelForGuard(config('auth.defaults.guard'))::find($data->user_id);
62 | };
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Rules/ValidPhone.php:
--------------------------------------------------------------------------------
1 | user = $user;
30 |
31 | $this->twoFactor = $twoFactor;
32 | }
33 |
34 | /**
35 | * Determine if the validation rule passes.
36 | *
37 | * @param string $attribute
38 | * @param mixed $value
39 | * @return bool
40 | */
41 | public function passes($attribute, $value)
42 | {
43 | return $this->twoFactor->verifyToken($this->user, $value);
44 | }
45 |
46 | /**
47 | * Get the validation error message.
48 | *
49 | * @return string
50 | */
51 | public function message()
52 | {
53 | return 'Invalid two factor token';
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Seeds/CountryTableSeeder.php:
--------------------------------------------------------------------------------
1 | 'Germany',
20 | 'code' => 'DE',
21 | 'dial_code' => '49',
22 | ],
23 | [
24 | 'name' => 'Russia',
25 | 'code' => 'RU',
26 | 'dial_code' => '7',
27 | ],
28 | [
29 | 'name' => 'Ukraine',
30 | 'code' => 'UA',
31 | 'dial_code' => '380',
32 | ],
33 | ];
34 |
35 | Country::insert($countries);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Services/AuthyTwoFactor.php:
--------------------------------------------------------------------------------
1 | client = $client;
20 | }
21 |
22 | /**
23 | * Register user on Authy
24 | *
25 | * @param $user
26 | * @return boolean|object
27 | */
28 | public function register($user)
29 | {
30 | try {
31 | $response = $this->client->request(
32 | 'POST',
33 | 'https://api.authy.com/protected/json/users/new?api_key='
34 | . config('awesio-auth.two_factor.authy.secret'), [
35 | 'form_params' => $this->getUserRegistrationPayload($user)
36 | ]
37 | );
38 | } catch (\Exception $e) {
39 | return false;
40 | }
41 | return json_decode($response->getBody());
42 | }
43 |
44 | /**
45 | * Verify if 2FA token is valid
46 | *
47 | * @param $user
48 | * @param string $token
49 | * @return boolean
50 | */
51 | public function verifyToken($user, $token)
52 | {
53 | try {
54 | $response = $this->client->request(
55 | 'GET',
56 | 'https://api.authy.com/protected/json/verify/'
57 | . $token . '/' . $user->twoFactor->identifier
58 | . '?force=true&api_key=' . config('awesio-auth.two_factor.authy.secret')
59 | );
60 | } catch (\Exception $e) {
61 | return false;
62 | }
63 |
64 | $response = json_decode($response->getBody());
65 |
66 | return $response->token === 'is valid';
67 | }
68 |
69 | /**
70 | * Remove user from Authy
71 | *
72 | * @param $user
73 | * @return boolean
74 | */
75 | public function remove($user)
76 | {
77 | try {
78 | $response = $this->client->request(
79 | 'POST',
80 | 'https://api.authy.com/protected/json/users/delete/'
81 | . $user->twoFactor->identifier
82 | . '?api_key=' . config('awesio-auth.two_factor.authy.secret')
83 | );
84 | } catch (\Exception $e) {
85 | return false;
86 | }
87 | return true;
88 | }
89 |
90 | /**
91 | * Request QR code link.
92 | *
93 | * @param array $user
94 | *
95 | * @return mixed
96 | */
97 | public function qrCode($user)
98 | {
99 | try {
100 | $response = $this->client->request(
101 | 'POST',
102 | 'https://api.authy.com/protected/json/users/'
103 | . $user->twoFactor->identifier
104 | . '/secret?api_key=' . config('awesio-auth.two_factor.authy.secret')
105 | );
106 | } catch (\Exception $e) {
107 | return false;
108 | }
109 | return json_decode($response->getBody());
110 | }
111 |
112 | /**
113 | * Get data needed for user registration on Authy
114 | *
115 | * @param $user
116 | * @return array
117 | */
118 | protected function getUserRegistrationPayload($user)
119 | {
120 | return [
121 | 'user' => [
122 | 'email' => $user->email,
123 | 'cellphone' => $user->twoFactor->phone,
124 | 'country_code' => $user->twoFactor->dial_code
125 | ]
126 | ];
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/Services/Contracts/SocialProvidersManager.php:
--------------------------------------------------------------------------------
1 | providerClassName(ucfirst($service)),
20 | $this->getConfig($service)
21 | );
22 | }
23 |
24 | /**
25 | * Generate provider full class name
26 | *
27 | * @param string $prefix
28 | * @return string
29 | */
30 | protected function providerClassName($prefix)
31 | {
32 | return "\Laravel\Socialite\Two\\{$prefix}Provider";
33 | }
34 |
35 | /**
36 | * Get provider credentials from package config
37 | *
38 | * @param string $service
39 | * @return array
40 | */
41 | protected function getConfig($service)
42 | {
43 | return config('awesio-auth.socialite.' . $service);
44 | }
45 | }
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | map(function ($guard) {
13 | if (! isset($guard['provider'])) {
14 | return;
15 | }
16 | return config("auth.providers.{$guard['provider']}.model");
17 | })->get($guard);
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/Feature/AuthRoutesTest.php:
--------------------------------------------------------------------------------
1 | get('login');
14 |
15 | $response->assertStatus(200);
16 | }
17 |
18 | /** @test */
19 | public function it_has_post_login_route()
20 | {
21 | $response = $this->post('login');
22 |
23 | $response->assertStatus(302);
24 | }
25 |
26 | /** @test */
27 | public function it_has_logout_route()
28 | {
29 | $response = $this->post('logout');
30 |
31 | $response->assertStatus(302);
32 | }
33 |
34 | /** @test */
35 | public function it_has_get_registration_route()
36 | {
37 | $response = $this->get('register');
38 |
39 | $response->assertStatus(200);
40 | }
41 |
42 | /** @test */
43 | public function it_has_post_registration_route()
44 | {
45 | $response = $this->post('register');
46 |
47 | $response->assertStatus(302);
48 | }
49 |
50 | /** @test */
51 | public function it_has_login_social_route()
52 | {
53 | $response = $this->get('login/service');
54 |
55 | $response->assertStatus(302);
56 | }
57 |
58 | /** @test */
59 | public function it_has_login_social_callback_route()
60 | {
61 | $response = $this->get('login/service/callback');
62 |
63 | $response->assertStatus(302);
64 | }
65 |
66 | /** @test */
67 | public function it_has_two_factor_index_route()
68 | {
69 | $response = $this->get('twofactor');
70 |
71 | $response->assertStatus(302);
72 | }
73 |
74 | /** @test */
75 | public function it_has_two_factor_store_route()
76 | {
77 | $response = $this->post('twofactor');
78 |
79 | $response->assertStatus(302);
80 | }
81 |
82 | /** @test */
83 | public function it_has_two_factor_verify_route()
84 | {
85 | $response = $this->post('twofactor/verify');
86 |
87 | $response->assertStatus(302);
88 | }
89 |
90 | /** @test */
91 | public function it_has_two_factor_delete_route()
92 | {
93 | $response = $this->delete('twofactor');
94 |
95 | $response->assertStatus(302);
96 | }
97 |
98 | /** @test */
99 | public function it_has_login_two_factor_index_route()
100 | {
101 | $response = $this->get('login/twofactor/verify');
102 |
103 | $response->assertStatus(200);
104 | }
105 |
106 | /** @test */
107 | public function it_has_login_verification_code_route()
108 | {
109 | $response = $this->get('email/verify');
110 |
111 | $response->assertStatus(302);
112 | }
113 |
114 | /** @test */
115 | public function it_has_login_verification_code_verify_route()
116 | {
117 | $response = $this->post('email/verify');
118 |
119 | $response->assertStatus(302);
120 | }
121 |
122 | /** @test */
123 | public function it_has_login_verification_verify_route()
124 | {
125 | $response = $this->get('email/verify/1');
126 |
127 | $response->assertStatus(302);
128 | }
129 |
130 | /** @test */
131 | public function it_has_login_verification_resend_route()
132 | {
133 | $response = $this->get('email/resend');
134 |
135 | $response->assertStatus(302);
136 | }
137 | }
--------------------------------------------------------------------------------
/tests/Feature/ForgotPasswordTest.php:
--------------------------------------------------------------------------------
1 | get('password/reset')
13 | ->assertViewIs('awesio-auth::auth.passwords.email');
14 | }
15 | }
--------------------------------------------------------------------------------
/tests/Feature/LoginTest.php:
--------------------------------------------------------------------------------
1 | get('login')
15 | ->assertViewIs('awesio-auth::auth.login');
16 | }
17 |
18 | /** @test */
19 | public function email_is_required()
20 | {
21 | $this->json('POST', 'login')
22 | ->assertJsonValidationErrors(['email']);
23 | }
24 |
25 | /** @test */
26 | public function password_is_required()
27 | {
28 | $this->json('POST', 'login')
29 | ->assertJsonValidationErrors(['password']);
30 | }
31 |
32 | /** @test */
33 | public function it_can_login_user()
34 | {
35 | $user = factory(User::class)->create();
36 |
37 | $this->json('POST', 'login', [
38 | 'email' => $user->email,
39 | 'password' => 'secret'
40 | ]);
41 |
42 | $this->assertAuthenticatedAs($user);
43 | }
44 |
45 | /** @test */
46 | public function it_cant_login_without_valid_password()
47 | {
48 | $user = factory(User::class)->create();
49 |
50 | $this->json('POST', 'login', [
51 | 'email' => $user->email,
52 | 'password' => 'password'
53 | ]);
54 |
55 | $this->assertGuest();
56 | }
57 |
58 | /** @test */
59 | public function it_responds_with_json_to_ajax_request_after_successful_login()
60 | {
61 | $user = factory(User::class)->create();
62 |
63 | $this->json('POST', 'login', [
64 | 'email' => $user->email,
65 | 'password' => 'secret'
66 | ], ['X-Requested-With' => 'XMLHttpRequest']
67 | )->assertExactJson([
68 | 'redirectUrl' => 'http://localhost' . config('awesio-auth.redirects.login')
69 | ]);
70 | }
71 |
72 | /** @test */
73 | public function it_handles_two_factor_authentication_and_redirects()
74 | {
75 | $user = factory(User::class)->create();
76 |
77 | factory(TwoFactor::class)->create([
78 | 'user_id' => $user->id,
79 | 'verified' => 1
80 | ]);
81 |
82 | $this->json('POST', 'login', [
83 | 'email' => $user->email,
84 | 'password' => 'secret'
85 | ])->assertRedirect('/login/twofactor/verify');
86 |
87 | $this->assertGuest();
88 | }
89 | }
--------------------------------------------------------------------------------
/tests/Feature/RegisterTest.php:
--------------------------------------------------------------------------------
1 | get('register')
14 | ->assertViewIs('awesio-auth::auth.register');
15 | }
16 |
17 | /** @test */
18 | public function name_is_required()
19 | {
20 | $this->json('POST', 'register')
21 | ->assertJsonValidationErrors(['name']);
22 | }
23 |
24 | /** @test */
25 | public function email_is_required()
26 | {
27 | $this->json('POST', 'register')
28 | ->assertJsonValidationErrors(['email']);
29 | }
30 |
31 | /** @test */
32 | public function password_is_required()
33 | {
34 | $this->json('POST', 'register')
35 | ->assertJsonValidationErrors(['password']);
36 | }
37 |
38 | /** @test */
39 | public function it_can_register_user()
40 | {
41 | Event::fake();
42 |
43 | $this->post('register', [
44 | 'name' => $name = uniqid(),
45 | 'email' => 'email@email.com',
46 | 'password' => $password = uniqid(),
47 | 'password_confirmation' => $password,
48 | ]);
49 |
50 | $this->assertDatabaseHas('users', [
51 | 'id' => 1,
52 | 'name' => $name,
53 | ]);
54 | }
55 | }
--------------------------------------------------------------------------------
/tests/Feature/ResetPasswordTest.php:
--------------------------------------------------------------------------------
1 | get('password/reset')
16 | ->assertViewIs('awesio-auth::auth.passwords.email');
17 | }
18 |
19 | /** @test */
20 | public function it_returns_view_on_password_reset()
21 | {
22 | $this->get('password/reset/token')
23 | ->assertViewIs('awesio-auth::auth.passwords.reset');
24 | }
25 |
26 | /** @test */
27 | public function it_sends_password_reset_email()
28 | {
29 | $user = factory(User::class)->create();
30 |
31 | Notification::fake();
32 |
33 | $this->post('password/email', ['email' => $user->email]);
34 |
35 | Notification::assertSentTo($user, ResetPasswordNotification::class);
36 | }
37 | }
--------------------------------------------------------------------------------
/tests/Feature/SocialLoginTest.php:
--------------------------------------------------------------------------------
1 | get('login/github')
19 | ->assertSee('Redirecting to https://github.com/login/oauth/');
20 | }
21 |
22 | /** @test */
23 | public function it_creates_new_user_on_callback()
24 | {
25 | $name = uniqid();
26 |
27 | $email = uniqid();
28 |
29 | $serviceUserMock = $this->mock('serviceUser', function ($mock) use ($name, $email) {
30 | $mock->shouldReceive('getName')
31 | ->once()
32 | ->andReturn($name);
33 | $mock->shouldReceive('getEmail')
34 | ->twice()
35 | ->andReturn($email);
36 | $mock->shouldReceive('getId')
37 | ->twice()
38 | ->andReturn(uniqid());
39 | });
40 |
41 | $serviceMock = $this->mock('service', function ($mock) use ($serviceUserMock) {
42 | $mock->shouldReceive('user')
43 | ->andReturn($serviceUserMock);
44 | });
45 |
46 | $this->mock(SocialProvidersManager::class, function ($mock) use ($serviceMock) {
47 | $mock->shouldReceive('buildProvider')
48 | ->once()
49 | ->with('github')
50 | ->andReturn($serviceMock);
51 | });
52 |
53 | $this->get('login/github/callback');
54 |
55 | $this->assertDatabaseHas('users', [
56 | 'name' => $name,
57 | 'email' => $email,
58 | ]);
59 | }
60 |
61 | /** @test */
62 | public function it_creates_user_social_on_callback()
63 | {
64 | $socialId = uniqid();
65 |
66 | $serviceUserMock = $this->mock('serviceUser', function ($mock) use ($socialId) {
67 | $mock->shouldReceive('getName')
68 | ->once()
69 | ->andReturn(uniqid());
70 | $mock->shouldReceive('getEmail')
71 | ->twice()
72 | ->andReturn(uniqid());
73 | $mock->shouldReceive('getId')
74 | ->twice()
75 | ->andReturn($socialId);
76 | });
77 |
78 | $serviceMock = $this->mock('service', function ($mock) use ($serviceUserMock) {
79 | $mock->shouldReceive('user')
80 | ->andReturn($serviceUserMock);
81 | });
82 |
83 | $this->mock(SocialProvidersManager::class, function ($mock) use ($serviceMock) {
84 | $mock->shouldReceive('buildProvider')
85 | ->once()
86 | ->with('github')
87 | ->andReturn($serviceMock);
88 | });
89 |
90 | $this->get('login/github/callback');
91 |
92 | $this->assertDatabaseHas('users_social', [
93 | 'service' => 'github',
94 | 'social_id' => $socialId,
95 | ]);
96 | }
97 |
98 | /** @test */
99 | public function it_authenticates_user_on_callback()
100 | {
101 | $serviceUserMock = $this->mock('serviceUser', function ($mock) {
102 | $mock->shouldReceive('getName')
103 | ->once()
104 | ->andReturn(uniqid());
105 | $mock->shouldReceive('getEmail')
106 | ->twice()
107 | ->andReturn(uniqid());
108 | $mock->shouldReceive('getId')
109 | ->twice()
110 | ->andReturn(uniqid());
111 | });
112 |
113 | $serviceMock = $this->mock('service', function ($mock) use ($serviceUserMock) {
114 | $mock->shouldReceive('user')
115 | ->andReturn($serviceUserMock);
116 | });
117 |
118 | $this->mock(SocialProvidersManager::class, function ($mock) use ($serviceMock) {
119 | $mock->shouldReceive('buildProvider')
120 | ->once()
121 | ->with('github')
122 | ->andReturn($serviceMock);
123 | });
124 |
125 | $this->get('login/github/callback');
126 |
127 | $this->assertAuthenticated();
128 | }
129 | }
--------------------------------------------------------------------------------
/tests/Feature/TwoFactorLoginTest.php:
--------------------------------------------------------------------------------
1 | get('login/twofactor/verify')
14 | ->assertViewIs('awesio-auth::twofactor.verify');
15 | }
16 |
17 | /** @test */
18 | public function it_verifies_two_factor_auth()
19 | {
20 | $user = factory(User::class)->create();
21 |
22 | $this->withSession(['two_factor' => (object)[
23 | 'user_id' => $user->id,
24 | 'remember' => true
25 | ]])->post('login/twofactor/verify', [
26 | 'token' => uniqid()
27 | ])->assertStatus(302);
28 | }
29 | }
--------------------------------------------------------------------------------
/tests/Feature/TwoFactorTest.php:
--------------------------------------------------------------------------------
1 | artisan('db:seed', ['--class' => 'AwesIO\Auth\Seeds\CountryTableSeeder']);
15 | }
16 |
17 | /** @test */
18 | public function it_returns_view_on_index()
19 | {
20 | $user = factory(User::class)->create();
21 |
22 | $this->actingAs($user)->post('twofactor', [
23 | 'phone' => ($code = '+7') . ' ' . ($phone = '999 999-99-99'),
24 | ]);
25 |
26 | $this->actingAs($user)->get('twofactor')
27 | ->assertViewIs('awesio-auth::twofactor.index')->assertViewHas('qrCode');
28 | }
29 |
30 | /** @test */
31 | public function it_stores_new_user_two_factor_record()
32 | {
33 | $user = factory(User::class)->create();
34 |
35 | $this->actingAs($user)->post('twofactor', [
36 | 'phone' => ($code = '+7') . ' ' . ($phone = '999 999-99-99'),
37 | ]);
38 |
39 | $this->assertDatabaseHas('two_factor', [
40 | 'user_id' => $user->id,
41 | 'phone' => $phone,
42 | 'dial_code' => $code
43 | ]);
44 | }
45 |
46 | /** @test */
47 | public function it_verifies_user_two_factor_record()
48 | {
49 | $user = factory(User::class)->create();
50 |
51 | $this->actingAs($user)->post('twofactor', [
52 | 'phone' => ($code = '+7') . ' ' . ($phone = '999 999-99-99'),
53 | ]);
54 |
55 | $this->assertDatabaseHas('two_factor', [
56 | 'user_id' => $user->id,
57 | 'phone' => $phone,
58 | 'dial_code' => $code,
59 | 'verified' => 0
60 | ]);
61 |
62 | $this->actingAs($user)->post('twofactor/verify', [
63 | 'token' => uniqid()
64 | ]);
65 |
66 | $this->assertDatabaseHas('two_factor', [
67 | 'user_id' => $user->id,
68 | 'phone' => $phone,
69 | 'dial_code' => $code,
70 | 'verified' => 1
71 | ]);
72 | }
73 |
74 | /** @test */
75 | public function it_destroys_user_two_factor_record()
76 | {
77 | $user = factory(User::class)->create();
78 |
79 | $this->actingAs($user)->post('twofactor', [
80 | 'phone' => ($code = '+7') . ' ' . ($phone = '999 999-99-99'),
81 | ]);
82 |
83 | $this->assertDatabaseHas('two_factor', [
84 | 'user_id' => $user->id,
85 | 'phone' => $phone,
86 | 'dial_code' => $code,
87 | ]);
88 |
89 | $this->actingAs($user)->delete('twofactor', [], array('HTTP_X-Requested-With' => 'XMLHttpRequest'));
90 |
91 | $this->assertDatabaseMissing('two_factor', [
92 | 'user_id' => $user->id,
93 | 'phone' => $phone,
94 | 'dial_code' => $code,
95 | ]);
96 | }
97 | }
--------------------------------------------------------------------------------
/tests/Feature/VerificationTest.php:
--------------------------------------------------------------------------------
1 | create([
17 | 'email_verified_at' => null
18 | ]);
19 |
20 | $this->actingAs($user)->get('email/verify')
21 | ->assertViewIs('awesio-auth::auth.verify');
22 | }
23 |
24 | /** @test */
25 | public function it_verifies_email_using_code()
26 | {
27 | $user = factory(User::class)->create([
28 | 'email_verified_at' => null
29 | ]);
30 |
31 | $this->assertDatabaseHas('users', [
32 | 'id' => $user->id,
33 | 'email_verified_at' => null
34 | ]);
35 |
36 | $code = $user->generateVerificationCode();
37 |
38 | $this->actingAs($user)->withSession(
39 | ['email_verification' =>
40 | [
41 | 'code' => $code, 'expire' => now()->addMinutes(60)
42 | ]
43 | ]
44 | )->post('email/verify', ['code' => $code]);
45 |
46 | $this->assertDatabaseMissing('users', [
47 | 'id' => $user->id,
48 | 'email_verified_at' => null
49 | ]);
50 | }
51 |
52 | /** @test */
53 | public function it_fails_email_verification_if_code_not_stored()
54 | {
55 | $user = factory(User::class)->create([
56 | 'email_verified_at' => null
57 | ]);
58 |
59 | $code = $user->generateVerificationCode();
60 |
61 | $this->actingAs($user)->post('email/verify', ['code' => $code])
62 | ->assertStatus(403);
63 |
64 | $this->assertDatabaseHas('users', [
65 | 'id' => $user->id,
66 | 'email_verified_at' => null
67 | ]);
68 | }
69 |
70 | /** @test */
71 | public function it_fails_email_verification_if_code_is_not_valid()
72 | {
73 | $user = factory(User::class)->create([
74 | 'email_verified_at' => null
75 | ]);
76 |
77 | $code = $user->generateVerificationCode();
78 |
79 | $this->actingAs($user)->withSession(
80 | ['email_verification' =>
81 | [
82 | 'code' => $user->generateVerificationCode(),
83 | 'expire' => now()->addMinutes(60)
84 | ]
85 | ]
86 | )->post('email/verify', ['code' => $code])->assertStatus(403);
87 |
88 | $this->assertDatabaseHas('users', [
89 | 'id' => $user->id,
90 | 'email_verified_at' => null
91 | ]);
92 | }
93 |
94 | /** @test */
95 | public function it_fails_email_verification_if_code_is_expired()
96 | {
97 | $user = factory(User::class)->create([
98 | 'email_verified_at' => null
99 | ]);
100 |
101 | $code = $user->generateVerificationCode();
102 |
103 | $this->actingAs($user)->withSession(
104 | ['email_verification' =>
105 | [
106 | 'code' => $code, 'expire' => now()->subMinute()
107 | ]
108 | ]
109 | )->post('email/verify', ['code' => $code])->assertStatus(403);
110 |
111 | $this->assertDatabaseHas('users', [
112 | 'id' => $user->id,
113 | 'email_verified_at' => null
114 | ]);
115 | }
116 |
117 | /** @test */
118 | public function it_verifies_email_using_link()
119 | {
120 | $user = factory(User::class)->create([
121 | 'email_verified_at' => null
122 | ]);
123 |
124 | $this->assertDatabaseHas('users', [
125 | 'id' => $user->id,
126 | 'email_verified_at' => null
127 | ]);
128 |
129 | $url = URL::temporarySignedRoute(
130 | 'verification.verify',
131 | $expire = now()->addMinutes(60),
132 | ['id' => $user->getKey()]
133 | );
134 |
135 | $this->actingAs($user)->get($url);
136 |
137 | $this->assertDatabaseMissing('users', [
138 | 'id' => $user->id,
139 | 'email_verified_at' => null
140 | ]);
141 | }
142 |
143 | /** @test */
144 | public function it_fails_email_verification_if_link_is_not_valid()
145 | {
146 | $user = factory(User::class)->create([
147 | 'email_verified_at' => null
148 | ]);
149 |
150 | $url = URL::temporarySignedRoute(
151 | 'verification.verify',
152 | $expire = now()->addMinutes(60),
153 | ['id' => $user->getKey()]
154 | );
155 |
156 | $this->actingAs($user)->get($url . 1)->assertStatus(403);
157 |
158 | $this->assertDatabaseHas('users', [
159 | 'id' => $user->id,
160 | 'email_verified_at' => null
161 | ]);
162 | }
163 |
164 | /** @test */
165 | public function it_fails_email_verification_using_link_if_id_is_invalid()
166 | {
167 | $user = factory(User::class)->create([
168 | 'email_verified_at' => null
169 | ]);
170 |
171 | $url = URL::temporarySignedRoute(
172 | 'verification.verify',
173 | $expire = now()->addMinutes(60),
174 | ['id' => $user->getKey() + 1]
175 | );
176 |
177 | $this->actingAs($user)->get($url)->assertStatus(403);
178 |
179 | $this->assertDatabaseHas('users', [
180 | 'id' => $user->id,
181 | 'email_verified_at' => null
182 | ]);
183 | }
184 |
185 | /** @test */
186 | public function it_resends_verification_email()
187 | {
188 | $user = factory(User::class)->create([
189 | 'email_verified_at' => null
190 | ]);
191 |
192 | Notification::fake();
193 |
194 | $this->actingAs($user)->get('email/resend');
195 |
196 | Notification::assertSentTo($user, VerifyEmail::class);
197 | }
198 | }
--------------------------------------------------------------------------------
/tests/Stubs/TwoFactor.php:
--------------------------------------------------------------------------------
1 | id = uniqid();
32 | $respose->user = $user;
33 | return $respose;
34 | }
35 |
36 | /**
37 | * Verify if 2FA token is valid
38 | *
39 | * @param \App\User $user
40 | * @param string $token
41 | * @return boolean
42 | */
43 | public function verifyToken($user, $token)
44 | {
45 | return true;
46 | }
47 |
48 | /**
49 | * Remove user from Authy
50 | *
51 | * @param \App\User $user
52 | * @return boolean
53 | */
54 | public function remove($user)
55 | {
56 | return true;
57 | }
58 |
59 | /**
60 | * Request QR code link.
61 | *
62 | * @param string $authy_id User's id stored in your database
63 | * @param array $opts Array of options, for example: array("qr_size" => 300)
64 | *
65 | * @return mixed
66 | */
67 | public function qrCode($user)
68 | {
69 | $respose = new \stdClass();
70 | $respose->qr_code = uniqid();
71 | return $respose;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/tests/Stubs/User.php:
--------------------------------------------------------------------------------
1 | loadLaravelMigrations(['--database' => 'testing']);
24 |
25 | $this->assignRouteActionMiddlewares();
26 |
27 | $this->withFactories(__DIR__ . '/../database/factories');
28 |
29 | $this->app->singleton(TwoFactorContract::class, TwoFactor::class);
30 | }
31 |
32 | /**
33 | * Define environment setup.
34 | *
35 | * @param \Illuminate\Foundation\Application $app
36 | * @return void
37 | */
38 | protected function getEnvironmentSetUp($app)
39 | {
40 | $app['config']->set('app.debug', env('APP_DEBUG', true));
41 |
42 | $app['config']->set('auth.providers.users.model', User::class);
43 |
44 | $this->setUpDatabase($app);
45 |
46 | $app->register( \Laravel\Socialite\SocialiteServiceProvider::class);
47 | }
48 |
49 | /**
50 | * Load package service provider
51 | * @param \Illuminate\Foundation\Application $app
52 | * @return array
53 | */
54 | protected function getPackageProviders($app)
55 | {
56 | return [
57 | AuthServiceProvider::class
58 | ];
59 | }
60 |
61 | /**
62 | * Load package alias
63 | * @param \Illuminate\Foundation\Application $app
64 | * @return array
65 | */
66 | protected function getPackageAliases($app)
67 | {
68 | return [
69 | 'AwesAuth' => Auth::class,
70 | ];
71 | }
72 |
73 | protected function assignRouteActionMiddlewares()
74 | {
75 | $actions = [
76 | 'AwesIO\Auth\Controllers\LoginController@showLoginForm',
77 | 'AwesIO\Auth\Controllers\LoginController@login',
78 | 'AwesIO\Auth\Controllers\LoginController@logout',
79 | 'AwesIO\Auth\Controllers\RegisterController@showRegistrationForm',
80 | 'AwesIO\Auth\Controllers\TwoFactorLoginController@index',
81 | 'AwesIO\Auth\Controllers\SocialLoginController@redirect',
82 | 'AwesIO\Auth\Controllers\SocialLoginController@callback',
83 | 'AwesIO\Auth\Controllers\TwoFactorController@index',
84 | 'AwesIO\Auth\Controllers\ForgotPasswordController@showLinkRequestForm',
85 | 'AwesIO\Auth\Controllers\ResetPasswordController@showResetForm',
86 | 'AwesIO\Auth\Controllers\VerificationController@show',
87 | ];
88 |
89 | $middlwares = ['web'];
90 |
91 | foreach ($actions as $action) {
92 | app('router')->getRoutes()->getByAction($action)
93 | ->middleware($middlwares);
94 | }
95 | }
96 |
97 | protected function setUpDatabase($app)
98 | {
99 | $builder = $app['db']->connection()->getSchemaBuilder();
100 |
101 | $builder->create('two_factor', function (Blueprint $table) {
102 | $table->increments('id');
103 | $table->integer('user_id')->unsigned()->index();
104 | $table->string('identifier')->nullable();
105 | $table->string('phone');
106 | $table->string('dial_code');
107 | $table->boolean('verified')->default(false);
108 | $table->timestamps();
109 | });
110 |
111 | $builder->create('countries', function (Blueprint $table) {
112 | $table->increments('id');
113 | $table->string('name');
114 | $table->string('code', 2);
115 | $table->string('dial_code');
116 | });
117 |
118 | $builder->create('users_social', function (Blueprint $table) {
119 | $table->increments('id');
120 | $table->integer('user_id')->unsigned()->index();
121 | $table->string('social_id');
122 | $table->string('service');
123 | $table->timestamps();
124 |
125 | $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
126 | });
127 | }
128 | }
--------------------------------------------------------------------------------
/tests/Unit/Listeners/EventSubscriberTest.php:
--------------------------------------------------------------------------------
1 | mock(User::class, function ($mock) {
20 | $mock->shouldReceive('hasVerifiedEmail')
21 | ->once()
22 | ->andReturn(false);
23 | $mock->shouldReceive('sendEmailVerificationNotification')
24 | ->once()
25 | ->andReturn(true);
26 | });
27 |
28 | $event = new Registered($user);
29 |
30 | $subscriber = new EventSubscriber();
31 |
32 | $subscriber->handleRegistered($event);
33 | }
34 |
35 | /** @test */
36 | public function it_doesnt_send_verification_email_if_user_already_verified_it()
37 | {
38 | $this->app['config']->set('app.debug', env('APP_DEBUG', true));
39 |
40 | $user = $this->mock(User::class, function ($mock) {
41 | $mock->shouldReceive('hasVerifiedEmail')
42 | ->once()
43 | ->andReturn(true);
44 | });
45 |
46 | $event = new Registered($user);
47 |
48 | $subscriber = new EventSubscriber();
49 |
50 | $subscriber->handleRegistered($event);
51 | }
52 |
53 | /** @test */
54 | public function it_doesnt_send_verification_email_if_its_disabled()
55 | {
56 | $this->app['config']->set('awesio-auth.enabled', []);
57 |
58 | $user = $this->mock(User::class, function ($mock) {
59 | $mock->shouldNotReceive('hasVerifiedEmail');
60 | $mock->shouldNotReceive('sendEmailVerificationNotification');
61 | });
62 |
63 | $event = new Registered($user);
64 |
65 | $subscriber = new EventSubscriber();
66 |
67 | $subscriber->handleRegistered($event);
68 | }
69 | }
--------------------------------------------------------------------------------
/tests/Unit/Models/UserSocialTest.php:
--------------------------------------------------------------------------------
1 | create();
18 |
19 | $social = factory(UserSocial::class)->create([
20 | 'user_id' => $user->id
21 | ]);
22 |
23 | $this->assertInstanceOf(User::class, $social->user);
24 | }
25 | }
--------------------------------------------------------------------------------
/tests/Unit/Repositories/UserRepositoryTest.php:
--------------------------------------------------------------------------------
1 | create();
20 |
21 | $mock = $this->mock(SocialUser::class, function ($mock) use ($user) {
22 | $mock->shouldReceive('getEmail')
23 | ->once()
24 | ->andReturn($user->email);
25 | $mock->shouldReceive('getId')
26 | ->once()
27 | ->andReturn(uniqid());
28 | });
29 |
30 | $repository = new EloquentUserRepository();
31 |
32 | $this->assertInstanceOf(User::class, $repository->getUserBySocial($mock, 'github'));
33 | }
34 |
35 | /** @test */
36 | public function it_returns_user_by_social_id()
37 | {
38 | $user = factory(User::class)->create();
39 |
40 | DB::table('users_social')->insert([
41 | 'user_id' => $user->id,
42 | 'service' => $sevice = 'github',
43 | 'social_id' => $socialId = uniqid()
44 | ]);
45 |
46 | $mock = $this->mock(SocialUser::class, function ($mock) use ($socialId) {
47 | $mock->shouldReceive('getEmail')
48 | ->once()
49 | ->andReturn(uniqid());
50 | $mock->shouldReceive('getId')
51 | ->once()
52 | ->andReturn($socialId);
53 | });
54 |
55 | $repository = new EloquentUserRepository();
56 |
57 | $this->assertInstanceOf(User::class, $repository->getUserBySocial($mock, $sevice));
58 | }
59 | }
--------------------------------------------------------------------------------
/tests/Unit/Services/AuthyTest.php:
--------------------------------------------------------------------------------
1 | create();
21 |
22 | factory(TwoFactor::class)->create([
23 | 'user_id' => $user->id
24 | ]);
25 |
26 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
27 | $mock->shouldReceive('request')
28 | ->with(
29 | 'POST',
30 | 'https://api.authy.com/protected/json/users/new?api_key=',
31 | ['form_params' => $this->getUserRegistrationPayload($user)]
32 | )
33 | ->once()
34 | ->andThrow(\Exception::class);
35 | });
36 |
37 | $authy = new AuthyTwoFactor($mock);
38 |
39 | $this->assertFalse($authy->register($user));
40 | }
41 |
42 | /** @test */
43 | public function it_registers_new_user()
44 | {
45 |
46 | $user = factory(User::class)->create();
47 |
48 | factory(TwoFactor::class)->create([
49 | 'user_id' => $user->id
50 | ]);
51 |
52 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
53 | $mock->shouldReceive('request')
54 | ->with(
55 | 'POST',
56 | 'https://api.authy.com/protected/json/users/new?api_key=',
57 | ['form_params' => $this->getUserRegistrationPayload($user)]
58 | )
59 | ->once()
60 | ->andReturn(new Response);
61 | });
62 |
63 | $authy = new AuthyTwoFactor($mock);
64 |
65 | $this->isNull($authy->register($user));
66 | }
67 |
68 | /** @test */
69 | public function it_verifies_token_as_valid()
70 | {
71 | $user = factory(User::class)->create();
72 |
73 | factory(TwoFactor::class)->create([
74 | 'user_id' => $user->id
75 | ]);
76 |
77 | $token = uniqid();
78 |
79 | $response = $this->mock(Response::class, function ($mock) {
80 | $mock->shouldReceive('getBody')->once()->andReturn(json_encode(['token' => 'is valid']));
81 | });
82 |
83 | $mock = $this->mock(Client::class, function ($mock) use ($response, $token, $user) {
84 | $mock->shouldReceive('request')
85 | ->with(
86 | 'GET',
87 | 'https://api.authy.com/protected/json/verify/'
88 | . $token . '/' . $user->twoFactor->identifier . '?force=true&api_key='
89 | )
90 | ->once()
91 | ->andReturn($response);
92 | });
93 |
94 | $authy = new AuthyTwoFactor($mock);
95 |
96 | $this->assertTrue($authy->verifyToken($user, $token));
97 | }
98 |
99 | /** @test */
100 | public function it_verifies_token_as_invalid()
101 | {
102 | $user = factory(User::class)->create();
103 |
104 | factory(TwoFactor::class)->create([
105 | 'user_id' => $user->id
106 | ]);
107 |
108 | $token = uniqid();
109 |
110 | $response = $this->mock(Response::class, function ($mock) {
111 | $mock->shouldReceive('getBody')->once()->andReturn(json_encode(['token' => 'is not valid']));
112 | });
113 |
114 | $mock = $this->mock(Client::class, function ($mock) use ($response, $token, $user) {
115 | $mock->shouldReceive('request')
116 | ->with(
117 | 'GET',
118 | 'https://api.authy.com/protected/json/verify/'
119 | . $token . '/' . $user->twoFactor->identifier . '?force=true&api_key='
120 | )
121 | ->once()
122 | ->andReturn($response);
123 | });
124 |
125 | $authy = new AuthyTwoFactor($mock);
126 |
127 | $this->assertFalse($authy->verifyToken($user, $token));
128 | }
129 |
130 | /** @test */
131 | public function it_verifies_token_as_invalid_if_smth_wrong()
132 | {
133 | $user = factory(User::class)->create();
134 |
135 | factory(TwoFactor::class)->create([
136 | 'user_id' => $user->id
137 | ]);
138 |
139 | $token = uniqid();
140 |
141 | $mock = $this->mock(Client::class, function ($mock) use ($token, $user) {
142 | $mock->shouldReceive('request')
143 | ->with(
144 | 'GET',
145 | 'https://api.authy.com/protected/json/verify/'
146 | . $token . '/' . $user->twoFactor->identifier . '?force=true&api_key='
147 | )
148 | ->once()
149 | ->andThrow(\Exception::class);
150 | });
151 |
152 | $authy = new AuthyTwoFactor($mock);
153 |
154 | $this->assertFalse($authy->verifyToken($user, $token));
155 | }
156 |
157 | /** @test */
158 | public function it_removes_user_from_two_factor_service()
159 | {
160 | $user = factory(User::class)->create();
161 |
162 | factory(TwoFactor::class)->create([
163 | 'user_id' => $user->id
164 | ]);
165 |
166 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
167 | $mock->shouldReceive('request')
168 | ->with(
169 | 'POST',
170 | 'https://api.authy.com/protected/json/users/delete/'
171 | . $user->twoFactor->identifier . '?api_key='
172 | )
173 | ->once()
174 | ->andReturn(null);
175 | });
176 |
177 | $authy = new AuthyTwoFactor($mock);
178 |
179 | $this->assertTrue($authy->remove($user));
180 | }
181 |
182 | /** @test */
183 | public function it_doesnt_remove_user_from_two_factor_service()
184 | {
185 | $user = factory(User::class)->create();
186 |
187 | factory(TwoFactor::class)->create([
188 | 'user_id' => $user->id
189 | ]);
190 |
191 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
192 | $mock->shouldReceive('request')
193 | ->with(
194 | 'POST',
195 | 'https://api.authy.com/protected/json/users/delete/'
196 | . $user->twoFactor->identifier . '?api_key='
197 | )
198 | ->once()
199 | ->andThrow(\Exception::class);
200 | });
201 |
202 | $authy = new AuthyTwoFactor($mock);
203 |
204 | $this->assertFalse($authy->remove($user));
205 | }
206 |
207 | /** @test */
208 | public function it_returns_link_to_qr_code()
209 | {
210 | $user = factory(User::class)->create();
211 |
212 | factory(TwoFactor::class)->create([
213 | 'user_id' => $user->id
214 | ]);
215 |
216 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
217 | $mock->shouldReceive('request')
218 | ->with(
219 | 'POST',
220 | 'https://api.authy.com/protected/json/users/'
221 | . $user->twoFactor->identifier . '/secret?api_key='
222 | )
223 | ->once()
224 | ->andReturn(new Response);
225 | });
226 |
227 | $authy = new AuthyTwoFactor($mock);
228 |
229 | $this->isNull($authy->qrCode($user));
230 | }
231 |
232 | /** @test */
233 | public function it_doesnt_return_link_to_qr_code()
234 | {
235 | $user = factory(User::class)->create();
236 |
237 | factory(TwoFactor::class)->create([
238 | 'user_id' => $user->id
239 | ]);
240 |
241 | $mock = $this->mock(Client::class, function ($mock) use ($user) {
242 | $mock->shouldReceive('request')
243 | ->with(
244 | 'POST',
245 | 'https://api.authy.com/protected/json/users/'
246 | . $user->twoFactor->identifier . '/secret?api_key='
247 | )
248 | ->once()
249 | ->andThrow(\Exception::class);
250 | });
251 |
252 | $authy = new AuthyTwoFactor($mock);
253 |
254 | $this->assertFalse($authy->qrCode($user));
255 | }
256 |
257 | /**
258 | * Get data needed for user registration on Authy
259 | *
260 | * @param $user
261 | * @return array
262 | */
263 | protected function getUserRegistrationPayload($user)
264 | {
265 | return [
266 | 'user' => [
267 | 'email' => $user->email,
268 | 'cellphone' => $user->twoFactor->phone,
269 | 'country_code' => $user->twoFactor->dial_code
270 | ]
271 | ];
272 | }
273 | }
--------------------------------------------------------------------------------
/views/auth/login.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
5 |
6 |
7 |
70 |
71 | @if(AwesAuth::isSocialEnabled())
72 |
81 | @endif
82 |
83 |
84 |
85 |
86 | @endsection
87 |
--------------------------------------------------------------------------------
/views/auth/passwords/email.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | @if (session('status'))
12 |
13 | {{ session('status') }}
14 |
15 | @endif
16 |
17 |
42 |
43 |
44 |
45 |
46 |
47 | @endsection
48 |
--------------------------------------------------------------------------------
/views/auth/passwords/reset.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
65 | @endsection
66 |
--------------------------------------------------------------------------------
/views/auth/register.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
77 | @endsection
78 |
--------------------------------------------------------------------------------
/views/auth/verify.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
42 | @endsection
43 |
--------------------------------------------------------------------------------
/views/layouts/app.blade.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | {{ config('app.name', 'Laravel') }}
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
73 |
74 |
75 | @auth
76 | @if (!auth()->user()->hasVerifiedEmail())
77 |
78 | Before proceeding, please check your email for a verification code and link.
79 | If you did not receive the email click here to
request another.
80 |
81 | @endif
82 | @endauth
83 |
84 |
85 | @yield('content')
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/views/twofactor/index.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | @if (auth()->user()->isTwoFactorEnabled())
12 |
13 |
Two factor auth is enabled
14 |
25 |
26 | @else
27 | @if (auth()->user()->isTwoFactorPending())
28 |
29 |
52 |
53 |
54 |
55 |
66 |
67 | @else
68 |
69 |
112 |
113 | @endif
114 |
115 | @endif
116 |
117 |
118 |
119 |
120 |
121 |
122 | @endsection
123 |
--------------------------------------------------------------------------------
/views/twofactor/verify.blade.php:
--------------------------------------------------------------------------------
1 | @extends('awesio-auth::layouts.app')
2 |
3 | @section('content')
4 |
42 | @endsection
43 |
--------------------------------------------------------------------------------
/xdebug.ini:
--------------------------------------------------------------------------------
1 | ; NOTE: The actual debug.so extention is NOT SET HERE but rather (/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini)
2 |
3 | xdebug.remote_connect_back=0
4 | xdebug.remote_port=9009
5 | xdebug.remote_host=172.19.1.15
6 | xdebug.idekey=PHPSTORM
7 |
8 | xdebug.remote_autostart=0
9 | xdebug.remote_enable=0
10 | xdebug.cli_color=0
11 | xdebug.profiler_enable=0
12 | xdebug.profiler_output_dir="~/xdebug/phpstorm/tmp/profiling"
13 |
14 | xdebug.remote_handler=dbgp
15 | xdebug.remote_mode=req
16 |
17 | xdebug.var_display_max_children=-1
18 | xdebug.var_display_max_data=-1
19 | xdebug.var_display_max_depth=-1
20 |
--------------------------------------------------------------------------------