password reset functionality added

This commit is contained in:
ilmedova 2022-06-30 19:55:51 +05:00
parent 7de44537a0
commit 224657ef44
9 changed files with 461 additions and 19 deletions

View File

@ -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);
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ResetPassword extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public $name;
public $token;
public function __construct($name, $token)
{
$this->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]);
}
}

View File

@ -37,6 +37,9 @@ class Client extends Authenticatable
| FUNCTIONS
|--------------------------------------------------------------------------
*/
public function sendPasswordResetNotification($token){
$this->notify(new \App\Notifications\MailResetPasswordNotification($token));
}
/*
|--------------------------------------------------------------------------

View File

@ -0,0 +1,40 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
//custom
use Illuminate\Support\Facades\Lang;
use Illuminate\Auth\Notifications\ResetPassword;
class MailResetPasswordNotification extends Notification{
use Queueable;
protected $pageUrl;
public $token;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct(){
parent::__construct($token);
$this->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.'));
}
}

View File

@ -97,7 +97,7 @@
'passwords' => [
'users' => [
'provider' => 'users',
'provider' => 'clients',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,

View File

@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('clients', function (Blueprint $table) {
$table->string('token')->nullable();
$table->boolean('is_verified')->default(0);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('clients', function (Blueprint $table) {
//
});
}
};

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="x-apple-disable-message-reformatting">
<title></title>
<style>
table,
td,
div,
h1,
p {
font-weight: 500;
font-family: Arial, sans-serif;
}
.btn {margin: 10px 0px;
border-radius: 4px;
text-decoration: none;
color: #fff !important;
height: 46px;
padding: 10px 20px;
font-size: 16px;
font-weight: 600;
background-image: linear-gradient(to right top, #021d68, #052579, #072d8b, #09369d, #093fb0) !important;
}
.btn:hover {
text-decoration: none;
opacity: .8;
}
</style>
</head>
<body style="margin:0;padding:0;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;background:#ffffff;">
<tr>
<td align="center" style="padding:0;">
<table role="presentation"
style="width:600px;border-collapse:collapse;border:1px solid #cccccc;border-spacing:0;text-align:left;">
<tr style="border-collapse:collapse;border:1px solid #cccccc;border-spacing:0;">
<td align="left" style="padding:10px 25px;background:#fff; display: flex; align-items: center;">
<span style="font-weight: bold; padding-top: 10px;"> Programming Fields </span>
</td>
</tr>
<tr>
<td style="padding:36px 30px 42px 30px;">
<table role="presentation"
style="width:100%;border-collapse:collapse;border:0;border-spacing:0;">
<tr>
<td style="padding:0 0 36px 0;color:#153643;">
<p style="font-weight:bold;margin:0 0 20px 0;font-family:Arial,sans-serif;">
Hello {{ $user ? $user['name'] : '' }},</h1>
<p
style="margin:0 0 12px 0;font-size:14px;line-height:24px;font-family:Arial,sans-serif;">
We've received a request to reset the password.
</p>
<p
style="margin:10px 0 12px 0;font-size:14px;line-height:24px;font-family:Arial,sans-serif;">
You can reset your password by clicking the button below:
</p>
<p style="text-align: center;">
<a href="{{'http://localhost:8000/forgot-password/'.$user['token']}}" class="btn">Reset your password</a>
</p>
<p style="margin:100px 0 12px 0;font-size:14px;font-family:Arial,sans-serif;">
Thank
you, </p>
<p style="margin:0 0 12px 0;font-size:14px;font-family:Arial,sans-serif;">
Programming Fields </p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -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']);
});

View File

@ -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"
}
}
}
}