+ @if (Route::has('login'))
+
+ @endif
+
+
+
+
+
diff --git a/routes/api.php b/routes/api.php
new file mode 100644
index 0000000..1459701
--- /dev/null
+++ b/routes/api.php
@@ -0,0 +1,43 @@
+version('v1', function (Router $api) {
+ $api->group(['prefix' => 'auth'], function(Router $api) {
+ $api->post('signup', 'App\\Api\\V1\\Controllers\\SignUpController@signUp');
+ $api->post('login', 'App\\Api\\V1\\Controllers\\LoginController@login');
+
+ $api->post('recovery', 'App\\Api\\V1\\Controllers\\ForgotPasswordController@sendResetEmail');
+ $api->post('reset', 'App\\Api\\V1\\Controllers\\ResetPasswordController@resetPassword');
+
+ $api->post('logout', 'App\\Api\\V1\\Controllers\\LogoutController@logout');
+ $api->post('refresh', 'App\\Api\\V1\\Controllers\\RefreshController@refresh');
+ $api->get('me', 'App\\Api\\V1\\Controllers\\UserController@me');
+ });
+
+ $api->group(['middleware' => 'jwt.auth'], function(Router $api) {
+ $api->get('protected', function() {
+ return response()->json([
+ 'message' => 'Access to protected resources granted! You are seeing this text as you provided the token correctly.'
+ ]);
+ });
+
+ $api->get('refresh', [
+ 'middleware' => 'jwt.refresh',
+ function() {
+ return response()->json([
+ 'message' => 'By accessing this endpoint, you can refresh your access token at each request. Check out this response headers!'
+ ]);
+ }
+ ]);
+ });
+
+ $api->get('hello', function() {
+ return response()->json([
+ 'message' => 'This is a simple example of item returned by your APIs. Everyone can see it.'
+ ]);
+ });
+});
diff --git a/routes/console.php b/routes/console.php
new file mode 100644
index 0000000..75dd0cd
--- /dev/null
+++ b/routes/console.php
@@ -0,0 +1,18 @@
+comment(Inspiring::quote());
+})->describe('Display an inspiring quote');
diff --git a/routes/web.php b/routes/web.php
new file mode 100644
index 0000000..9fee2ae
--- /dev/null
+++ b/routes/web.php
@@ -0,0 +1,21 @@
+ 'password.reset', function($token)
+{
+ // implement your reset password route here!
+}]);
+
+Route::get('/', function () {
+ return view('welcome');
+});
diff --git a/server.php b/server.php
new file mode 100644
index 0000000..5fb6379
--- /dev/null
+++ b/server.php
@@ -0,0 +1,21 @@
+
+ */
+
+$uri = urldecode(
+ parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
+);
+
+// This file allows us to emulate Apache's "mod_rewrite" functionality from the
+// built-in PHP web server. This provides a convenient way to test a Laravel
+// application without having installed a "real" web server software here.
+if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) {
+ return false;
+}
+
+require_once __DIR__.'/public/index.php';
diff --git a/storage/app/.gitignore b/storage/app/.gitignore
new file mode 100644
index 0000000..8f4803c
--- /dev/null
+++ b/storage/app/.gitignore
@@ -0,0 +1,3 @@
+*
+!public/
+!.gitignore
diff --git a/storage/app/public/.gitignore b/storage/app/public/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/app/public/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/framework/.gitignore b/storage/framework/.gitignore
new file mode 100644
index 0000000..b02b700
--- /dev/null
+++ b/storage/framework/.gitignore
@@ -0,0 +1,8 @@
+config.php
+routes.php
+schedule-*
+compiled.php
+services.json
+events.scanned.php
+routes.scanned.php
+down
diff --git a/storage/framework/cache/.gitignore b/storage/framework/cache/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/framework/cache/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/framework/sessions/.gitignore b/storage/framework/sessions/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/framework/sessions/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/framework/views/.gitignore b/storage/framework/views/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/framework/views/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/storage/logs/.gitignore b/storage/logs/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/storage/logs/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/tests/Functional/Api/V1/Controllers/ForgotPasswordControllerTest.php b/tests/Functional/Api/V1/Controllers/ForgotPasswordControllerTest.php
new file mode 100644
index 0000000..d528814
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/ForgotPasswordControllerTest.php
@@ -0,0 +1,52 @@
+ 'Test',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $user->save();
+ }
+
+ public function testForgotPasswordRecoverySuccessfully()
+ {
+ $this->post('api/auth/recovery', [
+ 'email' => 'test@email.com'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->isOk();
+ }
+
+ public function testForgotPasswordRecoveryReturnsUserNotFoundError()
+ {
+ $this->post('api/auth/recovery', [
+ 'email' => 'unknown@email.com'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(404);
+ }
+
+ public function testForgotPasswordRecoveryReturnsValidationErrors()
+ {
+ $this->post('api/auth/recovery', [
+ 'email' => 'i am not an email'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(422);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/LoginControllerTest.php b/tests/Functional/Api/V1/Controllers/LoginControllerTest.php
new file mode 100644
index 0000000..70b8300
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/LoginControllerTest.php
@@ -0,0 +1,59 @@
+ 'Test',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $user->save();
+ }
+
+ public function testLoginSuccessfully()
+ {
+ $this->post('api/auth/login', [
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->assertJsonStructure([
+ 'status',
+ 'token',
+ 'expires_in'
+ ])->isOk();
+ }
+
+ public function testLoginWithReturnsWrongCredentialsError()
+ {
+ $this->post('api/auth/login', [
+ 'email' => 'unknown@email.com',
+ 'password' => '123456'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(403);
+ }
+
+ public function testLoginWithReturnsValidationError()
+ {
+ $this->post('api/auth/login', [
+ 'email' => 'test@email.com'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(422);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/LogoutControllerTest.php b/tests/Functional/Api/V1/Controllers/LogoutControllerTest.php
new file mode 100644
index 0000000..40ed6ec
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/LogoutControllerTest.php
@@ -0,0 +1,47 @@
+ 'Test',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $user->save();
+ }
+
+ public function testLogout()
+ {
+ $response = $this->post('api/auth/login', [
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $response->assertStatus(200);
+
+ $responseJSON = json_decode($response->getContent(), true);
+ $token = $responseJSON['token'];
+
+ $this->post('api/auth/logout', [], [
+ 'Authorization' => 'Bearer ' . $token
+ ])->assertStatus(200);
+
+ $this->post('api/auth/logout', [], [
+ 'Authorization' => 'Bearer ' . $token
+ ])->assertStatus(500);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/RefreshControllerTest.php b/tests/Functional/Api/V1/Controllers/RefreshControllerTest.php
new file mode 100644
index 0000000..9cc1145
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/RefreshControllerTest.php
@@ -0,0 +1,56 @@
+ 'Test',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $user->save();
+ }
+
+ public function testRefresh()
+ {
+ $response = $this->post('api/auth/login', [
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $response->assertStatus(200);
+
+ $responseJSON = json_decode($response->getContent(), true);
+ $token = $responseJSON['token'];
+
+ $this->post('api/auth/refresh', [], [
+ 'Authorization' => 'Bearer ' . $token
+ ])->assertJsonStructure([
+ 'status',
+ 'token',
+ 'expires_in'
+ ])->isOk();
+ }
+
+ public function testRefreshWithError()
+ {
+ $response = $this->post('api/auth/refresh', [], [
+ 'Authorization' => 'Bearer Wrong'
+ ]);
+
+ $response->assertStatus(500);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php b/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php
new file mode 100644
index 0000000..db6a341
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/ResetPasswordControllerTest.php
@@ -0,0 +1,85 @@
+ 'Test User',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+ $user->save();
+
+ DB::table('password_resets')->insert([
+ 'email' => 'test@email.com',
+ 'token' => bcrypt('my_super_secret_code'),
+ 'created_at' => Carbon::now()
+ ]);
+ }
+
+ public function testResetSuccessfully()
+ {
+ $this->post('api/auth/reset', [
+ 'email' => 'test@email.com',
+ 'token' => 'my_super_secret_code',
+ 'password' => 'mynewpass',
+ 'password_confirmation' => 'mynewpass'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->isOk();
+ }
+
+ public function testResetSuccessfullyWithTokenRelease()
+ {
+ Config::set('boilerplate.reset_password.release_token', true);
+
+ $this->post('api/auth/reset', [
+ 'email' => 'test@email.com',
+ 'token' => 'my_super_secret_code',
+ 'password' => 'mynewpass',
+ 'password_confirmation' => 'mynewpass'
+ ])->assertJsonStructure([
+ 'status',
+ 'token'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->isOk();
+ }
+
+ public function testResetReturnsProcessError()
+ {
+ $this->post('api/auth/reset', [
+ 'email' => 'unknown@email.com',
+ 'token' => 'this_code_is_invalid',
+ 'password' => 'mynewpass',
+ 'password_confirmation' => 'mynewpass'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(500);
+ }
+
+ public function testResetReturnsValidationError()
+ {
+ $this->post('api/auth/reset', [
+ 'email' => 'test@email.com',
+ 'token' => 'my_super_secret_code',
+ 'password' => 'mynewpass'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(422);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/SignUpControllerTest.php b/tests/Functional/Api/V1/Controllers/SignUpControllerTest.php
new file mode 100644
index 0000000..08d5539
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/SignUpControllerTest.php
@@ -0,0 +1,48 @@
+post('api/auth/signup', [
+ 'name' => 'Test User',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->assertStatus(201);
+ }
+
+ public function testSignUpSuccessfullyWithTokenRelease()
+ {
+ Config::set('boilerplate.sign_up.release_token', true);
+
+ $this->post('api/auth/signup', [
+ 'name' => 'Test User',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ])->assertJsonStructure([
+ 'status', 'token'
+ ])->assertJson([
+ 'status' => 'ok'
+ ])->assertStatus(201);
+ }
+
+ public function testSignUpReturnsValidationError()
+ {
+ $this->post('api/auth/signup', [
+ 'name' => 'Test User',
+ 'email' => 'test@email.com'
+ ])->assertJsonStructure([
+ 'error'
+ ])->assertStatus(422);
+ }
+}
diff --git a/tests/Functional/Api/V1/Controllers/UserControllerTest.php b/tests/Functional/Api/V1/Controllers/UserControllerTest.php
new file mode 100644
index 0000000..3f31e1e
--- /dev/null
+++ b/tests/Functional/Api/V1/Controllers/UserControllerTest.php
@@ -0,0 +1,46 @@
+ 'Test',
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $user->save();
+ }
+
+ public function testMe()
+ {
+ $response = $this->post('api/auth/login', [
+ 'email' => 'test@email.com',
+ 'password' => '123456'
+ ]);
+
+ $response->assertStatus(200);
+
+ $responseJSON = json_decode($response->getContent(), true);
+ $token = $responseJSON['token'];
+
+ $this->get('api/auth/me', [], [
+ 'Authorization' => 'Bearer ' . $token
+ ])->assertJson([
+ 'name' => 'Test',
+ 'email' => 'test@email.com'
+ ])->isOk();
+ }
+}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 0000000..7e56d02
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,30 @@
+make(Kernel::class)->bootstrap();
+
+ return $app;
+ }
+}