<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AL Mamon</title>
    <description>The latest articles on DEV Community by AL Mamon (@mamondev193).</description>
    <link>https://dev.to/mamondev193</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3133040%2Fd51bc864-bc57-4972-90aa-07f1a0739241.jpg</url>
      <title>DEV Community: AL Mamon</title>
      <link>https://dev.to/mamondev193</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mamondev193"/>
    <language>en</language>
    <item>
      <title>Implementing Sign in with Apple in Laravel REST API</title>
      <dc:creator>AL Mamon</dc:creator>
      <pubDate>Tue, 13 May 2025 03:55:11 +0000</pubDate>
      <link>https://dev.to/mamondev193/implementing-sign-in-with-apple-in-laravel-rest-api-m5o</link>
      <guid>https://dev.to/mamondev193/implementing-sign-in-with-apple-in-laravel-rest-api-m5o</guid>
      <description>&lt;p&gt;To implement Sign in with Apple functionality in your Laravel REST API, you'll need to follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;p&gt;An Apple Developer account&lt;/p&gt;

&lt;p&gt;A registered app in Apple Developer portal&lt;/p&gt;

&lt;p&gt;Laravel 8.x or higher&lt;/p&gt;

&lt;p&gt;PHP 7.4 or higher&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Setup Apple Developer Configuration&lt;/p&gt;

&lt;p&gt;Go to Apple Developer Portal&lt;/p&gt;

&lt;p&gt;Create a new App ID with "Sign In with Apple" capability enabled&lt;/p&gt;

&lt;p&gt;Note your Service ID (e.g., com.example.app)&lt;/p&gt;

&lt;p&gt;Generate a private key for "Sign In with Apple" and download it&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install Required Packages&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require laravel/socialite
composer require socialiteproviders/apple
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add these to your .env file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APPLE_CLIENT_ID=nz.co.fskills.app
APPLE_TEAM_ID=J43F88Y4BH
APPLE_KEY_ID=CLU5NYTK5P
APPLE_PRIVATE_KEY_PATH=storage/apple_private_key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;config/services.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'apple' =&amp;gt; [
  'client_id' =&amp;gt; env('APPLE_CLIENT_ID'),
  'client_secret' =&amp;gt; env('APPLE_CLIENT_SECRET'),
  'redirect' =&amp;gt; env('APPLE_REDIRECT_URI')
],
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Store the apple_private_key.pem in &lt;code&gt;storage/apple_private_key.pem&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-----BEGIN PRIVATE KEY-----
Example key
-----END PRIVATE KEY-----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an Controller :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public function SocialLogin(Request $request): \Illuminate\Http\JsonResponse
    {

        $request-&amp;gt;validate([
            'token'    =&amp;gt; 'required',
            'provider' =&amp;gt; 'required|in:google,facebook,apple',
        ]);

        try {
            $provider   = $request-&amp;gt;provider;
            $socialUser = Socialite::driver($provider)-&amp;gt;stateless()-&amp;gt;userFromToken($request-&amp;gt;token);
            //return response()-&amp;gt;json($socialUser);

            if ($socialUser) {
                $user      = User::withTrashed()-&amp;gt;where('email', $socialUser-&amp;gt;email)-&amp;gt;first();
                if (!empty($user-&amp;gt;deleted_at)) {
                    return Helper::jsonErrorResponse('Your account has been deleted.',410);
                }
                $isNewUser = false;

                if (!$user) {
                    $password = Str::random(16);
                    $user     = User::create([
                        'name'              =&amp;gt; $socialUser-&amp;gt;getName(),
                        'email'             =&amp;gt; $socialUser-&amp;gt;getEmail(),
                        'password'          =&amp;gt; bcrypt($password),
                        'avatar'             =&amp;gt; $socialUser-&amp;gt;getAvatar(),
                        'email_verified_at' =&amp;gt; now(),
                    ]);
                    $isNewUser = true;
                }

                Auth::login($user);
                $token = auth('api')-&amp;gt;login($user);

                return response()-&amp;gt;json([
                    'status'     =&amp;gt; true,
                    'message'    =&amp;gt; 'User logged in successfully.',
                    'code'       =&amp;gt; 200,
                    'token_type' =&amp;gt; 'bearer',
                    'token'      =&amp;gt; $token,
                    'expires_in' =&amp;gt; auth('api')-&amp;gt;factory()-&amp;gt;getTTL() * 60,
                    'data'       =&amp;gt; $user,
                ],200);
            } else {
                return Helper::jsonResponse(false, 'Unauthorized', 401);
            }
        } catch (Exception $e) {
            return Helper::jsonResponse(false, 'Something went wrong', 500, ['error' =&amp;gt; $e-&amp;gt;getMessage()]);
        }
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create a route:
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Route::post('/social-login', [SocialLoginController::class, 'SocialLogin']);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Laravel 12 API Integration with Sanctum: Step-by-Step Guide</title>
      <dc:creator>AL Mamon</dc:creator>
      <pubDate>Sat, 10 May 2025 13:56:13 +0000</pubDate>
      <link>https://dev.to/mamondev193/laravel-12-api-integration-with-sanctum-step-by-step-guide-2hio</link>
      <guid>https://dev.to/mamondev193/laravel-12-api-integration-with-sanctum-step-by-step-guide-2hio</guid>
      <description>&lt;h2&gt;
  
  
  Installation &amp;amp; Setup
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 1: Install Laravel&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer create-project laravel/laravel example-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a new Laravel project in the example-app directory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 2: Install Sanctum&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan install:api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This installs Laravel Sanctum for API authentication. Note: In Laravel 12, you might need to install Sanctum separately with:&lt;br&gt;
Step 3: Sanctum Configuration in migration file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Database Configuration
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Step 3: Migration Setup&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The provided migration file extends the default Laravel user table with additional fields for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('users', function (Blueprint $table) {
            $table-&amp;gt;id();
            $table-&amp;gt;string('name');
            $table-&amp;gt;string('email')-&amp;gt;unique();
            $table-&amp;gt;timestamp('email_verified_at')-&amp;gt;nullable();
            $table-&amp;gt;string('password');
            $table-&amp;gt;string('otp')-&amp;gt;nullable();
            $table-&amp;gt;string('otp_created_at')-&amp;gt;nullable();
            $table-&amp;gt;boolean('is_otp_verified')-&amp;gt;default(false);
            $table-&amp;gt;timestamp('otp_expires_at')-&amp;gt;nullable();
            $table-&amp;gt;string('reset_password_token')-&amp;gt;nullable();
            $table-&amp;gt;timestamp('reset_password_token_expire_at')-&amp;gt;nullable();
            $table-&amp;gt;string('delete_token')-&amp;gt;nullable();
            $table-&amp;gt;timestamp('delete_token_expires_at')-&amp;gt;nullable();
            $table-&amp;gt;string('deleted_at')-&amp;gt;nullable();
            $table-&amp;gt;rememberToken();
            $table-&amp;gt;timestamps();
        });

        Schema::create('password_reset_tokens', function (Blueprint $table) {
            $table-&amp;gt;string('email')-&amp;gt;primary();
            $table-&amp;gt;string('token');
            $table-&amp;gt;timestamp('created_at')-&amp;gt;nullable();
        });

        Schema::create('sessions', function (Blueprint $table) {
            $table-&amp;gt;string('id')-&amp;gt;primary();
            $table-&amp;gt;foreignId('user_id')-&amp;gt;nullable()-&amp;gt;index();
            $table-&amp;gt;string('ip_address', 45)-&amp;gt;nullable();
            $table-&amp;gt;text('user_agent')-&amp;gt;nullable();
            $table-&amp;gt;longText('payload');
            $table-&amp;gt;integer('last_activity')-&amp;gt;index();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('users');
        Schema::dropIfExists('password_reset_tokens');
        Schema::dropIfExists('sessions');
    }
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Model Configuration&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Step 4: User Model &lt;code&gt;(app/Models/User.php)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The User model is configured with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

namespace App\Models;


use Laravel\Sanctum\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class User extends Authenticatable
{
use HasFactory, Notifiable,HasApiTokens,SoftDeletes;

    protected $guarded = ['id'];

    protected $hidden = [
        'password',
        'otp',
        'otp_expires_at',
        'email_verified_at',
        'reset_password_token',
        'reset_password_token_expire_at',
        'is_otp_verified',
        'created_at',
        'updated_at',
        'role',
        'status',
        'remember_token',
    ];

    /**
     * Get the attributes that should be cast.
     *
     * @return array&amp;lt;string, string&amp;gt;
     */
    protected function casts(): array
    {
        return [
            'email_verified_at' =&amp;gt; 'datetime',
            'otp_expires_at' =&amp;gt; 'datetime',
            'is_otp_verified' =&amp;gt; 'boolean',
            'reset_password_token_expires_at' =&amp;gt; 'datetime',
            'password' =&amp;gt; 'hashed'
        ];
    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  API Routes
&lt;/h2&gt;

&lt;p&gt;Step 5: Route Configuration &lt;code&gt;(routes/api.php)&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;api.php&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\Auth\LoginController;
use App\Http\Controllers\API\Auth\LogoutController;
use App\Http\Controllers\API\Auth\RegisterController;
use App\Http\Controllers\API\Auth\ResetPasswordController;


//register
Route::post('register', [RegisterController::class, 'register']);
Route::post('/verify-email', [RegisterController::class, 'VerifyEmail']);
Route::post('/resend-otp', [RegisterController::class, 'ResendOtp']);
//login
Route::post('login', [LoginController::class, 'login']);
//forgot password
Route::post('/forget-password', [ResetPasswordController::class, 'forgotPassword']);
Route::post('/verify-otp', [ResetPasswordController::class, 'VerifyOTP']);
Route::post('/reset-password', [ResetPasswordController::class, 'ResetPassword']);

Route::group(['middleware' =&amp;gt; 'auth:sanctum'], static function () {
    Route::get('/refresh-token', [LoginController::class, 'refreshToken']);
    Route::post('/logout', [LogoutController::class, 'logout']);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Controller Implementation
&lt;/h2&gt;

&lt;p&gt;RegisterController&lt;/p&gt;

&lt;p&gt;Handles user registration and email verification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Controllers\API\Auth;

use Exception;
use Carbon\Carbon;
use App\Models\User;
use App\Mail\OtpMail;
use App\Helpers\Helper;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    public function register(Request $request): \Illuminate\Http\JsonResponse
    {
        $request-&amp;gt;validate([

            'name' =&amp;gt; 'nullable|string|max:100',
            'email' =&amp;gt; 'required|string|email|max:150|unique:users',
            'password' =&amp;gt; 'required|string|min:8|confirmed',
        ]);
        try {
            $otp = random_int(1000, 9999);
            $otpExpiresAt = Carbon::now()-&amp;gt;addMinutes(60);

            $user = User::create([

                'name' =&amp;gt; $request-&amp;gt;input('name'),
                'email' =&amp;gt; $request-&amp;gt;input('email'),
                'password' =&amp;gt; Hash::make($request-&amp;gt;input('password')),
                'otp' =&amp;gt; $otp,
                'otp_expires_at' =&amp;gt; $otpExpiresAt,
                'is_otp_verified' =&amp;gt; false,
            ]);
            // Send OTP email
            Mail::to($user-&amp;gt;email)-&amp;gt;send(mailable: new OtpMail($otp, $user, 'Verify Your Email Address'));
            return response()-&amp;gt;json([
                'status' =&amp;gt; true,
                'message' =&amp;gt; 'User successfully registered. Please verify your email to log in.',
                'code' =&amp;gt; 201,
                'data' =&amp;gt; $user
            ], 201);
        } catch (Exception $e) {
            return Helper::jsonErrorResponse('User registration failed', 500, [$e-&amp;gt;getMessage()]);
        }
    }
    public function VerifyEmail(Request $request): \Illuminate\Http\JsonResponse
    {
        $request-&amp;gt;validate([
            'email' =&amp;gt; 'required|email|exists:users,email',
            'otp'   =&amp;gt; 'required|digits:4',
        ]);
         try {
            $user = User::where('email', $request-&amp;gt;input('email'))-&amp;gt;first();

            // Check if email has already been verified
            if (!empty($user-&amp;gt;email_verified_at)) {
                $user-&amp;gt;is_verified = true;
                return Helper::jsonResponse(true, 'Email already verified.', 409);
            }

            if ((string)$user-&amp;gt;otp !== (string)$request-&amp;gt;input('otp')) {
                return Helper::jsonErrorResponse('Invalid OTP code', 422);
            }

            // Check if OTP has expired
            if (Carbon::parse($user-&amp;gt;otp_expires_at)-&amp;gt;isPast()) {
                return Helper::jsonErrorResponse('OTP has expired. Please request a new OTP.', 422);
            }
            $token = $user-&amp;gt;createToken('YourAppName')-&amp;gt;plainTextToken;
            // Verify the email
            $user-&amp;gt;email_verified_at = now();
            $user-&amp;gt;is_verified = true;
            $user-&amp;gt;otp = null;
            $user-&amp;gt;otp_expires_at = null;
            $user-&amp;gt;save();
            return response()-&amp;gt;json([
                'status' =&amp;gt; true,
                'message' =&amp;gt; 'Email verification successful.',
                'code' =&amp;gt; 200,
                'token'      =&amp;gt; $token,
                'data' =&amp;gt; [
                    'id' =&amp;gt; $user-&amp;gt;id,
                    'email' =&amp;gt; $user-&amp;gt;email,
                    'name' =&amp;gt; $user-&amp;gt;name,
                    'is_verified' =&amp;gt; $user-&amp;gt;is_verified,
                ]
            ], 200);

         } catch (Exception $e) {
             return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), $e-&amp;gt;getCode());
         }
    }

    public function ResendOtp(Request $request): \Illuminate\Http\JsonResponse
    {
        $request-&amp;gt;validate([
            'email' =&amp;gt; 'required|email|exists:users,email',
        ]);
        try {
            $user = User::where('email', $request-&amp;gt;input('email'))-&amp;gt;first();
            if (!$user) {
                return Helper::jsonErrorResponse('User not found.', 404);
            }

            if ($user-&amp;gt;email_verified_at) {
                return Helper::jsonErrorResponse('Email already verified.', 409);
            }

            $newOtp               = random_int(1000, 9999);
            $otpExpiresAt         = Carbon::now()-&amp;gt;addMinutes(60);
            $user-&amp;gt;otp            = $newOtp;
            $user-&amp;gt;otp_expires_at = $otpExpiresAt;
            $user-&amp;gt;save();
            Mail::to($user-&amp;gt;email)-&amp;gt;send(new OtpMail($newOtp,$user,'Verify Your Email Address'));

            return Helper::jsonResponse(true, 'A new OTP has been sent to your email.', 200);
        } catch (Exception $e) {
            return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), $e-&amp;gt;getCode());
        }
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LoginController
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&amp;lt;?php&lt;br&gt;
namespace App\Http\Controllers\API\Auth;&lt;/p&gt;

&lt;p&gt;use App\Helpers\Helper;&lt;br&gt;
use App\Http\Controllers\Controller;&lt;br&gt;
use App\Models\User;&lt;br&gt;
use Exception;&lt;br&gt;
use Illuminate\Http\Request;&lt;br&gt;
use Illuminate\Support\Facades\Hash;&lt;/p&gt;

&lt;p&gt;class LoginController extends Controller&lt;br&gt;
{&lt;br&gt;
    public function Login(Request $request): \Illuminate\Http\JsonResponse&lt;br&gt;
    {&lt;br&gt;
        $request-&amp;gt;validate([&lt;br&gt;
            'email'    =&amp;gt; 'required|string',&lt;br&gt;
            'password' =&amp;gt; 'required|string',&lt;br&gt;
        ]);&lt;br&gt;
        try {&lt;br&gt;
            if (filter_var($request-&amp;gt;email, FILTER_VALIDATE_EMAIL) !== false) {&lt;br&gt;
                $user = User::withTrashed()-&amp;gt;where('email', $request-&amp;gt;email)-&amp;gt;first();&lt;br&gt;
                if (empty($user)) {&lt;br&gt;
                    return Helper::jsonErrorResponse('User not found', 404);&lt;br&gt;
                }&lt;br&gt;
            }&lt;br&gt;
            // Check the password&lt;br&gt;
            if (! Hash::check($request-&amp;gt;password, $user-&amp;gt;password)) {&lt;br&gt;
                return Helper::jsonErrorResponse('Invalid password', 401);&lt;br&gt;
            }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        // Check if the email is verified before login is successful
        if (! $user-&amp;gt;email_verified_at) {
            return Helper::jsonErrorResponse('Email not verified. Please verify your email before logging in.', 403);
        }

        //* Generate token if email is verified
        $token = $user-&amp;gt;createToken('YourAppName')-&amp;gt;plainTextToken;

        return response()-&amp;gt;json([
            'status'     =&amp;gt; true,
            'message'    =&amp;gt; 'User logged in successfully.',
            'code'       =&amp;gt; 200,
            'token_type' =&amp;gt; 'bearer',
            'token'      =&amp;gt; $token,
            'data'       =&amp;gt; [
                'id'          =&amp;gt; $user-&amp;gt;id,
                'name'        =&amp;gt; $user-&amp;gt;name,
                'email'       =&amp;gt; $user-&amp;gt;email,
                'is_verified' =&amp;gt; $user-&amp;gt;is_verified,
            ],
        ], 200);
    } catch (Exception $e) {
        return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 500);

    }
}

public function refreshToken(): \Illuminate\Http\JsonResponse
{
    $refreshToken = auth('api')-&amp;gt;refresh();

    return response()-&amp;gt;json([
        'status'     =&amp;gt; true,
        'message'    =&amp;gt; 'Access token refreshed successfully.',
        'code'       =&amp;gt; 200,
        'token_type' =&amp;gt; 'bearer',
        'token'      =&amp;gt; $refreshToken,
        'expires_in' =&amp;gt; auth('api')-&amp;gt;factory()-&amp;gt;getTTL() * 60,
        'data'       =&amp;gt; auth('api')-&amp;gt;user()-&amp;gt;load('personalizedSickle'),
    ]);
}     
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ResetPasswordController
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

namespace App\Http\Controllers\API\Auth;

use Exception;
use Carbon\Carbon;
use App\Models\User;
use App\Mail\OtpMail;
use App\Helpers\Helper;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;

class ResetPasswordController extends Controller
{
    public function forgotPassword(Request $request): \Illuminate\Http\JsonResponse
    {
        $request-&amp;gt;validate([
            'email' =&amp;gt; 'required|email|exists:users,email'
        ]);

        try {
            $email = $request-&amp;gt;input('email');
            $otp = random_int(1000, 9999);
            $user = User::where('email', $email)-&amp;gt;first();

            if ($user) {
                Mail::to($email)-&amp;gt;send(new OtpMail($otp, $user, 'Reset Your Password'));
                $user-&amp;gt;update([
                    'otp' =&amp;gt; $otp,
                    'otp_expires_at' =&amp;gt; Carbon::now()-&amp;gt;addMinutes(60),
                ]);
                return Helper::jsonResponse(true, 'OTP Code Sent Successfully Please Check Your Email.', 200);
            } else {
                return Helper::jsonErrorResponse('Invalid Email Address', 404);
            }
        } catch (Exception $e) {
            return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 500);
        }
    }

    public function VerifyOTP(Request $request): \Illuminate\Http\JsonResponse
    {

        $request-&amp;gt;validate([
            'email' =&amp;gt; 'required|email|exists:users,email',
            'otp' =&amp;gt; 'required|digits:4',
        ]);

        try {
            $email = $request-&amp;gt;input('email');
            $otp = $request-&amp;gt;input('otp');
            $user = User::where('email', $email)-&amp;gt;first();

            if (!$user) {
                return Helper::jsonErrorResponse('User not found', 404);
            }

            if (Carbon::parse($user-&amp;gt;otp_expires_at)-&amp;gt;isPast()) {
                return Helper::jsonErrorResponse('OTP has expired.', 400);
            }

            if ($user-&amp;gt;otp !== $otp) {
                return Helper::jsonErrorResponse('Invalid OTP', 400);
            }
            $token = Str::random(60);
            $user-&amp;gt;update([
                'otp' =&amp;gt; null,
                'otp_expires_at' =&amp;gt; null,
                'reset_password_token' =&amp;gt; $token,
                'reset_password_token_expire_at' =&amp;gt; Carbon::now()-&amp;gt;addHour(),
            ]);
            return response()-&amp;gt;json([
                'status' =&amp;gt; true,
                'message' =&amp;gt; 'OTP verified successfully.',
                'code' =&amp;gt; 200,
                'token' =&amp;gt; $token,
            ]);
        } catch (Exception $e) {
            return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 500);
        }
    }

    public function ResetPassword(Request $request): \Illuminate\Http\JsonResponse
    {
        $request-&amp;gt;validate([
            'email' =&amp;gt; 'required|email|exists:users,email',
            'token' =&amp;gt; 'required|string',
            'password' =&amp;gt; 'required|string|min:6|confirmed',

        ]);

        try {
            $email = $request-&amp;gt;input('email');
            $newPassword = $request-&amp;gt;input('password');

            $user = User::where('email', $email)-&amp;gt;first();
            if (!$user) {
                return Helper::jsonErrorResponse('User not found', 404);
            }

            if (!empty($user-&amp;gt;reset_password_token) &amp;amp;&amp;amp; $user-&amp;gt;reset_password_token === $request-&amp;gt;token &amp;amp;&amp;amp; $user-&amp;gt;reset_password_token_expire_at &amp;gt;= Carbon::now()) {
                $user-&amp;gt;update([
                    'password' =&amp;gt; Hash::make($newPassword),
                    'reset_password_token' =&amp;gt; null,
                    'reset_password_token_expire_at' =&amp;gt; null,
                ]);

                return Helper::jsonResponse(true, 'Password reset successfully.', 200);
            } else {
                return Helper::jsonErrorResponse('Invalid Token', 419);
            }
        } catch (Exception $e) {
            return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 500);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Helper Functions&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Helper&lt;/code&gt; class provides:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

namespace App\Helpers;

use Exception;
use Illuminate\Support\Str;
use Kreait\Firebase\Factory;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Log;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification;
use Kreait\Firebase\Exception\MessagingException;


class Helper
{
    //! JSON Response
    public static function jsonResponse(bool $status, string $message, int $code, $data = null, bool $paginate = false, $paginateData = null): JsonResponse
    {
        $response = [
            'status'  =&amp;gt; $status,
            'message' =&amp;gt; $message,
            'code'    =&amp;gt; $code,
        ];
        if ($paginate &amp;amp;&amp;amp; !empty($paginateData)) {
            $response['data'] = $data;
            $response['pagination'] = [
                'current_page' =&amp;gt; $paginateData-&amp;gt;currentPage(),
                'last_page' =&amp;gt; $paginateData-&amp;gt;lastPage(),
                'per_page' =&amp;gt; $paginateData-&amp;gt;perPage(),
                'total' =&amp;gt; $paginateData-&amp;gt;total(),
                'first_page_url' =&amp;gt; $paginateData-&amp;gt;url(1),
                'last_page_url' =&amp;gt; $paginateData-&amp;gt;url($paginateData-&amp;gt;lastPage()),
                'next_page_url' =&amp;gt; $paginateData-&amp;gt;nextPageUrl(),
                'prev_page_url' =&amp;gt; $paginateData-&amp;gt;previousPageUrl(),
                'from' =&amp;gt; $paginateData-&amp;gt;firstItem(),
                'to' =&amp;gt; $paginateData-&amp;gt;lastItem(),
                'path' =&amp;gt; $paginateData-&amp;gt;path(),
            ];
        } elseif ($paginate &amp;amp;&amp;amp; !empty($data)) {
            $response['data'] = $data-&amp;gt;items();
            $response['pagination'] = [
                'current_page' =&amp;gt; $data-&amp;gt;currentPage(),
                'last_page' =&amp;gt; $data-&amp;gt;lastPage(),
                'per_page' =&amp;gt; $data-&amp;gt;perPage(),
                'total' =&amp;gt; $data-&amp;gt;total(),
                'first_page_url' =&amp;gt; $data-&amp;gt;url(1),
                'last_page_url' =&amp;gt; $data-&amp;gt;url($data-&amp;gt;lastPage()),
                'next_page_url' =&amp;gt; $data-&amp;gt;nextPageUrl(),
                'prev_page_url' =&amp;gt; $data-&amp;gt;previousPageUrl(),
                'from' =&amp;gt; $data-&amp;gt;firstItem(),
                'to' =&amp;gt; $data-&amp;gt;lastItem(),
                'path' =&amp;gt; $data-&amp;gt;path(),
            ];
        } elseif ($data !== null) {
            $response['data'] = $data;
        }

        return response()-&amp;gt;json($response, $code);
    }

    public static function jsonErrorResponse(string $message, int $code = 400, array $errors = []): JsonResponse
    {
        $response = [
            'status'  =&amp;gt; false,
            'message' =&amp;gt; $message,
            'code'    =&amp;gt; $code,
            'errors'  =&amp;gt; $errors,
        ];
        return response()-&amp;gt;json($response, $code);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note if your need custom file upload function than just adding rhis code in &lt;code&gt;Helper.php&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; //! File or Image Upload
    public static function fileUpload($file, string $folder, string $name): ?string
    {
        if (!$file-&amp;gt;isValid()) {
            return null;
        }

        $imageName = Str::slug($name) . '.' . $file-&amp;gt;extension();
        $path      = public_path('uploads/' . $folder);
        if (!file_exists($path)) {
            if (!mkdir($path, 0755, true) &amp;amp;&amp;amp; !is_dir($path)) {
                throw new \RuntimeException(sprintf('Directory "%s" was not created', $path));
            }
        }
        $file-&amp;gt;move($path, $imageName);
        return 'uploads/' . $folder . '/' . $imageName;
    }

    //! File or Image Delete
    public static function fileDelete(string $path): void
    {
        if (file_exists($path)) {
            unlink($path);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;The bootstrap/app.php is configured to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

use App\Helpers\Helper;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Request;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Database\Eloquent\ModelNotFoundException;

return Application::configure(basePath: dirname(__DIR__))
    -&amp;gt;withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    -&amp;gt;withMiddleware(function (Middleware $middleware) {
        //
    })
     -&amp;gt;withExceptions(function (Exceptions $exceptions) {
        $exceptions-&amp;gt;render(function (Throwable $e, Request $request) {
            if ($request-&amp;gt;is('api/*')) {
                if ($e instanceof ValidationException) {
                    return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 422, $e-&amp;gt;errors());
                }
                if ($e instanceof ModelNotFoundException) {
                    return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 404);
                }

                if ($e instanceof AuthenticationException) {
                    return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 401);
                }
                if ($e instanceof AuthorizationException) {
                    return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), 403);
                }
                // Dynamically determine the status code if available
                $statusCode = method_exists($e, 'getStatusCode') ? $e-&amp;gt;getStatusCode() : 500;
                return Helper::jsonErrorResponse($e-&amp;gt;getMessage(), $statusCode);
            }
            return null;
        });
    })-&amp;gt;create();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This implementation provides a solid foundation for a secure Laravel API with comprehensive authentication features.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a Real-Time Chat Application in Laravel with Wirechat</title>
      <dc:creator>AL Mamon</dc:creator>
      <pubDate>Wed, 07 May 2025 10:07:37 +0000</pubDate>
      <link>https://dev.to/mamondev193/building-a-real-time-chat-application-in-laravel-with-wirechat-3e5h</link>
      <guid>https://dev.to/mamondev193/building-a-real-time-chat-application-in-laravel-with-wirechat-3e5h</guid>
      <description>&lt;p&gt;Why Choose Wirechat?&lt;br&gt;
Wirechat simplifies real-time chat implementation in Laravel applications by providing&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seamless Livewire integration&lt;/li&gt;
&lt;li&gt;Built-in WebSocket support&lt;/li&gt;
&lt;li&gt;Polymorphic chat relationships&lt;/li&gt;
&lt;li&gt;Developer-friendly customization&lt;/li&gt;
&lt;li&gt;Production-ready components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Installation Guide&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Project Setup
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer create-project laravel/laravel your-project
cd your-project

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Authentication Setup (Recommended)
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require laravel/breeze --dev
php artisan breeze:install
npm install &amp;amp;&amp;amp; npm run dev
php artisan migrate

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Package Installation
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;composer require namu/wirechat
php artisan wirechat:install
php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To ensure Tailwind purges CSS classes from the package, add the following lines to your /resources/css/app.css file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* resources/css/app.css */

@source '../../vendor/namu/wirechat/resources/views/**/*.blade.php';
@source '../../vendor/namu/wirechat/src/Livewire/**/*.php';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: This package requires the &lt;a class="mentioned-user" href="https://dev.to/tailwindcss"&gt;@tailwindcss&lt;/a&gt;/forms plugin. Make sure it is installed and included in your Tailwind config.&lt;/p&gt;

&lt;p&gt;WebSocket Setup&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan install:broadcasting
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Follow prompts to install Laravel Reverb&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan reverb:start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Queue Worker&lt;br&gt;
For optimal performance, prioritize message processing:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan queue:work --queue=messages,default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Development Workflow&lt;br&gt;
 Terminal 1: Start Laravel server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terminal 2: Start WebSocket server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan reverb:start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terminal 3: Start queue worker&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan queue:work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Terminal 4: Start Vite dev server&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementation Guide&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Make Models Chatable
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php
use Namu\WireChat\Traits\Chatable;

class User extends Authenticatable
{
    use Chatable;

    // Your existing model code
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Starting Conversations
Programmatically create chats:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;php&lt;br&gt;
// One-to-one chat&lt;br&gt;
$user-&amp;gt;startConversationWith($recipient);&lt;/p&gt;

&lt;p&gt;// Group chat&lt;br&gt;
$user-&amp;gt;createGroupChat([$participant1, $participant2], 'Group Name');&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Customizing Components
Publish views for customization:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan vendor:publish --tag=wirechat-views

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Extending Functionality
Create a service provider to extend Wirechat:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Namu\WireChat\WireChat;

class ChatServiceProvider extends ServiceProvider
{
    public function boot()
    {
        WireChat::messageReceived(function ($message) {
            // Custom logic when message is received
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Wirechat provides a robust foundation for Laravel chat applications while maintaining flexibility for customization. By following this guide, you can implement a production-ready chat system quickly while having the option to extend functionality as your application grows.&lt;/p&gt;

&lt;p&gt;For further assistance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the package documentation&lt;/li&gt;
&lt;li&gt;Review Laravel broadcasting and Livewire documentation&lt;/li&gt;
&lt;li&gt;Explore the package source code for implementation examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! &lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
