diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 890283b7..a2c17b28 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -2,18 +2,36 @@ namespace App\Http\Controllers; +use App\Mail\ResetPassword; use App\Models\Client; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Mail; +use Illuminate\Support\Str; + + +use Illuminate\Support\Facades\Password; +use Illuminate\Support\Facades\Validator; +use Illuminate\Auth\Events\PasswordReset; +use Illuminate\Validation\ValidationException; /** * @OA\Info( - * title="Legalization API", + * title="Legalization API - Authorization", * version="1.0.1" * ) + * @OA\SecurityScheme( + * securityScheme="bearerAuth", + * in="header", + * name="bearerAuth", + * type="http", + * scheme="bearer", + * bearerFormat="JWT", + * ), */ + //controller where all auth process for client happens class AuthController extends Controller { @@ -22,7 +40,7 @@ class AuthController extends Controller * @OA\POST( * path="/api/login", * summary=" - Login user", - * tags = {"authorization"}, + * tags = {"Authorization"}, * @OA\RequestBody( * @OA\MediaType( * mediaType="application/json", @@ -35,13 +53,13 @@ class AuthController extends Controller * property="password", * type="string", * ), - * example={"email": "ilmedovamahri@gmail.com", "password": "Hello001!"} + * example={"email": "ilmedovamahri@gmail.com", "password": 12345678} * ) * ) * ), * @OA\Response( * response="200", - * description="Authorization API - LOGIN user" + * description="OK" * ), * @OA\Response( * response="401", @@ -49,7 +67,7 @@ class AuthController extends Controller * ) * ) */ - public function login(Request $request){//Login - POST (function to login client from API using Sanctum) + public function login(Request $request){ $validatedData = request()->validate([ 'email' => 'required', 'password' => 'required|min:6' @@ -77,7 +95,7 @@ public function login(Request $request){//Login - POST (function to login client * @OA\POST( * path="/api/register", * summary=" - Register user", - * tags = {"authorization"}, + * tags = {"Authorization"}, * @OA\RequestBody( * @OA\MediaType( * mediaType="application/json", @@ -104,7 +122,7 @@ public function login(Request $request){//Login - POST (function to login client * ), * @OA\Response( * response="200", - * description="Authorization API - REGISTER user" + * description="OK" * ), * @OA\Response( * response="401", @@ -112,7 +130,7 @@ public function login(Request $request){//Login - POST (function to login client * ) * ) */ - public function register(Request $request){//Register - POST (function to register client from API using Sanctum) + public function register(Request $request){ $validatedData = request()->validate([ 'email' => 'required', 'password' => 'required|min:6', @@ -146,4 +164,139 @@ public function register(Request $request){//Register - POST (function to regist return response()->json(['success' => ['token' => $tokenResult]], 200); } + /** + * @OA\GET( + * path="/api/client", + * summary=" - Get user", + * tags = {"Authorization"}, + * security={ + * {"bearerAuth": {}} + * }, + * @OA\Response( + * response="200", + * description="OK" + * ), + * @OA\Response( + * response="401", + * description="Unauthorized" + * ) + * ) + */ + public function client(Request $request) { + $user = $request->user(); + if($user){ + return response()->json($request->user(),200); + } + return response()->json([ + 'message' => 'token_expired' + ], 401); + } + + /** + * @OA\POST( + * path="/api/logout", + * summary=" - Logout user", + * tags = {"Authorization"}, + * security={ + * {"bearerAuth": {}} + * }, + * @OA\Response( + * response="200", + * description="OK" + * ), + * @OA\Response( + * response="401", + * description="Unauthorized" + * ) + * ) + */ + public function logout(Request $request) { + // Revoke the token that was used to authenticate the current request + $request->user()->currentAccessToken()->delete(); + //$request->user->tokens()->delete(); // use this to revoke all tokens (logout from all devices) + return response()->json([ + 'message' => 'ok' + ], 200); + } + + /** + * @OA\POST( + * path="/api/forgot-password", + * summary=" - Send a user password reset link", + * tags = {"Authorization"}, + * @OA\RequestBody( + * @OA\MediaType( + * mediaType="application/json", + * @OA\Schema( + * @OA\Property( + * property="email", + * type="string", + * ), + * example={"email": "ilmedovamahri@gmail.com"} + * ) + * ) + * ), + * @OA\Response( + * response="200", + * description="OK" + * ) + * ) + */ + public function sendPasswordResetLinkEmail(Request $request) { + $request->validate(['email' => 'required|email']); + + $user = Client::where('email', $request->email)->first(); + if (!$user) { + return back()->with('failed', 'Failed! email is not registered.'); + } + + $token = Str::random(60); + + $user['token'] = $token; + $user['is_verified'] = 0; + $user->save(); + + Mail::to($request->email)->send(new ResetPassword($user->name, $token)); + + return response()->json([ + 'message' => 'OK' + ], 200); + } + + public function forgotPasswordValidate($token) + { + $user = Client::where('token', $token)->where('is_verified', 0)->first(); + if ($user) { + $email = $user->email; + return response()->json([ + 'message' => compact('email') + ]); + } + return response()->json([ + 'message' => 'token_expired' + ], 419); + } + + public function updatePassword(Request $request) { + $this->validate($request, [ + 'email' => 'required', + 'password' => 'required|min:6', + 'confirm_password' => 'required|same:password' + ]); + + $user = Client::where('email', $request->email)->first(); + if ($user) { + $user['is_verified'] = 0; + $user['token'] = ''; + $user['password'] = Hash::make($request->password); + $user->save(); + return response()->json([ + 'message' => 'OK' + ], 200); + } + return response()->json([ + 'message' => 'not_found' + ], 404); + } + } diff --git a/app/Mail/ResetPassword.php b/app/Mail/ResetPassword.php new file mode 100644 index 00000000..d8dd50a5 --- /dev/null +++ b/app/Mail/ResetPassword.php @@ -0,0 +1,43 @@ +name = $name; + $this->token = $token; + } + + /** + * Build the message. + * + * @return $this + */ + public function build() + { + $user['name'] = $this->name; + $user['token'] = $this->token; + + return $this->from("milmedova96@gmail.com", "Mahri Ilmedova") + ->subject('Password Reset Link') + ->view('emails.reset-password', ['user' => $user]); + } +} diff --git a/app/Models/Client.php b/app/Models/Client.php index 0ee904be..d7a00566 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -37,6 +37,9 @@ class Client extends Authenticatable | FUNCTIONS |-------------------------------------------------------------------------- */ + public function sendPasswordResetNotification($token){ + $this->notify(new \App\Notifications\MailResetPasswordNotification($token)); + } /* |-------------------------------------------------------------------------- diff --git a/app/Notifications/MailResetPasswordNotification.php b/app/Notifications/MailResetPasswordNotification.php new file mode 100644 index 00000000..306c9b99 --- /dev/null +++ b/app/Notifications/MailResetPasswordNotification.php @@ -0,0 +1,40 @@ +pageUrl = 'localhost:8080'; + } + public function via($notifiable){ + return ['mail']; + } + public function toMail($notifiable){ + if(static::$toMailCallback) { + return call_user_func(static::$toMailCallback, $notifiable, $this->token); + } + return (new MailMessage) + ->subject(Lang::getFromJson('Reset application Password v1')) + ->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.')) + ->action(Lang::getFromJson('Reset Password'), $this->pageUrl."?token=".$this->token) + ->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])) + ->line(Lang::getFromJson('If you did not request a password reset, no further action is required.')); + } +} diff --git a/config/auth.php b/config/auth.php index f348efc6..84eab93a 100644 --- a/config/auth.php +++ b/config/auth.php @@ -97,7 +97,7 @@ 'passwords' => [ 'users' => [ - 'provider' => 'users', + 'provider' => 'clients', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, diff --git a/database/migrations/2022_06_30_125804_add_token_to_clients_table.php b/database/migrations/2022_06_30_125804_add_token_to_clients_table.php new file mode 100644 index 00000000..e6c50edd --- /dev/null +++ b/database/migrations/2022_06_30_125804_add_token_to_clients_table.php @@ -0,0 +1,33 @@ +string('token')->nullable(); + $table->boolean('is_verified')->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('clients', function (Blueprint $table) { + // + }); + } +}; diff --git a/resources/views/emails/reset-password.blade.php b/resources/views/emails/reset-password.blade.php new file mode 100644 index 00000000..54b83bcc --- /dev/null +++ b/resources/views/emails/reset-password.blade.php @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + +
+ + + + + + + +
+ Programming Fields +
+ + + + +
+

+ Hello {{ $user ? $user['name'] : '' }}, +

+ We've received a request to reset the password. +

+

+ You can reset your password by clicking the button below: +

+ +

+ Reset your password +

+ + +

+ Thank + you,

+

+ Programming Fields

+
+
+
+ + diff --git a/routes/api.php b/routes/api.php index a525c70e..7b951595 100644 --- a/routes/api.php +++ b/routes/api.php @@ -22,10 +22,11 @@ Route::post('/register', [AuthController::class, 'register']); Route::post('/login', [AuthController::class, 'login']); - +Route::post('/forgot-password', [AuthController::class, 'sendPasswordResetLinkEmail'])->name('passwords.sent'); +Route::post('/reset', [AuthController::class, 'sendResetResponse'])->name('passwords.reset'); +Route::get('/forgot-password/{token}', [AuthController::class, 'forgotPasswordValidate']); Route::middleware(['auth.client','auth:api'])->group(function () { - Route::get('/client', function (Request $request) { - return $request->user(); - }); + Route::get('/client', [AuthController::class, 'client']); + Route::post('/logout', [AuthController::class, 'logout']); }); diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 10c279e9..305989c4 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -1,14 +1,14 @@ { "openapi": "3.0.0", "info": { - "title": "Legalization API", + "title": "Legalization API - Authorization", "version": "1.0.1" }, "paths": { "/api/login": { "post": { "tags": [ - "authorization" + "Authorization" ], "summary": " - Login user", "operationId": "a3b306d14572d1f4bd6c064b3233e7b8", @@ -27,7 +27,7 @@ "type": "object", "example": { "email": "ilmedovamahri@gmail.com", - "password": "Hello001!" + "password": 12345678 } } } @@ -35,7 +35,7 @@ }, "responses": { "200": { - "description": "Authorization API - LOGIN user" + "description": "OK" }, "401": { "description": "Unauthorized" @@ -46,7 +46,7 @@ "/api/register": { "post": { "tags": [ - "authorization" + "Authorization" ], "summary": " - Register user", "operationId": "8a56853624e025573120a09a4c75d468", @@ -81,13 +81,99 @@ }, "responses": { "200": { - "description": "Authorization API - REGISTER user" + "description": "OK" }, "401": { "description": "Unauthorized" } } } + }, + "/api/client": { + "get": { + "tags": [ + "Authorization" + ], + "summary": " - Get user", + "operationId": "9f1c53ceee113e04ff7709c819ce9bf5", + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/logout": { + "post": { + "tags": [ + "Authorization" + ], + "summary": " - Logout user", + "operationId": "fe8f3429cd6979b3b4517e186505f9f9", + "responses": { + "200": { + "description": "OK" + }, + "401": { + "description": "Unauthorized" + } + }, + "security": [ + { + "bearerAuth": [] + } + ] + } + }, + "/api/forgot-password": { + "post": { + "tags": [ + "Authorization" + ], + "summary": " - Send a user password reset link", + "operationId": "45bf57623119762aa1c685aff5d3716e", + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "email": { + "type": "string" + } + }, + "type": "object", + "example": { + "email": "ilmedovamahri@gmail.com" + } + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "securitySchemes": { + "bearerAuth": { + "type": "http", + "name": "bearerAuth", + "in": "header", + "bearerFormat": "JWT", + "scheme": "bearer" + } } } } \ No newline at end of file