diff --git a/.gitignore b/.gitignore index b6a4b86..6fe5f35 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ Homestead.yaml npm-debug.log yarn-error.log .env +package-lock.json +composer.lock +*~ diff --git a/app/Http/Controllers/Auth/ForgotPasswordController.php b/app/Http/Controllers/Auth/ForgotPasswordController.php deleted file mode 100644 index 6a247fe..0000000 --- a/app/Http/Controllers/Auth/ForgotPasswordController.php +++ /dev/null @@ -1,32 +0,0 @@ -middleware('guest'); - } -} diff --git a/app/Http/Controllers/Auth/LoginController.php b/app/Http/Controllers/Auth/LoginController.php deleted file mode 100644 index b2ea669..0000000 --- a/app/Http/Controllers/Auth/LoginController.php +++ /dev/null @@ -1,39 +0,0 @@ -middleware('guest')->except('logout'); - } -} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php deleted file mode 100644 index f77265a..0000000 --- a/app/Http/Controllers/Auth/RegisterController.php +++ /dev/null @@ -1,71 +0,0 @@ -middleware('guest'); - } - - /** - * Get a validator for an incoming registration request. - * - * @param array $data - * @return \Illuminate\Contracts\Validation\Validator - */ - protected function validator(array $data) - { - return Validator::make($data, [ - 'name' => 'required|string|max:255', - 'email' => 'required|string|email|max:255|unique:users', - 'password' => 'required|string|min:6|confirmed', - ]); - } - - /** - * Create a new user instance after a valid registration. - * - * @param array $data - * @return \App\User - */ - protected function create(array $data) - { - return User::create([ - 'name' => $data['name'], - 'email' => $data['email'], - 'password' => bcrypt($data['password']), - ]); - } -} diff --git a/app/Http/Controllers/Auth/ResetPasswordController.php b/app/Http/Controllers/Auth/ResetPasswordController.php deleted file mode 100644 index cf726ee..0000000 --- a/app/Http/Controllers/Auth/ResetPasswordController.php +++ /dev/null @@ -1,39 +0,0 @@ -middleware('guest'); - } -} diff --git a/app/Http/Controllers/IndexController.php b/app/Http/Controllers/IndexController.php index dbd3d20..2661fc2 100644 --- a/app/Http/Controllers/IndexController.php +++ b/app/Http/Controllers/IndexController.php @@ -9,8 +9,9 @@ class IndexController extends Controller { public function index(Request $request) { + $title = 'hctf'; return APIReturn::success([ - 'hello' => 'hctf' + 'hello' => $title ]); } } diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php new file mode 100644 index 0000000..3d93d1e --- /dev/null +++ b/app/Http/Controllers/TeamController.php @@ -0,0 +1,58 @@ +team = $team; + } + + public function login(Request $request) + { + $credentials = $request->only('email', 'password'); + $access_token = null; + + try { + if (!$access_token = JWTAuth::attempt($credentials)) { + return APIReturn::error(401, ['invalid_email_or_password'], 401); + } + } catch (JWTAuthException $err) { + return APIReturn::error(500, ['failed_to_create_token'], 500); + } + return response()->json(compact('access_token')); + } + + public function register(Request $request) { + $input = $request->only('teamName', 'email', 'password'); + try { + $team = $this->team->create([ + 'teamName' => $input['teamName'], + 'email' => $input['email'], + 'password' => bcrypt($input['password']), + 'signUpTime' => Carbon::now('Asia/Shanghai'), + 'lastLoginTime' => Carbon::now('Asia/Shanghai'), + ]); + } catch (Exception $err) { + return APIReturn::error(500, ['msg' => 'Team/Email already exists.'], 500); + } + + return APIReturn::success([ + 'msg' => 'Welcome to HCTF 2017!', + ]); + } + + public function getAuthInfo(Request $request) { + $team = JWTAuth::parseToken()->authenticate(); + return APIReturn::success(['team' => $team]); + } +} diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 29a4ef3..20d4466 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -13,7 +13,7 @@ public function __construct(UserRepositoryInterface $userRepository) $this->userRepository = $userRepository; } public function index(Request $request){ - return \APIReturn::success([ + return APIReturn::success([ "param" => $request->get('param') ]); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 66d34c3..6ac9f9c 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -3,6 +3,7 @@ namespace App\Http; use Illuminate\Foundation\Http\Kernel as HttpKernel; +use Tymon\JWTAuth\Middleware\GetUserFromToken; class Kernel extends HttpKernel { @@ -32,7 +33,7 @@ class Kernel extends HttpKernel \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\VerifyCsrfToken::class, + //\App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], @@ -56,5 +57,9 @@ class Kernel extends HttpKernel 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, + 'jwt.auth' => GetUserFromToken::class, + 'jwt.refresh' => RefreshToken::class, + 'jwt.auth.mod' => \App\Http\Middleware\VerifyCsrfToken::class, + ]; } diff --git a/app/Http/Middleware/VerifyJWTToken.php b/app/Http/Middleware/VerifyJWTToken.php new file mode 100644 index 0000000..4de7270 --- /dev/null +++ b/app/Http/Middleware/VerifyJWTToken.php @@ -0,0 +1,43 @@ +auth->setRequest($request)->getToken()) { + return $this->respond('tymon.jwt.absent', 'token_not_provided', 400); + } + + try { + $user = $this->auth->authenticate($token); + } catch (TokenExpiredException $err) { + return $this->respond('Token expired', 'token_expired', $err->getStatusCode(), [$err]); + } catch (TokenInvalidException $err) { + return $this->respond('Token invalid', 'token_invalid', $err->getStatusCode(), [$err]); + } + + if (! $user) { + return $this->respond('tymon.jwt.user_not_found', 'user_not_found', 404); + } + + $this->events->fire('tymon.jwt.valid', $user); + + return $next($request); + } +} diff --git a/app/Team.php b/app/Team.php new file mode 100644 index 0000000..a10ca3e --- /dev/null +++ b/app/Team.php @@ -0,0 +1,41 @@ + | auto_increment | + | teamName | varchar(255) | NO | | | | + | email | varchar(255) | YES | UNI | | | + | password | varchar(255) | NO | | | | + | signUpTime | datetime | NO | | | | + | lastLoginTime | datetime | NO | | | | + | score | decimal(8,2) | NO | | 0.00 | | + | banned | tinyint(1) | NO | | 0 | | + | remember_token | varchar(100) | YES | | | | + * + */ + protected $fillable = [ + 'teamName', 'email', 'password', 'signUpTime', 'lastLoginTime' + ]; + + /** + * The attributes that should be hidden for arrays. + * + * @var array + */ + protected $hidden = [ + 'password', 'remember_token', + ]; + + public $timestamps = false; +} \ No newline at end of file diff --git a/app/User.php b/app/User.php index bfd96a6..84ecc1e 100644 --- a/app/User.php +++ b/app/User.php @@ -27,3 +27,4 @@ class User extends Authenticatable 'password', 'remember_token', ]; } + diff --git a/composer.json b/composer.json index 7147047..51a07cd 100644 --- a/composer.json +++ b/composer.json @@ -6,9 +6,12 @@ "type": "project", "require": { "php": ">=5.6.4", + "barryvdh/laravel-cors": "^0.9.2", "barryvdh/laravel-ide-helper": "^2.4", "laravel/framework": "5.4.*", - "laravel/tinker": "~1.0" + "laravel/passport": "^3.0", + "laravel/tinker": "~1.0", + "tymon/jwt-auth": "^0.5.12" }, "require-dev": { "fzaninotto/faker": "~1.4", diff --git a/config/app.php b/config/app.php index 401102b..ef94eb5 100644 --- a/config/app.php +++ b/config/app.php @@ -186,7 +186,8 @@ * Custom Providers */ Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class, // IDE Helper - App\Providers\APIReturnServiceProvider::class + App\Providers\APIReturnServiceProvider::class, + Tymon\JWTAuth\Providers\JWTAuthServiceProvider::class, ], /* @@ -239,7 +240,10 @@ /** * Custom Facades */ - 'APIReturn' => \App\Facades\APIReturnFacade::class + 'APIReturn' => \App\Facades\APIReturnFacade::class, + 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, + 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class, + ], ]; diff --git a/config/auth.php b/config/auth.php index 7817501..3bfcabf 100644 --- a/config/auth.php +++ b/config/auth.php @@ -67,7 +67,7 @@ 'providers' => [ 'users' => [ 'driver' => 'eloquent', - 'model' => App\User::class, + 'model' => App\Team::class, ], // 'users' => [ diff --git a/config/jwt.php b/config/jwt.php new file mode 100644 index 0000000..d050aa2 --- /dev/null +++ b/config/jwt.php @@ -0,0 +1,173 @@ + + * + * 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, as it will be used to sign your tokens. + | A helper command is provided for this: `php artisan jwt:generate` + | + */ + + 'secret' => env('JWT_SECRET', '1Ma44OhpL6TD3RdUcNf4OI5vEvZnqzMp'), + + /* + |-------------------------------------------------------------------------- + | JWT time to live + |-------------------------------------------------------------------------- + | + | Specify the length of time (in minutes) that the token will be valid for. + | Defaults to 1 hour + | + */ + + '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 + | + */ + + '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/2.2.0/src/Namshi/JOSE/Signer + | for possible values + | + */ + + 'algo' => 'HS256', + + /* + |-------------------------------------------------------------------------- + | User Model namespace + |-------------------------------------------------------------------------- + | + | Specify the full namespace to your User model. + | e.g. 'Acme\Entities\User' + | + */ + + 'user' => 'App\User', + + /* + |-------------------------------------------------------------------------- + | User identifier + |-------------------------------------------------------------------------- + | + | Specify a unique property of the user that will be added as the 'sub' + | claim of the token payload. + | + */ + + 'identifier' => 'id', + + /* + |-------------------------------------------------------------------------- + | 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'], + + /* + |-------------------------------------------------------------------------- + | 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), + + /* + |-------------------------------------------------------------------------- + | Providers + |-------------------------------------------------------------------------- + | + | Specify the various providers used throughout the package. + | + */ + + 'providers' => [ + + /* + |-------------------------------------------------------------------------- + | User Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to find the user based + | on the subject claim + | + */ + + 'user' => 'Tymon\JWTAuth\Providers\User\EloquentUserAdapter', + + /* + |-------------------------------------------------------------------------- + | JWT Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to create and decode the tokens. + | + */ + + 'jwt' => 'Tymon\JWTAuth\Providers\JWT\NamshiAdapter', + + /* + |-------------------------------------------------------------------------- + | Authentication Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to authenticate users. + | + */ + + 'auth' => 'Tymon\JWTAuth\Providers\Auth\IlluminateAuthAdapter', + + /* + |-------------------------------------------------------------------------- + | Storage Provider + |-------------------------------------------------------------------------- + | + | Specify the provider that is used to store tokens in the blacklist + | + */ + + 'storage' => 'Tymon\JWTAuth\Providers\Storage\IlluminateCacheAdapter', + + ], + +]; diff --git a/database/migrations/2017_08_21_120744_create_team_table.php b/database/migrations/2017_08_21_120744_create_team_table.php index 8841ffa..c5c75e7 100644 --- a/database/migrations/2017_08_21_120744_create_team_table.php +++ b/database/migrations/2017_08_21_120744_create_team_table.php @@ -15,7 +15,7 @@ public function up() { Schema::create('teams', function (Blueprint $table) { $table->increments('id'); - $table->string('teamName'); // Display Name + $table->string('teamName')->unique(); // Display Name $table->string('email')->unique(); // Login Name $table->string('password'); $table->dateTimeTz('signUpTime'); diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php deleted file mode 100644 index e119db6..0000000 --- a/database/seeds/DatabaseSeeder.php +++ /dev/null @@ -1,16 +0,0 @@ -call(UsersTableSeeder::class); - } -} diff --git a/database/seeds/TeamTableSeeder.php b/database/seeds/TeamTableSeeder.php new file mode 100644 index 0000000..35ca9b2 --- /dev/null +++ b/database/seeds/TeamTableSeeder.php @@ -0,0 +1,24 @@ +teamName = 'Vidar'; + $team->email = 'aklis@vidar.club'; + $team->password = bcrypt('aklis@hctf'); + $team->signUpTime = Carbon::now('Asia/Shanghai'); + $team->lastLoginTime = Carbon::now('Asia/Shanghai'); + $team->save(); + } +} diff --git a/routes/api.php b/routes/api.php index c641ca5..ce10ce6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -13,6 +13,6 @@ | */ -Route::middleware('auth:api')->get('/user', function (Request $request) { - return $request->user(); -}); +//Route::middleware('auth:api')->get('/user', function (Request $request) { +// return $request->user(); +//}); \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 4914277..98a474e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -13,5 +13,10 @@ Route::get('/', 'IndexController@index'); Route::group(["prefix" => "User"], function(){ - Route::get("index", "UserController@index"); +// Route::get("index", "UserController@index"); + Route::post('login', 'TeamController@login'); + Route::post('register', 'TeamController@register'); + Route::group(['middleware' => 'jwt.auth.mod'], function () { + Route::get('info', 'TeamController@getAuthInfo'); + }); });