diff --git a/app/Http/Controllers/Admin/APIs/Customer_API/AuthController.php b/app/Http/Controllers/Admin/APIs/Customer_API/AuthController.php index ea3f2c3..b5fb1dc 100644 --- a/app/Http/Controllers/Admin/APIs/Customer_API/AuthController.php +++ b/app/Http/Controllers/Admin/APIs/Customer_API/AuthController.php @@ -7,10 +7,13 @@ use App\Services\APIs\CustomerAPIs\AuthServices; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Validation\Rule; +use App\Models\IamPrincipalOtp; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Hash; use Illuminate\Database\QueryException; use App\Models\IamPrincipal; use Carbon\Carbon; +use Exception; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Validator; @@ -87,7 +90,7 @@ class AuthController extends Controller if ($validator->fails()) { $validationErrors = $validator->errors()->all(); - Log::error("Registation validation error: " . implode(", ", $validationErrors)); + Log::error("Customer validation error: " . implode(", ", $validationErrors)); return jsonResponseWithErrorMessageApi($validationErrors, 403); } return $this->AuthServices->register($request); @@ -96,4 +99,157 @@ class AuthController extends Controller return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); } } + + /** + * Created By : sayli Raut + * Created at : 24 May 2024 + * Use : Customer login. + */ + public function login(Request $request) + { + + try { + $validator = Validator::make($request->all(), [ + 'email_address' => 'required|string|email', + 'password' => 'required|string|min:6', + ]); + + if ($validator->fails()) { + $validationErrors = $validator->errors()->all(); + Log::error("Login validation error: " . implode(", ", $validationErrors)); + return jsonResponseWithErrorMessageApi($validationErrors, 403); + } + + return $this->AuthServices->login($request); + } catch (QueryException $e) { + + Log::error('Customer Login Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } + + /** + * Created By : sayli Raut + * Created at : 24 May 2024 + * Use : forgot password. + */ + public function forgotPassword(Request $request) + { + try { + $validator = Validator::make($request->all(), [ + 'email_address' => [ + 'required', + 'string', + 'email', + function ($attribute, $value, $fail) { + $existingUser = IamPrincipal::where('email_address', $value)->where('principal_type_xid', 3)->whereNull('deleted_at')->exists(); + if (!$existingUser) { + $fail('The selected email address is invalid.'); + } + }, + ], + ]); + + if ($validator->fails()) { + $validationErrors = $validator->errors()->all(); + Log::error("Forgot password validation error: " . implode(", ", $validationErrors)); + return jsonResponseWithErrorMessageApi($validationErrors, 403); + } + return $this->AuthServices->forgotPassword($request); + + } catch (Exception $e) { + Log::error('Customer Forgot Password OTP function failed: ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.something_went_wrong'), 500); + } + } + + + /** + * Created By : sayli Raut + * Created at : 24 May 2024 + * Use : OTP verification. + */ + public function verifyOTPForgotPassword(Request $request) + { + try { + $validator = Validator::make($request->all(), [ + 'email_address' => [ + 'required', + 'string', + 'email', + function ($attribute, $value, $fail) { + $existingUser = IamPrincipal::where('email_address', $value)->where('principal_type_xid', 3)->whereNull('deleted_at')->exists(); + if (!$existingUser) { + $fail('The selected email address is invalid.'); + } + }, + ], + 'otp' => 'required', + ]); + + if ($validator->fails()) { + $validationErrors = $validator->errors()->all(); + Log::error("Forgot password validation error: " . implode(", ", $validationErrors)); + return jsonResponseWithErrorMessageApi($validationErrors, 403); + } + return $this->AuthServices->verifyOTPForgotPassword($request); + + } catch (Exception $e) { + DB::rollBack(); + Log::error("An error occurred in " . __METHOD__ . ": " . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.something_went_wrong'), 500); + } + } + + /** + * Created By : sayli Raut + * Created at : 24 May 2024 + * Use : Change Password. + */ + public function changePassword(Request $request) + { + try { + $validator = Validator::make($request->all(), [ + 'iam_principal_xid' => 'required|exists:iam_principal,id', + 'password' => 'required|confirmed', + ]); + if ($validator->fails()) { + $validationErrors = $validator->errors()->all(); + Log::error("Forgot password validation error: " . implode(", ", $validationErrors)); + return jsonResponseWithErrorMessageApi($validationErrors, 403); + } + return $this->AuthServices->changePassword($request); + + } catch (Exception $e) { + Log::error("An error occurred in " . __METHOD__ . ": " . $e->getMessage()); + return response()->json(__('something_went_wrong'), 500); + } + } + + /** + * Created By : sayli Raut + * Created at : 24 May 2024 + * Use : Resend OTP . + */ + public function resendOtp(Request $request) + { + try { + $validator = Validator::make($request->all(), [ + 'iam_principal_xid' => 'required|exists:iam_principal,id', + 'otp_purpose' => 'required' + ]); + + if ($validator->fails()) { + $validationErrors = $validator->errors()->all(); + Log::error("Forgot password validation error: " . implode(", ", $validationErrors)); + return jsonResponseWithErrorMessageApi($validationErrors, 403); + } + return $this->AuthServices->resendOtp($request); + } catch (Exception $e) { + + DB::rollBack(); + Log::error("An error occurred in " . __METHOD__ . ": " . $e->getMessage()); + return response()->json(__('something_went_wrong'), 500); + } + } } diff --git a/app/Http/Helpers/Webhelper.php b/app/Http/Helpers/Webhelper.php index 04a152f..30aa672 100644 --- a/app/Http/Helpers/Webhelper.php +++ b/app/Http/Helpers/Webhelper.php @@ -165,4 +165,20 @@ if (!function_exists('readRestHeaderToken')) { return false; } } + + if (!function_exists('generateOTP')) { + + function generateOTP() + { + // Define the length of the OTP + $otpLength = 4; + + // Generate a random OTP with $otpLength digits + $otp = ''; + for ($i = 0; $i < $otpLength; $i++) { + $otp .= rand(0, 9); + } + return $otp; + } +} } diff --git a/app/Models/IamPrincipal.php b/app/Models/IamPrincipal.php index 0847fce..d7d0c83 100644 --- a/app/Models/IamPrincipal.php +++ b/app/Models/IamPrincipal.php @@ -14,7 +14,7 @@ use App\Models\admin\ManageModule; use App\Models\OrderedPassport; -class IamPrincipal extends Model +class IamPrincipal extends Authenticatable implements JWTSubject { use SoftDeletes; use HasApiTokens, HasFactory, Notifiable; diff --git a/app/Services/APIs/CustomerAPIs/AuthServices.php b/app/Services/APIs/CustomerAPIs/AuthServices.php index 09106d0..5cc7922 100644 --- a/app/Services/APIs/CustomerAPIs/AuthServices.php +++ b/app/Services/APIs/CustomerAPIs/AuthServices.php @@ -1,10 +1,14 @@ input('age'); + $age = $request->input('age'); if ($age == 'yes') { return jsonResponseWithSuccessMessage(__('auth.legally_21'), 200); } else { @@ -33,7 +37,6 @@ class AuthServices public function register($request) { - dd($request); try { DB::beginTransaction(); $user = IamPrincipal::create([ @@ -56,7 +59,7 @@ class AuthServices 'access_token' => $token, 'token_type' => 'bearer', ]; - return jsonResponseWithSuccessMessage(__('auth.Rest_user_created'), $response, 200); + return jsonResponseWithSuccessMessage(__('auth.Customer_user_created'), $response, 200); } catch (QueryException $e) { DB::rollBack(); Log::error('Restaurant Registration Failed ' . $e->getMessage()); @@ -64,4 +67,222 @@ class AuthServices } } + public function login($request) + { + try { + $credentials = [ + 'email_address' => $request->email_address, + 'password' => $request->password, + ]; + + $isDelete = IamPrincipal::where('email_address', $request->email_address)->where('principal_type_xid', 3)->where('deleted_by_admin', 1)->onlyTrashed()->first(); + if ($isDelete) { + return jsonResponseWithErrorMessageApi(__('auth.deleted_user_by_admin'), 403); + } + $isExistEmail = IamPrincipal::where('email_address', $request->email_address)->where('principal_type_xid', 3)->whereNull('deleted_at')->first(); + if ($isExistEmail == null) { + return jsonResponseWithErrorMessageApi(__('auth.incorrect_email_passport'), 403); + } + if ($isExistEmail && !(Hash::check($request->password, $isExistEmail->password))) { + Log::error('Entered Password is wrong.'); + return jsonResponseWithErrorMessageApi(__('auth.incorrect_email_passport'), 403); + } + + if (!$token = auth()->login($isExistEmail)) { + Log::error('Customer Login Failed'); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + + $isExistEmail->one_signal_player_id = $request->one_signal_player_id; + $isExistEmail->save(); + $response = [ + 'userId' => $isExistEmail->id, + 'access_token' => $token, + ]; + + return jsonResponseWithSuccessMessage(__('auth.data_fetched_successfully'), $response, 200); + } catch (QueryException $e) { + Log::error('Customer Login Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } + + + public function forgotPassword($request) + { + try { + DB::beginTransaction(); + + $user = IamPrincipal::where('email_address', $request->email_address)->where('principal_type_xid', 3)->whereNull('deleted_at')->first(); + if ($user == null) { + Log::error('Email not exist'); + return jsonResponseWithErrorMessageApi(__('auth.incorrect_email'), 403); + } + + $otp = generateOTP(); + + IamPrincipalOTP::updateOrCreate( + ['principal_xid' => $user->id], + [ + 'otp_code' => $otp, + 'otp_purpose' => 'forgot password', + 'valid_till' => Carbon::now()->addMinutes(2), + 'is_used' => 0, + ] + ); + + $mail = Mail::send( + 'frontend.Mail.customer_forgot_password_mail', + [ + 'user' => $user, + 'otp_code' => $otp, + 'valid_till' => Carbon::now()->addMinutes(2) + ], + function ($message) use ($user) { + $message->to($user->email_address); + $message->subject('Forgot Password Mail Page'); + } + ); + + + DB::commit(); + Log::info('Customer Forgot Password otp sent successfully'); + + $response = [ + 'iam_principal_xid' => $user->id, + ]; + + + return jsonResponseWithSuccessMessageApi(__('auth.otp_sent_successfully'), $response, 200); + } catch (QueryException $e) { + DB::rollBack(); + + Log::error('Customer Forgot password Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } + + + public function verifyOTPForgotPassword($request) + { + try { + DB::beginTransaction(); + $User = IamPrincipal::where('email_address', $request->email_address)->where('principal_type_xid', 3)->whereNull('deleted_at')->first(); + + $iamPrincipal = IamPrincipalOTP::where('principal_xid', $User->id)->first(); + + if (!$iamPrincipal) { + Log::error('User not exist'); + return jsonResponseWithErrorMessageApi(__('auth.failed_to_verify_otp'), 403); + } + + if ($iamPrincipal->otp_code !== $request->otp) { + Log::error('Customer entered invalid otp'); + return jsonResponseWithErrorMessageApi(__('auth.invalid_otp'), 403); + } + + if (Carbon::now()->gt($iamPrincipal->valid_till)) { + + Log::error('Customer otp Exipred'); + return jsonResponseWithErrorMessageApi(__('auth.otp_expired'), 403); + } + + if ($iamPrincipal->is_used === 1) { + Log::error('Customer otp Already used'); + return jsonResponseWithErrorMessageApi(__('auth.otp_already_used'), 403); + } + + $iamPrincipal->is_used = 1; + $iamPrincipal->save(); + DB::commit(); + $response = [ + 'iam_principal_xid' => $User->id + ]; + Log::info('Customer OTP verified successfully'); + return jsonResponseWithSuccessMessageApi(__('auth.otp_verified'), $response, 200); + } catch (QueryException $e) { + DB::rollBack(); + Log::error('Customer verify otp Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } + + + + public function changePassword($request) + { + try { + DB::beginTransaction(); + $User = IamPrincipal::find($request->iam_principal_xid); + $User->password = Hash::make($request->password); + $User->save(); + DB::commit(); + return jsonResponseWithSuccessMessageApi(__('auth.password_updated_successfully')); + + } catch (QueryException $e) { + DB::rollBack(); + Log::error('Customer change password Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } + + + public function resendOtp($request) + { + try { + DB::beginTransaction(); + $iamPrincipal = IamPrincipalOTP::where('principal_xid', $request->iam_principal_xid)->first(); + $user = IamPrincipal::where('id', $request->iam_principal_xid)->first(); + + if (!$iamPrincipal) { + return response()->json('OTP not found for this user.', 203); + } + + $allowedResendInterval = Carbon::now()->subMinutes(2); + + if ($iamPrincipal->updated_at >= $allowedResendInterval) { + + return jsonResponseWithErrorMessageApi(__('auth.try_resend_otp'), 429); + } + + $otp = generateOTP(); + + $iamPrincipal->principal_xid = $request->iam_principal_xid; + $iamPrincipal->otp_code = $otp; + $iamPrincipal->otp_purpose = $request->otp_purpose; + $iamPrincipal->valid_till = Carbon::now()->addMinutes(2); + $iamPrincipal->is_used = 0; + $iamPrincipal->save(); + + + + $mail = Mail::send( + 'frontend.Mail.customer_forgot_password_mail', + [ + 'user' => $user, + 'otp_code' => $otp, + 'valid_till' => Carbon::now()->addMinutes(2) + ], + function ($message) use ($user) { + $message->to($user->email_address); + $message->subject('Forgot Password Mail Page'); + } + ); + + + DB::commit(); + + $response = [ + 'iam_principal_xid' => $iamPrincipal->principal_xid, + 'email_address' => $user->email_address + + ]; + + return jsonResponseWithSuccessMessageApi(__('auth.otp_resend_sent_successfully'), $response, 200); + } catch (QueryException $e) { + DB::rollBack(); + Log::error('Resend otp Failed ' . $e->getMessage()); + return jsonResponseWithErrorMessageApi(__('auth.authentication_failed'), 403); + } + } } diff --git a/config/app.php b/config/app.php index f467267..3cb6d56 100644 --- a/config/app.php +++ b/config/app.php @@ -1,5 +1,9 @@ env('APP_URL', 'http://localhost'), + 'asset_url' => env('ASSET_URL'), + /* |-------------------------------------------------------------------------- @@ -78,34 +84,42 @@ return [ | */ - 'locale' => env('APP_LOCALE', 'en'), + 'locale' => 'en', - 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), - 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), + + 'fallback_locale' => 'en', + + /* + |-------------------------------------------------------------------------- + | Faker Locale + |-------------------------------------------------------------------------- + | + | This locale will be used by the Faker PHP library when generating fake + | data for your database seeds. For example, this will be used to get + | localized telephone numbers, street address information and more. + | + */ + + + 'faker_locale' => 'en_US', /* |-------------------------------------------------------------------------- | Encryption Key |-------------------------------------------------------------------------- | - | This key is utilized by Laravel's encryption services and should be set - | to a random, 32 character string to ensure that all encrypted values - | are secure. You should do this prior to deploying the application. + | This key is used by the Illuminate encrypter service and should be set + | to a random, 32 character string, otherwise these encrypted strings + | will not be safe. Please do this before deploying an application! | */ - 'cipher' => 'AES-256-CBC', - 'key' => env('APP_KEY'), - 'previous_keys' => [ - ...array_filter( - explode(',', env('APP_PREVIOUS_KEYS', '')) - ), - ], + 'cipher' => 'AES-256-CBC', - /* + /* |-------------------------------------------------------------------------- | Maintenance Mode Driver |-------------------------------------------------------------------------- @@ -119,8 +133,61 @@ return [ */ 'maintenance' => [ - 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), - 'store' => env('APP_MAINTENANCE_STORE', 'database'), + 'driver' => 'file', + // 'store' => 'redis', ], + + /* + |-------------------------------------------------------------------------- + | Autoloaded Service Providers + |-------------------------------------------------------------------------- + | + | The service providers listed here will be automatically loaded on the + | request to your application. Feel free to add your own services to + | this array to grant expanded functionality to your applications. + | + */ + + 'providers' => ServiceProvider::defaultProviders()->merge([ + /* + * Package Service Providers... + */ + + /* + * Application Service Providers... + */ + App\Providers\AppServiceProvider::class, + // App\Providers\AuthServiceProvider::class, + // Barryvdh\DomPDF\ServiceProvider::class, + // App\Providers\BroadcastServiceProvider::class, + // App\Providers\EventServiceProvider::class, + App\Providers\RouteServiceProvider::class, + Tymon\JWTAuth\Providers\LaravelServiceProvider::class, + // Ladumor\OneSignal\OneSignalServiceProvider::class, + // Maatwebsite\Excel\ExcelServiceProvider::class, + + ])->toArray(), + + /* + |-------------------------------------------------------------------------- + | Class Aliases + |-------------------------------------------------------------------------- + | + | This array of class aliases will be registered when this application + | is started. However, feel free to register as many as you wish as + | the aliases are "lazy" loaded so they don't hinder performance. + | + */ + + 'aliases' => Facade::defaultAliases()->merge([ + // 'Example' => App\Facades\Example::class, + 'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class, + 'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class, + // 'OneSignal' => \Ladumor\OneSignal\OneSignal::class, + // 'PDF' => Barryvdh\DomPDF\Facade::class, + // 'Excel' => Maatwebsite\Excel\Facades\Excel::class, + + ])->toArray(), + ]; diff --git a/resources/lang/en/auth.php b/resources/lang/en/auth.php index f60c6e6..2f88e3e 100644 --- a/resources/lang/en/auth.php +++ b/resources/lang/en/auth.php @@ -87,6 +87,7 @@ return [ 'passport_search' => 'Passport Search successfully', 'not_found_otp' => 'OTP not found for this user', 'Rest_user_created' => 'Restaurant user created successfully', + 'Customer_user_created' => 'Customer user created successfully', 'User_details_fetch' => 'User details fetch successfully', 'Voucher_not_found' => 'Voucher not found', 'delete_user' => 'Customer deleted successfully', diff --git a/resources/views/frontend/Mail/customer_forgot_password_mail.blade.php b/resources/views/frontend/Mail/customer_forgot_password_mail.blade.php new file mode 100644 index 0000000..a86b7d8 --- /dev/null +++ b/resources/views/frontend/Mail/customer_forgot_password_mail.blade.php @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + + +
+ +
+ {{-- user --}} +
+
+ +
+

+ One-Time Password (OTP) for verification +

+

{{ 'Dear ' . $user->first_name }} +

+

Your verification code is + {{ $otp_code }}. +

+

The otp is valid for 2 minutes

+
+
+
+
+ + + + diff --git a/routes/customer_api.php b/routes/customer_api.php index 681ef92..bf90acf 100644 --- a/routes/customer_api.php +++ b/routes/customer_api.php @@ -14,6 +14,10 @@ Route::post('/v1/register', [AuthController::class, 'register']); Route::post('/v1/login', [AuthController::class, 'login']); Route::post('/v1/forgot-password', [AuthController::class, 'forgotPassword']); Route::post('/v1/password/verify-otp', [AuthController::class, 'verifyOtpForgotPassword']); +Route::post('/v1/change-password', [AuthController::class, 'changePassword']); +Route::post('/v1/resend-otp', [AuthController::class, 'resendOtp']); + +// Route::group(['middleware' => ['customer.jwt.verify']], function () { //*******************************************************CMS******************************************************** @@ -28,3 +32,6 @@ Route::get('/v1/list-of-news-articles', [CMSApiController::class, 'getNewsArticl // }); + +// }); +