{"id":481,"date":"2025-04-20T08:04:07","date_gmt":"2025-04-20T08:04:07","guid":{"rendered":"https:\/\/imcodinggenius.com\/?p=481"},"modified":"2025-04-20T08:04:07","modified_gmt":"2025-04-20T08:04:07","slug":"otp-authentication-in-laravel-vue-js-for-secure-transactions","status":"publish","type":"post","link":"https:\/\/imcodinggenius.com\/?p=481","title":{"rendered":"OTP Authentication in Laravel &amp; Vue.js for Secure Transactions"},"content":{"rendered":"<h2>Introduction<\/h2>\n<p>In today\u2019s digital world, security is paramount, especially when dealing with sensitive data like user authentication and financial transactions. One of the most effective ways to enhance security is by implementing One-Time Password (OTP) authentication. This article explores how to implement OTP authentication in a Laravel backend with a Vue.js frontend, ensuring secure transactions.<\/p>\n<h2>Why Use OTP Authentication?<\/h2>\n<p>OTP authentication provides an extra layer of security beyond traditional username and password authentication. Some key benefits include:<\/p>\n<p><strong>Prevention of Unauthorized Access:<\/strong> Even if login credentials are compromised, an attacker cannot log in without the OTP.<br \/>\n<strong>Enhanced Security for Transactions:<\/strong> OTPs can be used to confirm high-value transactions, preventing fraud.<br \/>\n<strong>Temporary Validity:<\/strong> Since OTPs expire after a short period, they reduce the risk of reuse by attackers.<\/p>\n<h2>Prerequisites<\/h2>\n<p>Before getting started, ensure you have the following:<\/p>\n<p><a target=\"_blank\" href=\"https:\/\/laravel.com\/docs\/10.x\/installation\" rel=\"noopener\">Laravel<\/a> 8 or later installed<br \/>\nVue.js configured in your project<br \/>\nA mail or SMS service provider for sending OTPs (e.g., Twilio, Mailtrap)<br \/>\nBasic understanding of Laravel and Vue.js<\/p>\n<p>In this guide, we\u2019ll implement OTP authentication in a <strong>Laravel (backend) and Vue.js (frontend) application<\/strong>. We\u2019ll cover:<\/p>\n<p>Setting up Laravel and Vue (frontend) from scratch<br \/>\nSetting up OTP generation and validation in Laravel<br \/>\nCreating a Vue.js component for OTP input<br \/>\nIntegrating OTP authentication into login workflows<br \/>\nEnhancing security with best practices<\/p>\n<p>By the end, you\u2019ll have a fully functional OTP authentication system ready to enhance the security of your fintech or web application.<\/p>\n<h2>Setting Up Laravel for OTP Authentication<\/h2>\n<h3>Step 1: Install Laravel and Required Packages<\/h3>\n<p>If you haven&#8217;t already set up a Laravel project, create a new one:<\/p>\n<p>composer create-project <span class=\"hljs-string\">&#171;laravel\/laravel:^10.0&#187;<\/span> example-app<\/p>\n<p>Next, install the <a target=\"_blank\" href=\"https:\/\/laravel.com\/docs\/10.x\/starter-kits\" rel=\"noopener\">Laravel Breeze package<\/a> for frontend scaffolding:<\/p>\n<p>composer <span class=\"hljs-keyword\">require<\/span> laravel\/breeze &#8212;dev<\/p>\n<p>After composer has finished installing, run the following command to select the framework you want to use\u2014the Vue configuration:<\/p>\n<p>php artisan breeze:install<\/p>\n<p>You\u2019ll see a prompt with the available stacks:<\/p>\n<p>Which Breeze stack would you like to install?<br \/>\n&#8212; Vue with Inertia<br \/>\nWould you like any optional features?<br \/>\n&#8212; None<br \/>\nWhich testing framework do you prefer?<br \/>\n&#8212; PHPUnit<\/p>\n<p>Breeze will automatically install the necessary packages for your Laravel Vue project. You should see:<\/p>\n<p>INFO Breeze scaffolding installed successfully.<\/p>\n<p>Now run the npm command to build your frontend assets:<\/p>\n<p>npm run dev<\/p>\n<p>Then, open another terminal and launch your Laravel app:<\/p>\n<p>php artisan serve<\/p>\n<h3>Step 2: Setting up OTP generation and validation in Laravel<\/h3>\n<p>We&#8217;ll use a mail testing platform called Mailtrap to send and receive mail locally. If you don\u2019t have a mail testing service set up, sign up at <a target=\"_blank\" href=\"https:\/\/mailtrap.io\/\" rel=\"noopener\">Mailtrap<\/a> to get your SMTP credentials and add them to your .env file:<\/p>\n<p>MAIL_MAILER=smtp<br \/>\nMAIL_HOST=sandbox.smtp.mailtrap.io<br \/>\nMAIL_PORT=2525<br \/>\nMAIL_USERNAME=1780944422200a<br \/>\nMAIL_PASSWORD=a8250ee453323b<br \/>\nMAIL_ENCRYPTION=tls<br \/>\nMAIL_FROM_ADDRESS=hello@example.com<br \/>\nMAIL_FROM_NAME=&#187;${APP_NAME}&#187;<\/p>\n<p>To send OTPs to users, we\u2019ll use Laravel\u2019s built-in mail services. Create a mail class and controller:<\/p>\n<p>php artisan make:mail OtpMail<br \/>\nphp artisan make:controller OtpController<\/p>\n<p>Then modify the OtpMail class:<\/p>\n<p><span class=\"hljs-meta\">&lt;?php<\/span><\/p>\n<p><span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">App<\/span><span class=\"hljs-title\">Mail<\/span>;<\/p>\n<p><span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Bus<\/span><span class=\"hljs-title\">Queueable<\/span>;<br \/>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Contracts<\/span><span class=\"hljs-title\">Queue<\/span><span class=\"hljs-title\">ShouldQueue<\/span>;<br \/>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Mail<\/span><span class=\"hljs-title\">Mailable<\/span>;<br \/>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Mail<\/span><span class=\"hljs-title\">Mailables<\/span><span class=\"hljs-title\">Content<\/span>;<br \/>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Mail<\/span><span class=\"hljs-title\">Mailables<\/span><span class=\"hljs-title\">Envelope<\/span>;<br \/>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span><span class=\"hljs-title\">Queue<\/span><span class=\"hljs-title\">SerializesModels<\/span>;<\/p>\n<p><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">OtpMail<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">Mailable<\/span><br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Queueable<\/span>, <span class=\"hljs-title\">SerializesModels<\/span>;<\/p>\n<p>    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-variable\">$otp<\/span>;<\/p>\n<p>    <span class=\"hljs-comment\">\/**<br \/>\n     * Create a new message instance.<br \/>\n     *\/<\/span><br \/>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">__construct<\/span>(<span class=\"hljs-params\"><span class=\"hljs-variable\">$otp<\/span><\/span>)<br \/>\n    <\/span>{<br \/>\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;otp = <span class=\"hljs-variable\">$otp<\/span>;<br \/>\n    }<\/p>\n<p>    <span class=\"hljs-comment\">\/**<br \/>\n     * Build the email message.<br \/>\n     *\/<\/span><br \/>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">build<\/span>(<span class=\"hljs-params\"><\/span>)<br \/>\n    <\/span>{<br \/>\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">$this<\/span>-&gt;subject(<span class=\"hljs-string\">&#8216;Your OTP Code&#8217;<\/span>)<br \/>\n            -&gt;view(<span class=\"hljs-string\">&#8217;emails.otp&#8217;<\/span>)<br \/>\n            -&gt;with([<span class=\"hljs-string\">&#8216;otp&#8217;<\/span> =&gt; <span class=\"hljs-keyword\">$this<\/span>-&gt;otp]);<br \/>\n    }<\/p>\n<p>    <span class=\"hljs-comment\">\/**<br \/>\n     * Get the message envelope.<br \/>\n     *\/<\/span><br \/>\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">envelope<\/span>(<span class=\"hljs-params\"><\/span>): <span class=\"hljs-title\">Envelope<\/span><br \/>\n    <\/span>{<br \/>\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> Envelope(<br \/>\n            subject: <span class=\"hljs-string\">&#8216;OTP Mail&#8217;<\/span>,<br \/>\n        );<br \/>\n    }<br \/>\n}<\/p>\n<p>Create a Blade view in resources\/views\/emails\/otp.blade.php:<\/p>\n<p><span class=\"hljs-meta\">&lt;!DOCTYPE <span class=\"hljs-meta-keyword\">html<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">html<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">head<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Your OTP Code<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">head<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Hello,<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Your One-Time Password (OTP) is: <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>{{ $otp }}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>This code is valid for 10 minutes. Do not share it with anyone.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Thank you!<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">html<\/span>&gt;<\/span><\/p>\n<h3>Step 3: Creating a Vue.js component for OTP input<\/h3>\n<p>Normally, after login or registration, users are redirected to the dashboard. In this tutorial, we add an extra security step that validates users with an OTP before granting dashboard access.<\/p>\n<p>Create two Vue files:<\/p>\n<p>Request.vue: requests the OTP<br \/>\nVerify.vue: inputs the OTP for verification<\/p>\n<p>Now we create the routes for the purpose of return the View and the functionality of creating OTP codes, storing OTP codes, sending OTP codes through the mail class, we head to our web.php file:<\/p>\n<p>Route::middleware(<span class=\"hljs-string\">&#8216;auth&#8217;<\/span>)-&gt;group(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\"><\/span>) <\/span>{<br \/>\n    Route::get(<span class=\"hljs-string\">&#8216;\/request&#8217;<\/span>, [OtpController::class, <span class=\"hljs-string\">&#8216;create&#8217;<\/span>])-&gt;name(<span class=\"hljs-string\">&#8216;request&#8217;<\/span>);<br \/>\n    Route::post(<span class=\"hljs-string\">&#8216;\/store-request&#8217;<\/span>, [OtpController::class, <span class=\"hljs-string\">&#8216;store&#8217;<\/span>])-&gt;name(<span class=\"hljs-string\">&#8216;send.otp.request&#8217;<\/span>);<\/p>\n<p>    Route::get(<span class=\"hljs-string\">&#8216;\/verify&#8217;<\/span>, [OtpController::class, <span class=\"hljs-string\">&#8216;verify&#8217;<\/span>])-&gt;name(<span class=\"hljs-string\">&#8216;verify&#8217;<\/span>);<br \/>\n    Route::post(<span class=\"hljs-string\">&#8216;\/verify-request&#8217;<\/span>, [OtpController::class, <span class=\"hljs-string\">&#8216;verify_request&#8217;<\/span>])-&gt;name(<span class=\"hljs-string\">&#8216;verify.otp.request&#8217;<\/span>);<br \/>\n});<\/p>\n<p>Putting all of this code in the OTP controller returns the View for our request.vue and verify.vue file and the functionality of creating OTP codes, storing OTP codes, sending OTP codes through the mail class and verifying OTP codes, we head to our web.php file to set up the routes.<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">create<\/span>(<span class=\"hljs-params\">Request <span class=\"hljs-variable\">$request<\/span><\/span>)<br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-keyword\">return<\/span> Inertia::render(<span class=\"hljs-string\">&#8216;Request&#8217;<\/span>, [<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-variable\">$request<\/span>-&gt;query(<span class=\"hljs-string\">&#8217;email&#8217;<\/span>, <span class=\"hljs-string\">&#187;<\/span>),<br \/>\n    ]);<br \/>\n}<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">store<\/span>(<span class=\"hljs-params\">Request <span class=\"hljs-variable\">$request<\/span><\/span>)<br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-variable\">$request<\/span>-&gt;validate([<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;required|email|exists:users,email&#8217;<\/span>,<br \/>\n    ]);<\/p>\n<p>    <span class=\"hljs-variable\">$otp<\/span> = rand(<span class=\"hljs-number\">100000<\/span>, <span class=\"hljs-number\">999999<\/span>);<\/p>\n<p>    Cache::put(<span class=\"hljs-string\">&#8216;otp_&#8217;<\/span> . <span class=\"hljs-variable\">$request<\/span>-&gt;email, <span class=\"hljs-variable\">$otp<\/span>, now()-&gt;addMinutes(<span class=\"hljs-number\">10<\/span>));<\/p>\n<p>    Log::info(<span class=\"hljs-string\">&#171;OTP generated for &#171;<\/span> . <span class=\"hljs-variable\">$request<\/span>-&gt;email . <span class=\"hljs-string\">&#171;: &#171;<\/span> . <span class=\"hljs-variable\">$otp<\/span>);<\/p>\n<p>    Mail::to(<span class=\"hljs-variable\">$request<\/span>-&gt;email)-&gt;send(<span class=\"hljs-keyword\">new<\/span> OtpMail(<span class=\"hljs-variable\">$otp<\/span>));<\/p>\n<p>    <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;route(<span class=\"hljs-string\">&#8216;verify&#8217;<\/span>, [<span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-variable\">$request<\/span>-&gt;email]);<br \/>\n}<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">verify<\/span>(<span class=\"hljs-params\">Request <span class=\"hljs-variable\">$request<\/span><\/span>)<br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-keyword\">return<\/span> Inertia::render(<span class=\"hljs-string\">&#8216;Verify&#8217;<\/span>, [<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-variable\">$request<\/span>-&gt;query(<span class=\"hljs-string\">&#8217;email&#8217;<\/span>),<br \/>\n    ]);<br \/>\n}<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">verify_request<\/span>(<span class=\"hljs-params\">Request <span class=\"hljs-variable\">$request<\/span><\/span>)<br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-variable\">$request<\/span>-&gt;validate([<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;required|email|exists:users,email&#8217;<\/span>,<br \/>\n        <span class=\"hljs-string\">&#8216;otp&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;required|digits:6&#8217;<\/span>,<br \/>\n    ]);<\/p>\n<p>    <span class=\"hljs-variable\">$cachedOtp<\/span> = Cache::get(<span class=\"hljs-string\">&#8216;otp_&#8217;<\/span> . <span class=\"hljs-variable\">$request<\/span>-&gt;email);<\/p>\n<p>    Log::info(<span class=\"hljs-string\">&#171;OTP entered: &#171;<\/span> . <span class=\"hljs-variable\">$request<\/span>-&gt;otp);<br \/>\n    Log::info(<span class=\"hljs-string\">&#171;OTP stored in cache: &#171;<\/span> . (<span class=\"hljs-variable\">$cachedOtp<\/span> ?? <span class=\"hljs-string\">&#8216;No OTP found&#8217;<\/span>));<\/p>\n<p>    <span class=\"hljs-keyword\">if<\/span> (!<span class=\"hljs-variable\">$cachedOtp<\/span>) {<br \/>\n        <span class=\"hljs-keyword\">return<\/span> back()-&gt;withErrors([<span class=\"hljs-string\">&#8216;otp&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;OTP has expired. Please request a new one.&#8217;<\/span>]);<br \/>\n    }<\/p>\n<p>    <span class=\"hljs-keyword\">if<\/span> ((<span class=\"hljs-keyword\">string<\/span>) <span class=\"hljs-variable\">$cachedOtp<\/span> !== (<span class=\"hljs-keyword\">string<\/span>) <span class=\"hljs-variable\">$request<\/span>-&gt;otp) {<br \/>\n        <span class=\"hljs-keyword\">return<\/span> back()-&gt;withErrors([<span class=\"hljs-string\">&#8216;otp&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;Invalid OTP. Please try again.&#8217;<\/span>]);<br \/>\n    }<\/p>\n<p>    Cache::forget(<span class=\"hljs-string\">&#8216;otp_&#8217;<\/span> . <span class=\"hljs-variable\">$request<\/span>-&gt;email);<\/p>\n<p>    <span class=\"hljs-variable\">$user<\/span> = User::where(<span class=\"hljs-string\">&#8217;email&#8217;<\/span>, <span class=\"hljs-variable\">$request<\/span>-&gt;email)-&gt;first();<br \/>\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-variable\">$user<\/span>) {<br \/>\n        <span class=\"hljs-variable\">$user<\/span>-&gt;email_verified_at = now();<br \/>\n        <span class=\"hljs-variable\">$user<\/span>-&gt;save();<br \/>\n    }<\/p>\n<p>    <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;route(<span class=\"hljs-string\">&#8216;dashboard&#8217;<\/span>)-&gt;with(<span class=\"hljs-string\">&#8216;success&#8217;<\/span>, <span class=\"hljs-string\">&#8216;OTP Verified Successfully!&#8217;<\/span>);<br \/>\n}<\/p>\n<p>Having set all this code, we return to the request.vue file to set it up.<\/p>\n<p>&lt;script setup&gt;<br \/>\n<span class=\"hljs-keyword\">import<\/span> AuthenticatedLayout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Layouts\/AuthenticatedLayout.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> InputError <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/InputError.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> InputLabel <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/InputLabel.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> PrimaryButton <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/PrimaryButton.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> TextInput <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/TextInput.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { Head, useForm } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@inertiajs\/vue3&#8217;<\/span>;<\/p>\n<p><span class=\"hljs-keyword\">const<\/span> props = defineProps({<br \/>\n    <span class=\"hljs-attr\">email<\/span>: {<br \/>\n        <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-built_in\">String<\/span>,<br \/>\n        <span class=\"hljs-attr\">required<\/span>: <span class=\"hljs-literal\">true<\/span>,<br \/>\n    },<br \/>\n});<\/p>\n<p><span class=\"hljs-keyword\">const<\/span> form = useForm({<br \/>\n    <span class=\"hljs-attr\">email<\/span>: props.email,<br \/>\n});<\/p>\n<p><span class=\"hljs-keyword\">const<\/span> submit = <span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n    form.post(route(<span class=\"hljs-string\">&#8216;send.otp.request&#8217;<\/span>), {<br \/>\n        <span class=\"hljs-attr\">onSuccess<\/span>: <span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n            alert(<span class=\"hljs-string\">&#171;OTP has been sent to your email!&#187;<\/span>);<br \/>\n            form.get(route(<span class=\"hljs-string\">&#8216;verify&#8217;<\/span>), { <span class=\"hljs-attr\">email<\/span>: form.email }); <span class=\"hljs-comment\">\/\/ Redirecting to OTP verification<\/span><br \/>\n        },<br \/>\n    });<br \/>\n};<br \/>\n&lt;\/script&gt;<\/p>\n<p><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">&#171;Request OTP&#187;<\/span> \/&gt;<\/span><\/span><\/p>\n<p>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">AuthenticatedLayout<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> @<span class=\"hljs-attr\">submit.prevent<\/span>=<span class=\"hljs-string\">&#171;submit&#187;<\/span>&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InputLabel<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">&#171;email&#187;<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">&#171;Email&#187;<\/span> \/&gt;<\/span><\/p>\n<p>                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TextInput<\/span><br \/>\n                    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">&#171;email&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">&#171;email&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-1 block w-full&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">v-model<\/span>=<span class=\"hljs-string\">&#171;form.email&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">required<\/span><br \/>\n                    <span class=\"hljs-attr\">autofocus<\/span><br \/>\n                \/&gt;<\/span><\/p>\n<p>                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InputError<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-2&#187;<\/span> <span class=\"hljs-attr\">:message<\/span>=<span class=\"hljs-string\">&#171;form.errors.email&#187;<\/span> \/&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/p>\n<p>            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-4 flex items-center justify-end&#187;<\/span>&gt;<\/span><br \/>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PrimaryButton<\/span> <span class=\"hljs-attr\">:class<\/span>=<span class=\"hljs-string\">&#171;{ &#8216;opacity-25&#8217;: form.processing }&#187;<\/span> <span class=\"hljs-attr\">:disabled<\/span>=<span class=\"hljs-string\">&#171;form.processing&#187;<\/span>&gt;<\/span><br \/>\n                    Request OTP<br \/>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">PrimaryButton<\/span>&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">AuthenticatedLayout<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span><\/p>\n<p>Having set all this code, we return to the verify.vue to set it up:<\/p>\n<p>&lt;script setup&gt;<br \/>\n<span class=\"hljs-keyword\">import<\/span> AuthenticatedLayout <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Layouts\/AuthenticatedLayout.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> InputError <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/InputError.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> InputLabel <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/InputLabel.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> PrimaryButton <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/PrimaryButton.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> TextInput <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@\/Components\/TextInput.vue&#8217;<\/span>;<br \/>\n<span class=\"hljs-keyword\">import<\/span> { Head, useForm, usePage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">&#8216;@inertiajs\/vue3&#8217;<\/span>;<\/p>\n<p><span class=\"hljs-keyword\">const<\/span> page = usePage();<br \/>\n<span class=\"hljs-comment\">\/\/ Get the email from the URL query params<\/span><br \/>\n<span class=\"hljs-keyword\">const<\/span> email = page.props.email || <span class=\"hljs-string\">&#187;<\/span>;<\/p>\n<p><span class=\"hljs-comment\">\/\/ Initialize form with email and OTP field<\/span><br \/>\n<span class=\"hljs-keyword\">const<\/span> form = useForm({<br \/>\n    <span class=\"hljs-attr\">email<\/span>: email,<br \/>\n    <span class=\"hljs-attr\">otp<\/span>: <span class=\"hljs-string\">&#187;<\/span>,<br \/>\n});<\/p>\n<p><span class=\"hljs-comment\">\/\/ Submit function<\/span><br \/>\n<span class=\"hljs-keyword\">const<\/span> submit = <span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n    form.post(route(<span class=\"hljs-string\">&#8216;verify.otp.request&#8217;<\/span>), {<br \/>\n        <span class=\"hljs-attr\">onSuccess<\/span>: <span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n            alert(<span class=\"hljs-string\">&#171;OTP verified successfully! Redirecting&#8230;&#187;<\/span>);<br \/>\n            <span class=\"hljs-built_in\">window<\/span>.location.href = <span class=\"hljs-string\">&#8216;\/dashboard&#8217;<\/span>; <span class=\"hljs-comment\">\/\/ Change to your desired redirect page<\/span><br \/>\n        },<br \/>\n        <span class=\"hljs-attr\">onError<\/span>: <span class=\"hljs-function\">() =&gt;<\/span> {<br \/>\n            alert(<span class=\"hljs-string\">&#171;Invalid OTP. Please try again.&#187;<\/span>);<br \/>\n        },<br \/>\n    });<br \/>\n};<br \/>\n&lt;\/script&gt;<\/p>\n<p><span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">template<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span> <span class=\"hljs-attr\">title<\/span>=<span class=\"hljs-string\">&#171;Verify OTP&#187;<\/span> \/&gt;<\/span><\/span><\/p>\n<p>    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">AuthenticatedLayout<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> @<span class=\"hljs-attr\">submit.prevent<\/span>=<span class=\"hljs-string\">&#171;submit&#187;<\/span>&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InputLabel<\/span> <span class=\"hljs-attr\">for<\/span>=<span class=\"hljs-string\">&#171;otp&#187;<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">&#171;Enter OTP&#187;<\/span> \/&gt;<\/span><\/p>\n<p>                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TextInput<\/span><br \/>\n                    <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">&#171;otp&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">&#171;text&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-1 block w-full&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">v-model<\/span>=<span class=\"hljs-string\">&#171;form.otp&#187;<\/span><br \/>\n                    <span class=\"hljs-attr\">required<\/span><br \/>\n                \/&gt;<\/span><\/p>\n<p>                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">InputError<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-2&#187;<\/span> <span class=\"hljs-attr\">:message<\/span>=<span class=\"hljs-string\">&#171;form.errors.otp&#187;<\/span> \/&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/p>\n<p>            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">&#171;mt-4 flex items-center justify-end&#187;<\/span>&gt;<\/span><br \/>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PrimaryButton<\/span> <span class=\"hljs-attr\">:disabled<\/span>=<span class=\"hljs-string\">&#171;form.processing&#187;<\/span>&gt;<\/span><br \/>\n                    Verify OTP<br \/>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">PrimaryButton<\/span>&gt;<\/span><br \/>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><br \/>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span><br \/>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">AuthenticatedLayout<\/span>&gt;<\/span><br \/>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">template<\/span>&gt;<\/span><\/p>\n<h3>Step 4: Integrating OTP authentication into login and register workflows<\/h3>\n<p>Update the login controller:<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">store<\/span>(<span class=\"hljs-params\">LoginRequest <span class=\"hljs-variable\">$request<\/span><\/span>): <span class=\"hljs-title\">RedirectResponse<\/span><br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-variable\">$request<\/span>-&gt;authenticate();<\/p>\n<p>    <span class=\"hljs-variable\">$request<\/span>-&gt;session()-&gt;regenerate();<\/p>\n<p>    <span class=\"hljs-keyword\">return<\/span> redirect()-&gt;intended(route(<span class=\"hljs-string\">&#8216;request&#8217;<\/span>, absolute: <span class=\"hljs-literal\">false<\/span>));<br \/>\n}<\/p>\n<p>Update the registration controller:<\/p>\n<p><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">store<\/span>(<span class=\"hljs-params\">Request <span class=\"hljs-variable\">$request<\/span><\/span>): <span class=\"hljs-title\">RedirectResponse<\/span><br \/>\n<\/span>{<br \/>\n    <span class=\"hljs-variable\">$request<\/span>-&gt;validate([<br \/>\n        <span class=\"hljs-string\">&#8216;name&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;required|string|max:255&#8217;<\/span>,<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-string\">&#8216;required|string|lowercase|email|max:255|unique:&#8217;<\/span> . User::class,<br \/>\n        <span class=\"hljs-string\">&#8216;password&#8217;<\/span> =&gt; [<span class=\"hljs-string\">&#8216;required&#8217;<\/span>, <span class=\"hljs-string\">&#8216;confirmed&#8217;<\/span>, RulesPassword::defaults()],<br \/>\n    ]);<\/p>\n<p>    <span class=\"hljs-variable\">$user<\/span> = User::create([<br \/>\n        <span class=\"hljs-string\">&#8216;name&#8217;<\/span> =&gt; <span class=\"hljs-variable\">$request<\/span>-&gt;name,<br \/>\n        <span class=\"hljs-string\">&#8217;email&#8217;<\/span> =&gt; <span class=\"hljs-variable\">$request<\/span>-&gt;email,<br \/>\n        <span class=\"hljs-string\">&#8216;password&#8217;<\/span> =&gt; Hash::make(<span class=\"hljs-variable\">$request<\/span>-&gt;password),<br \/>\n    ]);<\/p>\n<p>    event(<span class=\"hljs-keyword\">new<\/span> Registered(<span class=\"hljs-variable\">$user<\/span>));<\/p>\n<p>    Auth::login(<span class=\"hljs-variable\">$user<\/span>);<\/p>\n<p>    <span class=\"hljs-keyword\">return<\/span> redirect(route(<span class=\"hljs-string\">&#8216;request&#8217;<\/span>, absolute: <span class=\"hljs-literal\">false<\/span>));<br \/>\n}<\/p>\n<h2>Conclusion<\/h2>\n<p>Implementing OTP authentication in Laravel and Vue.js enhances security for user logins and transactions. By generating, sending, and verifying OTPs, we can add an extra layer of protection against unauthorized access. This method is particularly useful for financial applications and sensitive user data.<\/p>","protected":false},"excerpt":{"rendered":"<p>Introduction In today\u2019s digital world, security is paramount, especially when dealing with sensitive data like user authentication and financial transactions. One of the most effective ways to enhance security is by implementing One-Time Password (OTP) authentication. This article explores how to implement OTP authentication in a Laravel backend with a &#8230; <\/p>\n<div><a class=\"more-link bs-book_btn\" href=\"https:\/\/imcodinggenius.com\/?p=481\">Read More<\/a><\/div>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-481","post","type-post","status-publish","format-standard","hentry","category-news"],"_links":{"self":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts\/481"}],"collection":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=481"}],"version-history":[{"count":0,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts\/481\/revisions"}],"wp:attachment":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=481"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=481"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=481"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}