diff --git a/.env.testing b/.env.testing index 7497f810..8d3c0c19 100644 --- a/.env.testing +++ b/.env.testing @@ -35,3 +35,5 @@ REDIS_PORT = 6379 PLUGINS_DIR = plugins PLUGINS_URL = null + +JWT_SECRET = 1tdM3gXarxYI4KlAHMBo238iC2tEb4I3EtBlZTQQXvInXIt7V2ix7hJ1KTvxCKZW diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index e06be54d..9bd7eaa5 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -251,4 +251,20 @@ class AuthController extends Controller return view('auth.verify'); } + + public function apiLogin(Request $request) + { + $token = Auth::guard('api')->attempt([ + 'email' => $request->email, + 'password' => $request->password + ]); + + return json(compact('token')); + } + + public function apiLogout() + { + Auth::guard('api')->logout(); + return response('', 204); + } } diff --git a/app/Http/Controllers/SetupController.php b/app/Http/Controllers/SetupController.php index 9f27c43a..34bb843a 100644 --- a/app/Http/Controllers/SetupController.php +++ b/app/Http/Controllers/SetupController.php @@ -141,6 +141,7 @@ class SetupController extends Controller Artisan::call('key:random'); Artisan::call('salt:random'); } + Artisan::call('jwt:secret', ['--no-interaction' => true]); // Create tables Artisan::call('migrate', ['--force' => true]); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 971e0daa..158ee64a 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -34,6 +34,11 @@ class Kernel extends HttpKernel \App\Http\Middleware\ForbiddenIE::class, ], + 'api' => [ + 'throttle:60,1', + 'bindings', + ], + 'static' => [], ]; @@ -47,6 +52,8 @@ class Kernel extends HttpKernel protected $routeMiddleware = [ 'csrf' => \App\Http\Middleware\VerifyCsrfToken::class, 'auth' => \App\Http\Middleware\CheckAuthenticated::class, + 'auth.jwt' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class, + 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'verified' => \App\Http\Middleware\CheckUserVerified::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'admin' => \App\Http\Middleware\CheckAdministrator::class, @@ -54,5 +61,6 @@ class Kernel extends HttpKernel 'player' => \App\Http\Middleware\CheckPlayerExist::class, 'setup' => \App\Http\Middleware\CheckInstallation::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, + 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, ]; } diff --git a/app/Models/User.php b/app/Models/User.php index 378400f1..67cda435 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,9 +6,10 @@ use DB; use Carbon\Carbon; use Illuminate\Support\Arr; use App\Events\EncryptUserPassword; +use Tymon\JWTAuth\Contracts\JWTSubject; use Illuminate\Foundation\Auth\User as Authenticatable; -class User extends Authenticatable +class User extends Authenticatable implements JWTSubject { /** * Permissions. @@ -214,21 +215,11 @@ class User extends Authenticatable return Carbon::now()->diffInSeconds($lastSignTime->addHours(option('sign_gap_time')), false); } - /** - * Check if signing in is available now. - * - * @return bool - */ public function canSign() { return $this->getSignRemainingTime() <= 0; } - /** - * Delete the user. - * - * @return bool - */ public function delete() { Player::where('uid', $this->uid)->delete(); @@ -236,11 +227,6 @@ class User extends Authenticatable return parent::delete(); } - /** - * Get the players which are owned by the user. - * - * @return Illuminate\Database\Eloquent\Collection - */ public function players() { return $this->hasMany('App\Models\Player', 'uid'); @@ -250,4 +236,14 @@ class User extends Authenticatable { return $this->uid; } + + public function getJWTIdentifier() + { + return $this->getKey(); + } + + public function getJWTCustomClaims() + { + return []; + } } diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 1d9fa25e..56fe58f3 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use Route; use Illuminate\Routing\Router; use App\Events\ConfigureRoutes; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; @@ -41,6 +42,8 @@ class RouteServiceProvider extends ServiceProvider $this->mapWebRoutes($router); + $this->mapApiRoutes(); + event(new ConfigureRoutes($router)); } @@ -97,4 +100,19 @@ class RouteServiceProvider extends ServiceProvider require base_path('routes/static.php'); }); } + + /** + * Define the "api" routes for the application. + * + * These routes are typically stateless. + * + * @return void + */ + protected function mapApiRoutes() + { + Route::prefix('api') + ->middleware('api') + ->namespace($this->namespace) + ->group(base_path('routes/api.php')); + } } diff --git a/composer.json b/composer.json index 766acc93..c0783861 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "composer/semver": "^1.4", "mews/captcha": "^2.2", "guzzlehttp/guzzle": "^6.3", - "doctrine/dbal": "^2.9" + "doctrine/dbal": "^2.9", + "tymon/jwt-auth": "dev-develop" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/composer.lock b/composer.lock index 7747deba..f85852f7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8acc80effb620a298c57923229891388", + "content-hash": "ff9f059d35e12b8b7c52f529300ce9f2", "packages": [ { "name": "composer/semver", @@ -1018,6 +1018,64 @@ ], "time": "2019-03-26T17:19:10+00:00" }, + { + "name": "lcobucci/jwt", + "version": "3.2.5", + "source": { + "type": "git", + "url": "https://github.com/lcobucci/jwt.git", + "reference": "82be04b4753f8b7693b62852b7eab30f97524f9b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/lcobucci/jwt/zipball/82be04b4753f8b7693b62852b7eab30f97524f9b", + "reference": "82be04b4753f8b7693b62852b7eab30f97524f9b", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">=5.5" + }, + "require-dev": { + "mdanter/ecc": "~0.3.1", + "mikey179/vfsstream": "~1.5", + "phpmd/phpmd": "~2.2", + "phpunit/php-invoker": "~1.1", + "phpunit/phpunit": "~4.5", + "squizlabs/php_codesniffer": "~2.3" + }, + "suggest": { + "mdanter/ecc": "Required to use Elliptic Curves based algorithms." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Lcobucci\\JWT\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Luís Otávio Cobucci Oblonczyk", + "email": "lcobucci@gmail.com", + "role": "Developer" + } + ], + "description": "A simple library to work with JSON Web Token and JSON Web Signature", + "keywords": [ + "JWS", + "jwt" + ], + "time": "2018-11-11T12:22:26+00:00" + }, { "name": "league/flysystem", "version": "1.0.51", @@ -1247,6 +1305,69 @@ ], "time": "2018-11-05T09:00:11+00:00" }, + { + "name": "namshi/jose", + "version": "7.2.3", + "source": { + "type": "git", + "url": "https://github.com/namshi/jose.git", + "reference": "89a24d7eb3040e285dd5925fcad992378b82bcff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/namshi/jose/zipball/89a24d7eb3040e285dd5925fcad992378b82bcff", + "reference": "89a24d7eb3040e285dd5925fcad992378b82bcff", + "shasum": "" + }, + "require": { + "ext-date": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-spl": "*", + "php": ">=5.5", + "symfony/polyfill-php56": "^1.0" + }, + "require-dev": { + "phpseclib/phpseclib": "^2.0", + "phpunit/phpunit": "^4.5|^5.0", + "satooshi/php-coveralls": "^1.0" + }, + "suggest": { + "ext-openssl": "Allows to use OpenSSL as crypto engine.", + "phpseclib/phpseclib": "Allows to use Phpseclib as crypto engine, use version ^2.0." + }, + "type": "library", + "autoload": { + "psr-4": { + "Namshi\\JOSE\\": "src/Namshi/JOSE/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Nadalin", + "email": "alessandro.nadalin@gmail.com" + }, + { + "name": "Alessandro Cinelli (cirpo)", + "email": "alessandro.cinelli@gmail.com" + } + ], + "description": "JSON Object Signing and Encryption library for PHP.", + "keywords": [ + "JSON Web Signature", + "JSON Web Token", + "JWS", + "json", + "jwt", + "token" + ], + "time": "2016-12-05T07:27:31+00:00" + }, { "name": "nesbot/carbon", "version": "2.14.0", @@ -2561,6 +2682,62 @@ ], "time": "2018-09-21T13:07:52+00:00" }, + { + "name": "symfony/polyfill-php56", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "f4dddbc5c3471e1b700a147a20ae17cdb72dbe42" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/f4dddbc5c3471e1b700a147a20ae17cdb72dbe42", + "reference": "f4dddbc5c3471e1b700a147a20ae17cdb72dbe42", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2019-02-06T07:57:58+00:00" + }, { "name": "symfony/polyfill-php72", "version": "v1.10.0", @@ -2616,6 +2793,58 @@ ], "time": "2018-09-21T13:07:52+00:00" }, + { + "name": "symfony/polyfill-util", + "version": "v1.11.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "b46c6cae28a3106735323f00a0c38eccf2328897" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/b46c6cae28a3106735323f00a0c38eccf2328897", + "reference": "b46c6cae28a3106735323f00a0c38eccf2328897", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2019-02-08T14:16:39+00:00" + }, { "name": "symfony/process", "version": "v4.2.3", @@ -2997,6 +3226,81 @@ "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles", "time": "2017-11-27T11:13:29+00:00" }, + { + "name": "tymon/jwt-auth", + "version": "dev-develop", + "source": { + "type": "git", + "url": "https://github.com/tymondesigns/jwt-auth.git", + "reference": "63698d304554e5d0bc3eb481cc260a9fc900e151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tymondesigns/jwt-auth/zipball/63698d304554e5d0bc3eb481cc260a9fc900e151", + "reference": "63698d304554e5d0bc3eb481cc260a9fc900e151", + "shasum": "" + }, + "require": { + "illuminate/auth": "^5.1", + "illuminate/contracts": "^5.1", + "illuminate/http": "^5.1", + "illuminate/support": "^5.1", + "lcobucci/jwt": "^3.2", + "namshi/jose": "^7.0", + "nesbot/carbon": "^1.0 || ^2.0", + "php": "^5.5.9 || ^7.0" + }, + "require-dev": { + "cartalyst/sentinel": "2.0.*", + "illuminate/console": "^5.1", + "illuminate/database": "^5.1", + "illuminate/routing": "^5.1", + "mockery/mockery": ">=0.9.9", + "phpunit/phpunit": "~4.8 || ~6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-develop": "1.0-dev" + }, + "laravel": { + "aliases": { + "JWTAuth": "Tymon\\JWTAuth\\Facades\\JWTAuth", + "JWTFactory": "Tymon\\JWTAuth\\Facades\\JWTFactory" + }, + "providers": [ + "Tymon\\JWTAuth\\Providers\\LaravelServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Tymon\\JWTAuth\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sean Tymon", + "email": "tymon148@gmail.com", + "homepage": "https://tymon.xyz", + "role": "Developer" + } + ], + "description": "JSON Web Token Authentication for Laravel and Lumen", + "homepage": "https://github.com/tymondesigns/jwt-auth", + "keywords": [ + "Authentication", + "JSON Web Token", + "auth", + "jwt", + "laravel" + ], + "time": "2019-03-14T20:29:20+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v3.3.2", @@ -5802,7 +6106,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "tymon/jwt-auth": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/config/auth.php b/config/auth.php index 087bbb3e..4f251b41 100644 --- a/config/auth.php +++ b/config/auth.php @@ -42,7 +42,7 @@ return [ ], 'api' => [ - 'driver' => 'token', + 'driver' => 'jwt', 'provider' => 'users', ], ], diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 00000000..8b7843b6 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,304 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Secret + |-------------------------------------------------------------------------- + | + | Don't forget to set this in your .env file, as it will be used to sign + | your tokens. A helper command is provided for this: + | `php artisan jwt:secret` + | + | Note: This will be used for Symmetric algorithms only (HMAC), + | since RSA and ECDSA use a private/public key combo (See below). + | + */ + + 'secret' => env('JWT_SECRET'), + + /* + |-------------------------------------------------------------------------- + | JWT Authentication Keys + |-------------------------------------------------------------------------- + | + | The algorithm you are using, will determine whether your tokens are + | signed with a random string (defined in `JWT_SECRET`) or using the + | following public & private keys. + | + | Symmetric Algorithms: + | HS256, HS384 & HS512 will use `JWT_SECRET`. + | + | Asymmetric Algorithms: + | RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below. + | + */ + + 'keys' => [ + + /* + |-------------------------------------------------------------------------- + | Public Key + |-------------------------------------------------------------------------- + | + | A path or resource to your public key. + | + | E.g. 'file://path/to/public/key' + | + */ + + 'public' => env('JWT_PUBLIC_KEY'), + + /* + |-------------------------------------------------------------------------- + | Private Key + |-------------------------------------------------------------------------- + | + | A path or resource to your private key. + | + | E.g. 'file://path/to/private/key' + | + */ + + 'private' => env('JWT_PRIVATE_KEY'), + + /* + |-------------------------------------------------------------------------- + | Passphrase + |-------------------------------------------------------------------------- + | + | The passphrase for your private key. Can be null if none set. + | + */ + + 'passphrase' => env('JWT_PASSPHRASE'), + + ], + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour. + | + | You can also set this to null, to yield a never expiring token. + | Some people may want this behaviour for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | Notice: If you set this to null you should remove 'exp' element from 'required_claims' list. + | + */ + + 'ttl' => env('JWT_TTL', 60), + + /* + |-------------------------------------------------------------------------- + | Refresh time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token can be refreshed + | within. I.E. The user can refresh their token within a 2 week window of + | the original token being created until they must re-authenticate. + | Defaults to 2 weeks. + | + | You can also set this to null, to yield an infinite refresh time. + | Some may want this instead of never expiring tokens for e.g. a mobile app. + | This is not particularly recommended, so make sure you have appropriate + | systems in place to revoke the token if necessary. + | + */ + + 'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), + + /* + |-------------------------------------------------------------------------- + | JWT hashing algorithm + |-------------------------------------------------------------------------- + | + | Specify the hashing algorithm that will be used to sign the token. + | + | See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL + | for possible values. + | + */ + + 'algo' => env('JWT_ALGO', 'HS256'), + + /* + |-------------------------------------------------------------------------- + | Required Claims + |-------------------------------------------------------------------------- + | + | Specify the required claims that must exist in any token. + | A TokenInvalidException will be thrown if any of these claims are not + | present in the payload. + | + */ + + 'required_claims' => [ + 'iss', + 'iat', + 'exp', + 'nbf', + 'sub', + 'jti', + ], + + /* + |-------------------------------------------------------------------------- + | Persistent Claims + |-------------------------------------------------------------------------- + | + | Specify the claim keys to be persisted when refreshing a token. + | `sub` and `iat` will automatically be persisted, in + | addition to the these claims. + | + | Note: If a claim does not exist then it will be ignored. + | + */ + + 'persistent_claims' => [ + // 'foo', + // 'bar', + ], + + /* + |-------------------------------------------------------------------------- + | Lock Subject + |-------------------------------------------------------------------------- + | + | This will determine whether a `prv` claim is automatically added to + | the token. The purpose of this is to ensure that if you have multiple + | authentication models e.g. `App\User` & `App\OtherPerson`, then we + | should prevent one authentication request from impersonating another, + | if 2 tokens happen to have the same id across the 2 different models. + | + | Under specific circumstances, you may want to disable this behaviour + | e.g. if you only have one authentication model, then you would save + | a little on token size. + | + */ + + 'lock_subject' => true, + + /* + |-------------------------------------------------------------------------- + | Leeway + |-------------------------------------------------------------------------- + | + | This property gives the jwt timestamp claims some "leeway". + | Meaning that if you have any unavoidable slight clock skew on + | any of your servers then this will afford you some level of cushioning. + | + | This applies to the claims `iat`, `nbf` and `exp`. + | + | Specify in seconds - only if you know you need it. + | + */ + + 'leeway' => env('JWT_LEEWAY', 0), + + /* + |-------------------------------------------------------------------------- + | Blacklist Enabled + |-------------------------------------------------------------------------- + | + | In order to invalidate tokens, you must have the blacklist enabled. + | If you do not want or need this functionality, then set this to false. + | + */ + + 'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true), + + /* + | ------------------------------------------------------------------------- + | Blacklist Grace Period + | ------------------------------------------------------------------------- + | + | When multiple concurrent requests are made with the same JWT, + | it is possible that some of them fail, due to token regeneration + | on every request. + | + | Set grace period in seconds to prevent parallel request failure. + | + */ + + 'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0), + + /* + |-------------------------------------------------------------------------- + | Cookies encryption + |-------------------------------------------------------------------------- + | + | By default Laravel encrypt cookies for security reason. + | If you decide to not decrypt cookies, you will have to configure Laravel + | to not encrypt your cookie token by adding its name into the $except + | array available in the middleware "EncryptCookies" provided by Laravel. + | see https://laravel.com/docs/master/responses#cookies-and-encryption + | for details. + | + | Set it to true if you want to decrypt cookies. + | + */ + + 'decrypt_cookies' => false, + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class, + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class, + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist. + | + */ + + 'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class, + + ], + +]; diff --git a/database/update_scripts/update-4.0.4-to-4.1.0.php b/database/update_scripts/update-4.0.4-to-4.1.0.php new file mode 100644 index 00000000..1f91b563 --- /dev/null +++ b/database/update_scripts/update-4.0.4-to-4.1.0.php @@ -0,0 +1,5 @@ + true]); diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 00000000..81d307f7 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,11 @@ +group(function ($route) { + $route->post('login', 'AuthController@apiLogin'); + $route->post('logout', 'AuthController@apiLogout')->middleware('auth.jwt'); +}); + +Route::prefix('user')->middleware('auth.jwt')->group(function ($route) { + $route->post('sign', 'UserController@sign'); +}); + diff --git a/tests/AuthControllerTest.php b/tests/AuthControllerTest.php index 71428ba1..2e6ac050 100644 --- a/tests/AuthControllerTest.php +++ b/tests/AuthControllerTest.php @@ -525,4 +525,31 @@ class AuthControllerTest extends TestCase $this->get($url)->assertViewIs('auth.verify'); $this->assertEquals(1, User::find($user->uid)->verified); } + + public function testApiLogin() + { + $user = factory(User::class)->create(); + $user->changePassword('12345678'); + + $this->postJson('/api/auth/login')->assertJson(['token' => false]); + $token = $this->postJson('/api/auth/login', [ + 'email' => $user->email, + 'password' => '12345678' + ])->decodeResponseJson('token'); + $this->assertTrue(is_string($token)); + } + + public function testApiLogout() + { + $user = factory(User::class)->create(); + $user->changePassword('12345678'); + $token = $this->postJson('/api/auth/login', [ + 'email' => $user->email, + 'password' => '12345678' + ])->decodeResponseJson('token'); + + $this->post('/api/auth/logout', [], [ + 'Authorization' => "Bearer $token" + ])->assertStatus(204); + } } diff --git a/tests/SetupControllerTest.php b/tests/SetupControllerTest.php index 2b801a02..8c15e913 100644 --- a/tests/SetupControllerTest.php +++ b/tests/SetupControllerTest.php @@ -163,6 +163,10 @@ class SetupControllerTest extends TestCase ->with('salt:random') ->once() ->andReturn(true); + Artisan::shouldReceive('call') + ->with('jwt:secret', ['--no-interaction' => true]) + ->once() + ->andReturn(true); Artisan::shouldReceive('call') ->with('migrate', ['--force' => true]) ->once()