Laravel Registration OTP Verification
March 9, 2025 Β· AI Conversations
<script>
import { ref,shallowRef,inject } from "vue";
import { useForm, useField } from 'vee-validate'
import SingleColumn from '@/layouts/SingleColumn.vue';
const apiUrl = inject('apiUrl');
console.log(apiUrl);
const step = ref(1);
const items = [
'Sign Up',
'Otp',
'Login',
]
const otp = shallowRef('')
const { handleSubmit, handleReset, values } = useForm({
validationSchema: {
name (value) {
if (value?.length >= 2) return true
return 'Name needs to be at least 2 characters.'
},
email (value) {
if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/i.test(value)) return true
return 'Must be a valid e-mail.'
},
password (value) {
if (value?.length >= 6) return true
return 'Password needs to be at least 6 characters.'
},
confirmPassword (value) {
if (value === values.password) return true;
return 'Passwords do not match.';
}
},
})
const modelValue = ref(1);
const name = useField('name')
const email = useField('email')
const password = useField('password')
const confirmPassword = useField('confirmPassword');
const submit = handleSubmit(values => {
alert(JSON.stringify(values, null, 2))
modelValue.value = 2;
})
const user = ref({
username: "",
email: "",
password: "",
});
const loading = ref(false);
const message = ref("");
const showPassword = ref(false);
const createUser = async () => {
loading.value = true;
const apiUrl = "https://autohauslange.carmar.digital/wp-json/wp/v2/users";
const adminUsername = "plstr-admin";
const applicationPassword = "MrYNs0r68Hb7ywed01JmiX7d";
// Encode credentials for Basic Auth
const authHeader = `Basic ${btoa(`${adminUsername}:${applicationPassword}`)}`;
try {
const response = await fetch(apiUrl, {
method: "POST",
headers: {
"Authorization": authHeader,
"Content-Type": "application/json",
},
body: JSON.stringify(user.value),
});
const result = await response.json();
if (response.ok) {
message.value = `User ${result.username} created successfully!`;
} else {
message.value = `Error: ${result.message}`;
}
} catch (error) {
message.value = `Request failed: ${error.message}`;
loading.value = false;
} finally {
loading.value = false;
}
};
</script>
Register
Sigh UP
submit
clear
Verify Your Account
We sent a verification code to {{ email.value.value ? email.value.value : 'test@gmail.com' }}
Please check your email and paste the code below.
Didn't receive the code? Resend
add laravel register and email otp verification
—
### **Steps to Implement Laravel Register & OTP Verification**
1. **Setup Laravel API Endpoints:**
– User registration
– Send OTP via email
– Verify OTP
2. **Modify Vue Component:**
– Call Laravel API for registration
– Handle OTP verification
—
### **1. Laravel Backend Setup**
#### **(a) Create Authentication Routes**
In `routes/api.php`, define routes for registration, sending OTP, and verification.
use AppHttpControllersAuthController;
use IlluminateSupportFacadesRoute;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/send-otp', [AuthController::class, 'sendOtp']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
#### **(b) Create `AuthController`**
Run:
php artisan make:controller AuthController
Edit `app/Http/Controllers/AuthController.php`:
namespace AppHttpControllers;
use AppModelsUser;
use IlluminateHttpRequest;
use IlluminateSupportFacadesHash;
use IlluminateSupportFacadesMail;
use IlluminateSupportFacadesValidator;
use IlluminateSupportFacadesCache;
class AuthController extends Controller
{
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:2',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|confirmed',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return response()->json([
'message' => 'User registered successfully! Please verify your email.',
'user' => $user
]);
}
public function sendOtp(Request $request)
{
$request->validate(['email' => 'required|email|exists:users,email']);
$otp = rand(100000, 999999);
Cache::put("otp_{$request->email}", $otp, now()->addMinutes(10));
Mail::raw("Your OTP is: {$otp}", function ($message) use ($request) {
$message->to($request->email)
->subject('Your OTP Code');
});
return response()->json(['message' => 'OTP sent successfully!']);
}
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email|exists:users,email',
'otp' => 'required|digits:6',
]);
$cachedOtp = Cache::get("otp_{$request->email}");
if (!$cachedOtp || $cachedOtp != $request->otp) {
return response()->json(['message' => 'Invalid OTP'], 400);
}
Cache::forget("otp_{$request->email}");
return response()->json(['message' => 'OTP verified successfully!']);
}
}
—
### **2. Modify Vue Component**
Update your Vue component to integrate with Laravel APIs.
#### **(a) Update Registration Form Submission**
Modify `submit` function to call Laravel API.
const submit = handleSubmit(async values => {
loading.value = true;
try {
const response = await fetch(`${apiUrl}/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: values.name,
email: values.email,
password: values.password,
password_confirmation: values.confirmPassword,
}),
});
const result = await response.json();
if (!response.ok) {
message.value = result.errors ? Object.values(result.errors).join(', ') : result.message;
return;
}
message.value = result.message;
step.value = 2; // Move to OTP screen
} catch (error) {
message.value = "Registration failed.";
} finally {
loading.value = false;
}
});
#### **(b) Send OTP**
Modify the `sendOtp` function:
const sendOtp = async () => {
loading.value = true;
try {
const response = await fetch(`${apiUrl}/send-otp`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.value.value }),
});
const result = await response.json();
message.value = result.message;
} catch (error) {
message.value = "Failed to send OTP.";
} finally {
loading.value = false;
}
};
#### **(c) Verify OTP**
Modify `verifyOtp` function:
const verifyOtp = async () => {
loading.value = true;
try {
const response = await fetch(`${apiUrl}/verify-otp`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email.value.value, otp: otp.value }),
});
const result = await response.json();
if (!response.ok) {
message.value = "Invalid OTP!";
return;
}
message.value = "OTP Verified! Redirecting...";
step.value = 3; // Move to next step (Login)
} catch (error) {
message.value = "OTP verification failed.";
} finally {
loading.value = false;
}
};
—
### **3. Update Vue Template**
Modify “ to include OTP verification.
<template>
<SingleColumn :md="12" :mt="4">
<h3 class="text-h4 text-center mb-4">Register</h3>
<v-stepper v-model="step" :items="items" show-actions>
<template v-slot:item.1>
<h3 class="text-h6">Sign Up</h3>
<v-sheet class="mx-auto mt-4 mb-4" max-width="600">
<form @submit.prevent="submit">
<v-text-field v-model="name.value.value" label="Name"></v-text-field>
<v-text-field v-model="email.value.value" label="E-mail"></v-text-field>
<v-text-field v-model="password.value.value" label="Password"></v-text-field>
<v-text-field v-model="confirmPassword.value.value" label="Confirm Password"></v-text-field>
<v-btn class="me-4" type="submit">Submit</v-btn>
</form>
</v-sheet>
</template>
<template v-slot:item.2>
<v-card class="py-8 px-6 text-center mx-auto ma-4" elevation="12" max-width="400">
<h3 class="text-h6 mb-4">Verify Your Account</h3>
<div class="text-body-2">
We sent a verification code to {{ email.value.value }}. Check your email.
</div>
<v-otp-input v-model="otp" variant="solo"></v-otp-input>
<v-btn class="my-4" color="purple" text="Verify" @click="verifyOtp"></v-btn>
<div class="text-caption">
Didn't receive the code? <a href="#" @click.prevent="sendOtp">Resend</a>
</div>
</v-card>
</template>
<template v-slot:item.3>
<h3 class="text-h6 text-center">Login after verification</h3>
</template>
</v-stepper>
</SingleColumn>
</template>
—
### **Summary**
– **Laravel**: Added user registration, OTP generation, and verification.
– **Vue**: Updated form submission, OTP sending, and verification.
– **Next Steps**: Redirect to login after successful OTP verification.
Let me know if you need further modifications! π
—
## **1. Laravel Backend Setup**
### **Install Laravel Sanctum**
Run the following command in your Laravel project:
composer require laravel/sanctum
Then, publish the Sanctum configuration file and run migrations:
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
php artisan migrate
Next, add Sanctum's middleware in `app/Http/Kernel.php` under `$middlewareGroups`:
use LaravelSanctumHttpMiddlewareEnsureFrontendRequestsAreStateful;
'api' => [
EnsureFrontendRequestsAreStateful::class,
'throttle:api',
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
Now, add the `HasApiTokens` trait to your `User` model:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
—
### **2. User Registration with OTP**
Modify your `AuthController.php` to handle user registration, OTP generation, and login.
#### **Generate and Send OTP**
use AppModelsUser;
use IlluminateHttpRequest;
use IlluminateSupportFacadesHash;
use IlluminateSupportFacadesMail;
use IlluminateSupportFacadesValidator;
use IlluminateSupportStr;
class AuthController extends Controller
{
// Register User and Send OTP
public function register(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|string|min:2',
'email' => 'required|email|unique:users',
'password' => 'required|min:6|confirmed',
]);
if ($validator->fails()) {
return response()->json(['errors' => $validator->errors()], 422);
}
$otp = random_int(100000, 999999);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'otp' => $otp,
]);
// Send OTP via email
Mail::raw("Your OTP is: $otp", function ($message) use ($user) {
$message->to($user->email)
->subject('Account Verification OTP');
});
return response()->json(['message' => 'OTP sent to your email.'], 200);
}
#### **Verify OTP and Generate Token**
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|numeric'
]);
$user = User::where('email', $request->email)->first();
if (!$user || $user->otp != $request->otp) {
return response()->json(['message' => 'Invalid OTP'], 400);
}
// Mark as verified and generate token
$user->otp = null;
$user->email_verified_at = now();
$user->save();
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['token' => $token, 'user' => $user]);
}
#### **Login and Generate Token**
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required'
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Invalid credentials'], 401);
}
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['token' => $token, 'user' => $user]);
}
#### **Logout and Revoke Token**
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out successfully']);
}
—
### **3. Protect API Routes with Auth Middleware**
Modify your `routes/api.php`:
use AppHttpControllersAuthController;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/user', function (Request $request) {
return $request->user();
});
});
—
## **4. Vue.js Frontend Integration**
Modify your Vue.js component to handle user registration, OTP verification, and token-based requests.
### **Handle Registration and Send OTP**
Modify your `createUser` function:
const user = ref({
name: "",
email: "",
password: "",
confirmPassword: "",
});
const loading = ref(false);
const message = ref("");
const registerUser = async () => {
loading.value = true;
try {
const response = await fetch("https://your-laravel-api.com/api/register", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(user.value),
});
const result = await response.json();
if (response.ok) {
message.value = "OTP sent to your email.";
step.value = 2; // Move to OTP verification step
} else {
message.value = result.errors;
}
} catch (error) {
message.value = "Error: " + error.message;
} finally {
loading.value = false;
}
};
### **Verify OTP and Get Token**
const otp = ref("");
const verifyOtp = async () => {
try {
const response = await fetch("https://your-laravel-api.com/api/verify-otp", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: user.value.email, otp: otp.value }),
});
const result = await response.json();
if (response.ok) {
localStorage.setItem("token", result.token);
message.value = "Account verified successfully!";
step.value = 3; // Proceed to login
} else {
message.value = result.message;
}
} catch (error) {
message.value = "Error: " + error.message;
}
};
### **Login and Save Token**
const loginUser = async () => {
try {
const response = await fetch("https://your-laravel-api.com/api/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: user.value.email, password: user.value.password }),
});
const result = await response.json();
if (response.ok) {
localStorage.setItem("token", result.token);
message.value = "Login successful!";
} else {
message.value = result.message;
}
} catch (error) {
message.value = "Error: " + error.message;
}
};
### **Make Authenticated API Requests**
Modify your fetch requests to include the `Authorization` header:
const fetchData = async () => {
const token = localStorage.getItem("token");
const response = await fetch("https://your-laravel-api.com/api/protected-route", {
headers: { Authorization: `Bearer ${token}` },
});
const result = await response.json();
console.log(result);
};
—
## **Conclusion**
– Laravel Sanctum secures API endpoints.
– Users register and receive an OTP via email.
– OTP verification grants an authentication token.
– All API requests include `Authorization: Bearer token`.
This ensures each request is authenticated after login. Let me know if you need modifications! π
—
### **1. Install Sanctum (if not installed)**
Run:
composer require laravel/sanctum
Publish the Sanctum configuration:
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
Run migrations:
php artisan migrate
—
### **2. Add Sanctum Middleware in `Kernel.php`**
Modify `app/Http/Kernel.php`:
#### **Global Middleware (For Session-Based Authentication)**
If you're using Sanctum for SPA authentication, ensure it's in the `web` middleware group:
protected $middlewareGroups = [
'web' => [
LaravelSanctumHttpMiddlewareEnsureFrontendRequestsAreStateful::class,
// Other middleware...
],
];
#### **API Middleware (For Token-Based Authentication)**
For API token authentication, add Sanctum to the `api` middleware group:
protected $middlewareGroups = [
'api' => [
IlluminateRoutingMiddlewareSubstituteBindings::class,
],
];
—
### **3. Add `HasApiTokens` in User Model**
Modify `app/Models/User.php`:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
—
### **4. Protect API Routes Using Sanctum Middleware**
Modify `routes/api.php`:
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return response()->json($request->user());
});
Route::post('/logout', [AuthController::class, 'logout']);
});
—
### **5. Configure Sanctum in `config/sanctum.php`**
For API token authentication, update:
'guard' => ['web'],
If using Sanctum for SPA authentication, ensure the following:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),
—
### **6. Clear Config Cache and Restart Server**
Run:
php artisan config:clear
php artisan cache:clear
php artisan serve
—
Now, **all API requests require a Bearer token**. Let me know if you need further modifications! π
### **Adding Sanctum Middleware in Laravel 11**
Since Laravel 11 no longer has `Kernel.php`, you should apply middleware in the `routes/api.php` file directly.
—
### **1. Install Sanctum (If Not Installed)**
Run:
composer require laravel/sanctum
Then publish Sanctum's configuration:
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
Run migrations:
php artisan migrate
—
### **2. Apply Sanctum Middleware in `routes/api.php`**
Modify your `routes/api.php` file:
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
use AppHttpControllersAuthController;
// Public routes (no authentication required)
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
// Protected routes (Require Sanctum authentication)
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return response()->json($request->user());
});
Route::post('/logout', [AuthController::class, 'logout']);
});
—
### **3. Update the User Model**
Modify `app/Models/User.php`:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
—
### **4. Configure `config/sanctum.php`**
For API authentication, make sure `guards` are properly set in `config/sanctum.php`:
'guard' => ['web'],
For SPA authentication:
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),
—
### **5. Implement Authentication Controller**
Create a new Auth controller:
php artisan make:controller AuthController
Then add the following logic in `app/Http/Controllers/AuthController.php`:
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppModelsUser;
use IlluminateSupportFacadesHash;
use IlluminateValidationValidationException;
class AuthController extends Controller
{
// Register
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['access_token' => $token, 'token_type' => 'Bearer']);
}
// Login
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['access_token' => $token, 'token_type' => 'Bearer']);
}
// Logout
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out successfully']);
}
}
—
### **6. Clear Cache & Restart Server**
Run:
php artisan config:clear
php artisan cache:clear
php artisan serve
—
### **7. Making API Requests**
– **Register:**
POST /api/register
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}
– **Login:**
POST /api/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "password123"
}
– Response:
{
"access_token": "your_generated_token",
"token_type": "Bearer"
}
– **Authenticated Request (Pass Token in Header):**
GET /api/user
Authorization: Bearer your_generated_token
—
### **Done! π**
Now, Laravel 11 uses Sanctum authentication without `Kernel.php`. Let me know if you need further tweaks! π
—
### **1. Create AuthController in `API` Folder**
Run:
php artisan make:controller API/AuthController
Now, modify `app/Http/Controllers/API/AuthController.php`:
namespace AppHttpControllersAPI;
use AppHttpControllersController;
use IlluminateHttpRequest;
use AppModelsUser;
use IlluminateSupportFacadesHash;
use IlluminateValidationValidationException;
class AuthController extends Controller
{
// Register new user
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['access_token' => $token, 'token_type' => 'Bearer'], 201);
}
// Login user
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['The provided credentials are incorrect.'],
]);
}
$token = $user->createToken('auth_token')->plainTextToken;
return response()->json(['access_token' => $token, 'token_type' => 'Bearer']);
}
// Logout user
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out successfully']);
}
}
—
### **2. Update API Routes in `routes/api.php`**
Modify `routes/api.php`:
use IlluminateSupportFacadesRoute;
use AppHttpControllersAPIAuthController;
// Public routes (no authentication required)
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
// Protected routes (Require Sanctum authentication)
Route::middleware('auth:sanctum')->group(function () {
Route::get('/user', function (Request $request) {
return response()->json($request->user());
});
Route::post('/logout', [AuthController::class, 'logout']);
});
—
### **3. Update User Model for API Tokens**
Modify `app/Models/User.php`:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
—
### **4. Clear Cache & Restart Laravel**
Run:
php artisan config:clear
php artisan cache:clear
php artisan serve
—
### **5. API Requests to Test**
– **Register:**
POST /api/register
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123"
}
– **Login:**
POST /api/login
Content-Type: application/json
{
"email": "john@example.com",
"password": "password123"
}
– Response:
{
"access_token": "your_generated_token",
"token_type": "Bearer"
}
– **Authenticated Request (Pass Token in Header):**
GET /api/user
Authorization: Bearer your_generated_token
– **Logout:**
POST /api/logout
Authorization: Bearer your_generated_token
—
### **Done! π**
Now, your **AuthController** is properly structured in the `API` folder in Laravel 11. Let me know if you need further tweaks! π
not found
—
### **1. Check if Routes are Loaded Properly**
Run the following command to list all registered routes:
php artisan route:list
– Look for a `POST` route for `/api/register`.
– If it's missing, your `routes/api.php` file may not be loading correctly.
—
### **2. Ensure API Routes Exist**
Your `routes/api.php` should have:
use IlluminateSupportFacadesRoute;
use AppHttpControllersAPIAuthController;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:sanctum')->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
});
If `AuthController` is missing, ensure it's in `app/Http/Controllers/API/AuthController.php`.
—
### **3. Restart DDEV**
If the route exists in `route:list` but still doesn't work, restart DDEV:
ddev restart
—
### **4. Clear Laravel Cache**
Run inside the DDEV container:
ddev ssh
php artisan optimize:clear
php artisan config:clear
php artisan cache:clear
exit
Then try accessing the route again.
—
### **5. Check API Prefix in `RouteServiceProvider`**
Laravel's **API routes** are automatically prefixed with `/api`. Verify this in `app/Providers/RouteServiceProvider.php`:
protected function mapApiRoutes()
{
Route::middleware('api')
->prefix('api') // <- Ensure this is present
->group(base_path('routes/api.php'));
}
If you modified this, update your request URL accordingly.
—
### **6. Check `.env` File & Run Migrations**
Ensure your `.env` is correct:
APP_URL=http://syt.ddev.site
SANCTUM_STATEFUL_DOMAINS=syt.ddev.site
SESSION_DOMAIN=syt.ddev.site
Then, migrate the database if needed:
ddev ssh
php artisan migrate
exit
—
### **7. Try Registering Again**
Now, test:
curl -X POST http://syt.ddev.site/api/register
-H "Content-Type: application/json"
-d '{"name":"John Doe", "email":"john@example.com", "password":"password123"}'
If the issue persists, let me know what error message you see! π
To set up an **API authentication system with Sanctum**, follow these steps:
—
### **1. Create API Routes in `routes/web.php`**
Since Laravel 11 doesn't have `routes/api.php`, add your API routes inside `routes/web.php` and apply the `api` middleware.
use IlluminateSupportFacadesRoute;
use AppHttpControllersAPIAuthController;
// Public Routes
Route::middleware('api')->group(function () {
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
});
// Protected Routes (Require Authentication)
Route::middleware(['auth:sanctum', 'api'])->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/user', [AuthController::class, 'user']);
});
—
### **2. Create `AuthController` in `app/Http/Controllers/API`**
Since Laravel 11 follows a new structure, you need to create an **API folder** and an `AuthController`.
Run:
mkdir -p app/Http/Controllers/API
touch app/Http/Controllers/API/AuthController.php
Then, add the following code:
<?php
namespace AppHttpControllersAPI;
use AppHttpControllersController;
use IlluminateHttpRequest;
use AppModelsUser;
use IlluminateSupportFacadesHash;
use IlluminateValidationValidationException;
use LaravelSanctumSanctum;
class AuthController extends Controller
{
// Register User
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
return response()->json([
'message' => 'User registered successfully',
'token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user
], 201);
}
// Login User
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Invalid credentials'],
]);
}
return response()->json([
'message' => 'Login successful',
'token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user
], 200);
}
// Logout User
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out successfully'], 200);
}
// Get Authenticated User
public function user(Request $request)
{
return response()->json($request->user());
}
}
—
### **3. Install Sanctum & Configure Middleware**
Run:
composer require laravel/sanctum
Then publish Sanctum's configuration:
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
And run migrations:
php artisan migrate
Next, add Sanctum middleware to `bootstrap/app.php`:
$app->middleware([
LaravelSanctumHttpMiddlewareEnsureFrontendRequestsAreStateful::class,
]);
—
### **4. Add Sanctum Trait in `User.php`**
Modify `app/Models/User.php`:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
—
### **5. Restart DDEV & Check Routes**
Run:
ddev restart
php artisan route:list
You should now see:
POST /register
POST /login
POST /logout (Requires Sanctum auth)
GET /user (Requires Sanctum auth)
—
### **6. Test API with Postman**
#### **Register User**
POST `http://syt.ddev.site/register`
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123",
"password_confirmation": "password123"
}
#### **Login User**
POST `http://syt.ddev.site/login`
{
"email": "john@example.com",
"password": "password123"
}
**Response:**
{
"message": "Login successful",
"token": "generated_token_here",
"user": { "id": 1, "name": "John Doe", "email": "john@example.com" }
}
#### **Access Protected Routes**
Send a `GET` request to `http://syt.ddev.site/user` with:
Authorization: Bearer generated_token_here
—
### **Final Notes**
– Laravel 11 does not have `routes/api.php`; use `routes/web.php` with the `api` middleware.
– Sanctum authentication is required for protected routes.
– Add the `auth:sanctum` middleware to secure user authentication.
– Restart DDEV and clear caches if changes aren't reflecting.
Let me know if you need further debugging! π
—
## **1. Update Routes (`routes/web.php`)**
Since Laravel 11 does not have `routes/api.php`, we define API routes in `routes/web.php`:
use IlluminateSupportFacadesRoute;
use AppHttpControllersAPIAuthController;
// Public routes
Route::middleware('api')->group(function () {
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
Route::post('/login', [AuthController::class, 'login']);
});
// Protected routes
Route::middleware(['auth:sanctum', 'api'])->group(function () {
Route::post('/logout', [AuthController::class, 'logout']);
Route::get('/user', [AuthController::class, 'user']);
});
—
## **2. Create `AuthController.php` in `app/Http/Controllers/API`**
Run:
mkdir -p app/Http/Controllers/API
touch app/Http/Controllers/API/AuthController.php
Then, add the following code:
<?php
namespace AppHttpControllersAPI;
use AppHttpControllersController;
use IlluminateHttpRequest;
use AppModelsUser;
use IlluminateSupportFacadesHash;
use IlluminateSupportFacadesMail;
use IlluminateValidationValidationException;
use IlluminateSupportFacadesCache;
use LaravelSanctumSanctum;
class AuthController extends Controller
{
// Step 1: Register and Send OTP
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
// Generate OTP
$otp = rand(100000, 999999);
Cache::put('otp_' . $request->email, $otp, now()->addMinutes(10)); // Store OTP for 10 minutes
// Send OTP via email (You can use SMS here if needed)
Mail::raw("Your OTP is: $otp", function ($message) use ($request) {
$message->to($request->email)
->subject('Your OTP Code');
});
return response()->json(['message' => 'OTP sent to your email. Please verify.'], 200);
}
// Step 2: Verify OTP and Create User
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|numeric|digits:6',
'password' => 'required|string|min:6|confirmed',
'name' => 'required|string|max:255',
]);
$cachedOtp = Cache::get('otp_' . $request->email);
if (!$cachedOtp || $cachedOtp != $request->otp) {
return response()->json(['message' => 'Invalid or expired OTP'], 400);
}
// OTP is valid, create user
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
// Clear OTP after successful verification
Cache::forget('otp_' . $request->email);
return response()->json([
'message' => 'User registered successfully',
'token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user
], 201);
}
// Login User
public function login(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['Invalid credentials'],
]);
}
return response()->json([
'message' => 'Login successful',
'token' => $user->createToken('auth_token')->plainTextToken,
'user' => $user
], 200);
}
// Logout User
public function logout(Request $request)
{
$request->user()->tokens()->delete();
return response()->json(['message' => 'Logged out successfully'], 200);
}
// Get Authenticated User
public function user(Request $request)
{
return response()->json($request->user());
}
}
—
## **3. Configure Email in `.env`**
Make sure your email is configured correctly in the `.env` file:
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_mailtrap_username
MAIL_PASSWORD=your_mailtrap_password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="Your App Name"
You can use **Mailtrap** for testing or replace it with Gmail SMTP.
—
## **4. Update `User.php` Model**
Modify `app/Models/User.php` to include Sanctum:
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
—
## **5. Install & Configure Sanctum**
Run:
composer require laravel/sanctum
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
php artisan migrate
Then add middleware in `bootstrap/app.php`:
$app->middleware([
LaravelSanctumHttpMiddlewareEnsureFrontendRequestsAreStateful::class,
]);
—
## **6. Restart DDEV & Check Routes**
Run:
ddev restart
php artisan route:list
—
## **7. Frontend Implementation in Vue 3**
### **Register Component**
Create a **Vue 3 component** for registration:
<template>
<div>
<h2>Register</h2>
<input v-model="user.name" placeholder="Name" />
<input v-model="user.email" placeholder="Email" />
<input type="password" v-model="user.password" placeholder="Password" />
<input type="password" v-model="user.password_confirmation" placeholder="Confirm Password" />
<button @click="register">Send OTP</button>
<div v-if="showOtp">
<input v-model="otp" placeholder="Enter OTP" />
<button @click="verifyOtp">Verify & Register</button>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return {
user: {
name: '',
email: '',
password: '',
password_confirmation: ''
},
otp: '',
showOtp: false
};
},
methods: {
async register() {
try {
await axios.post('http://syt.ddev.site/register', this.user);
this.showOtp = true;
alert('OTP sent to email!');
} catch (error) {
console.error(error.response.data);
}
},
async verifyOtp() {
try {
const response = await axios.post('http://syt.ddev.site/verify-otp', {
email: this.user.email,
otp: this.otp,
password: this.user.password,
password_confirmation: this.user.password_confirmation,
name: this.user.name
});
localStorage.setItem('token', response.data.token);
alert('Registration successful!');
} catch (error) {
console.error(error.response.data);
}
}
}
};
</script>
—
## **Final Testing**
### **Step 1: Register User**
**POST** `http://syt.ddev.site/register`
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123",
"password_confirmation": "password123"
}
– This sends an **OTP** to the user's email.
### **Step 2: Verify OTP**
**POST** `http://syt.ddev.site/verify-otp`
{
"email": "john@example.com",
"otp": "123456",
"password": "password123",
"password_confirmation": "password123",
"name": "John Doe"
}
– This **verifies OTP**, registers the user, and returns an authentication token.
—
### **Conclusion**
β
Laravel 11 does not have `routes/api.php`, so we use `routes/web.php`.
β
OTP-based authentication is implemented using **Laravel Cache** and **Mail**.
β
Vue 3 frontend supports **OTP-based user verification and registration**.
Let me know if you need further tweaks! π
To improve performance, we'll **create a separate Mailable class** and use **queues** to send OTP emails asynchronously.
—
## **1. Configure Queue in Laravel**
First, ensure the queue driver is set to `database` in your `.env` file:
QUEUE_CONNECTION=database
Run the following command to create the required queue table:
php artisan queue:table
php artisan migrate
Start the queue worker in a separate terminal:
php artisan queue:work
—
## **2. Create OTP Mailable**
Run the following command:
php artisan make:mail OtpMail
Modify the generated file at `app/Mail/OtpMail.php`:
<?php
namespace AppMail;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldBeEncrypted;
use IlluminateContractsQueueShouldQueue;
use IlluminateMailMailable;
use IlluminateQueueSerializesModels;
class OtpMail extends Mailable implements ShouldQueue, ShouldBeEncrypted
{
use Queueable, SerializesModels;
public $otp;
/**
* Create a new message instance.
*/
public function __construct($otp)
{
$this->otp = $otp;
}
/**
* Build the message.
*/
public function build()
{
return $this->subject('Your OTP Code')
->view('emails.otp')
->with(['otp' => $this->otp]);
}
}
—
## **3. Create Email View**
Create an OTP email view at `resources/views/emails/otp.blade.php`:
<!DOCTYPE html>
<html>
<head>
<title>Your OTP Code</title>
</head>
<body>
<p>Hello,</p>
<p>Your OTP code is: <strong>{{ $otp }}</strong></p>
<p>This code is valid for 10 minutes. Please do not share it with anyone.</p>
<p>Thank you!</p>
</body>
</html>
—
## **4. Update `AuthController.php`**
Modify `register` to use **queues for sending OTP**:
use AppMailOtpMail;
use IlluminateSupportFacadesCache;
use IlluminateSupportFacadesMail;
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
// Generate OTP
$otp = rand(100000, 999999);
Cache::put('otp_' . $request->email, $otp, now()->addMinutes(10));
// Queue the email
Mail::to($request->email)->queue(new OtpMail($otp));
return response()->json(['message' => 'OTP sent to your email. Please verify.'], 200);
}
—
## **5. Verify OTP (No Changes Required)**
The OTP verification process remains the same.
—
## **6. Start Queue Worker**
If the queue is not running, start it:
php artisan queue:work
—
### **Final Workflow**
1. **User registers** β Laravel stores OTP in cache.
2. **OTP email is queued** (processed in the background).
3. **User enters OTP** β Laravel verifies and registers them.
4. **User receives a token for authentication**.
—
### **Conclusion**
β
**Email sending moved to the queue** for better performance.
β
**Uses a separate Mailable class** for better code organization.
β
**More scalable and secure OTP-based authentication**.
Let me know if you need further enhancements! π
—
### **Updated `OtpMail.php`**
Modify the `content()` method to use the correct Blade view:
<?php
namespace AppMail;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateMailMailable;
use IlluminateMailMailablesContent;
use IlluminateMailMailablesEnvelope;
use IlluminateQueueSerializesModels;
class OtpMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $otp;
/**
* Create a new message instance.
*/
public function __construct($otp)
{
$this->otp = $otp;
}
/**
* Get the message envelope.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Your OTP Code'
);
}
/**
* Get the message content definition.
*/
public function content(): Content
{
return new Content(
view: 'emails.otp',
with: ['otp' => $this->otp]
);
}
/**
* Get the attachments for the message.
*
* @return array<int, IlluminateMailMailablesAttachment>
*/
public function attachments(): array
{
return [];
}
}
—
### **Create OTP Email Blade View**
Create a new file at `resources/views/emails/otp.blade.php`:
<!DOCTYPE html>
<html>
<head>
<title>Your OTP Code</title>
</head>
<body>
<p>Hello,</p>
<p>Your OTP code is: <strong>{{ $otp }}</strong></p>
<p>This code is valid for 10 minutes. Please do not share it with anyone.</p>
<p>Thank you!</p>
</body>
</html>
—
### **Sending the OTP via Queue**
Modify your `AuthController` to send the OTP email **asynchronously using queues**:
use AppMailOtpMail;
use IlluminateHttpRequest;
use IlluminateSupportFacadesCache;
use IlluminateSupportFacadesMail;
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
// Generate OTP
$otp = rand(100000, 999999);
Cache::put('otp_' . $request->email, $otp, now()->addMinutes(10));
// Send OTP email via queue
Mail::to($request->email)->queue(new OtpMail($otp));
return response()->json(['message' => 'OTP sent to your email. Please verify.'], 200);
}
—
### **Ensure Queue is Running**
If you haven't already, set up queues in Laravel:
#### **1. Configure Queue Driver**
In `.env`:
QUEUE_CONNECTION=database
#### **2. Create Queue Table**
php artisan queue:table
php artisan migrate
#### **3. Start Queue Worker**
Run this command to start processing queued jobs:
php artisan queue:work
—
### **Final Workflow**
1. **User registers** β Laravel generates OTP & stores it in cache.
2. **OTP email is queued** (processed asynchronously).
3. **User enters OTP** β Laravel verifies it & logs them in.
4. **User gets an authentication token** for API requests.
Now your Laravel app is efficiently sending OTP emails using queues. π Let me know if you need any changes!
1. **The `MustVerifyEmail` trait is not used** in your `User` model.
2. **Laravel does not send the default email verification notification**.
3. **Sanctum or another authentication method** is used for login instead of Laravel's default email verification middleware.
—
### **Steps to Disable Default Email Verification**
#### **1. Remove `MustVerifyEmail` from the `User` Model**
By default, Laravel uses the `MustVerifyEmail` trait for email verification. Remove it if it's present:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasFactory, HasApiTokens;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
}
β **Make sure the `implements MustVerifyEmail` is removed** from the class definition.
—
#### **2. Remove the Default Email Verification Route (If Exists)**
Check if you have the email verification route in your routes and remove it if found.
In `routes/api.php` (or `routes/auth.php` in Laravel 11):
“`php
use IlluminateFoundationAuthEmailVerificationRequest;
// Remove this if it exists
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request
—
### **1. Remove `MustVerifyEmail` from API Registrations**
Modify your **API registration logic** so that it does **not require email verification**, but keep it intact for web users.
#### **Update API `AuthController` (app/Http/Controllers/Api/AuthController.php)**
Modify your **register method** to exclude email verification:
<?php
namespace AppHttpControllersApi;
use AppHttpControllersController;
use AppMailOtpMail;
use AppModelsUser;
use IlluminateHttpRequest;
use IlluminateSupportFacadesHash;
use IlluminateSupportFacadesMail;
use IlluminateSupportStr;
class AuthController extends Controller
{
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
$otp = rand(100000, 999999); // Generate OTP
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'email_verified_at' => now(), // β
Mark email as verified
'otp' => $otp, // Store OTP for verification
]);
// Send OTP via email using the queue
Mail::to($user->email)->queue(new OtpMail($otp));
return response()->json([
'message' => 'OTP sent to your email. Please verify to continue.',
]);
}
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|integer',
]);
$user = User::where('email', $request->email)->where('otp', $request->otp)->first();
if (!$user) {
return response()->json(['message' => 'Invalid OTP'], 422);
}
$user->update([
'otp' => null, // Clear OTP after successful verification
]);
// Generate and return Sanctum token
$token = $user->createToken('authToken')->plainTextToken;
return response()->json([
'message' => 'User verified successfully',
'token' => $token,
'user' => $user,
]);
}
}
—
### **2. Keep Email Verification for Web Users**
For users who register via the web (e.g., Laravel Breeze, Jetstream), keep the **MustVerifyEmail** trait in the `User` model:
<?php
namespace AppModels;
use IlluminateContractsAuthMustVerifyEmail; // β
Keep this for web users
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use LaravelSanctumHasApiTokens;
class User extends Authenticatable implements MustVerifyEmail
{
use HasFactory, HasApiTokens;
}
—
### **3. Separate API and Web Middleware**
Ensure that your API routes **do not require** email verification.
β
**Modify `routes/api.php`**
Define the API routes separately without enforcing email verification:
use AppHttpControllersApiAuthController;
use IlluminateSupportFacadesRoute;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
β
**Modify `routes/web.php`**
Keep the web email verification routes:
use IlluminateFoundationAuthEmailVerificationRequest;
use IlluminateHttpRequest;
use IlluminateSupportFacadesRoute;
// Email verification for web users
Route::get('/email/verify', function () {
return view('auth.verify-email');
})->middleware('auth')->name('verification.notice');
Route::get('/email/verify/{id}/{hash}', function (EmailVerificationRequest $request) {
$request->fulfill();
return redirect('/home');
})->middleware(['auth', 'signed'])->name('verification.verify');
Route::post('/email/resend', function (Request $request) {
$request->user()->sendEmailVerificationNotification();
return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.resend');
—
### **Final Flow**
1. **API Registration**
– User registers via API.
– OTP is sent via email.
– User verifies OTP β **Token is issued**.
2. **Web Registration**
– Default Laravel **email verification** remains enabled.
– Web users must verify via email before logging in.
—
This way, **API users are verified via OTP, while web users follow the default Laravel email verification**. π
1. **User registers β Save user in the database β Send OTP via email**
2. **User verifies OTP β Return token after successful verification**
—
### **1. Update `AuthController` (app/Http/Controllers/Api/AuthController.php)**
<?php
namespace AppHttpControllersApi;
use AppHttpControllersController;
use AppMailOtpMail;
use AppModelsUser;
use IlluminateHttpRequest;
use IlluminateSupportFacadesHash;
use IlluminateSupportFacadesMail;
use IlluminateSupportStr;
class AuthController extends Controller
{
/**
* Handle user registration and send OTP.
*/
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
// Generate OTP
$otp = rand(100000, 999999);
// Create user but do not mark email as verified yet
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'otp' => $otp, // Store OTP for verification
]);
// Send OTP via email using queue
Mail::to($user->email)->queue(new OtpMail($otp));
return response()->json([
'message' => 'OTP sent to your email. Please verify to continue.',
]);
}
/**
* Verify OTP and return authentication token.
*/
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|integer',
]);
$user = User::where('email', $request->email)->where('otp', $request->otp)->first();
if (!$user) {
return response()->json(['message' => 'Invalid OTP'], 422);
}
// Clear OTP after successful verification
$user->update([
'otp' => null,
'email_verified_at' => now(), // β
Mark email as verified
]);
// Generate Sanctum token
$token = $user->createToken('authToken')->plainTextToken;
return response()->json([
'message' => 'User verified successfully',
'token' => $token,
'user' => $user,
]);
}
}
—
### **2. Update `User` Model (app/Models/User.php)**
Modify the `User` model to include an **`otp` field**:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use LaravelSanctumHasApiTokens;
class User extends Authenticatable
{
use HasFactory, HasApiTokens;
protected $fillable = [
'name',
'email',
'password',
'otp',
'email_verified_at',
];
protected $hidden = [
'password',
'remember_token',
'otp',
];
}
—
### **3. Create `OtpMail` Mailable (app/Mail/OtpMail.php)**
<?php
namespace AppMail;
use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateMailMailable;
use IlluminateQueueSerializesModels;
class OtpMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
public $otp;
/**
* Create a new message instance.
*/
public function __construct($otp)
{
$this->otp = $otp;
}
/**
* Build the message.
*/
public function build()
{
return $this->subject('Your OTP Code')
->view('emails.otp')
->with(['otp' => $this->otp]);
}
}
—
### **4. Create Email View (resources/views/emails/otp.blade.php)**
<!DOCTYPE html>
<html>
<head>
<title>Your OTP Code</title>
</head>
<body>
<h1>Your OTP Code</h1>
<p>Your One-Time Password (OTP) is: <strong>{{ $otp }}</strong></p>
<p>Please enter this OTP to verify your email.</p>
</body>
</html>
—
### **5. Add OTP Column in Database**
Run a migration to add an `otp` field in the `users` table.
php artisan make:migration add_otp_to_users_table --table=users
Modify the migration file:
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('otp')->nullable()->after('password');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('otp');
});
}
Run the migration:
php artisan migrate
—
### **6. Define API Routes (routes/api.php)**
use AppHttpControllersApiAuthController;
use IlluminateSupportFacadesRoute;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
—
### **7. Queue Setup for OTP Emails**
Ensure your **queue is configured** for sending OTP emails.
– Use **database queue driver** in `.env`:
QUEUE_CONNECTION=database
– Create queue table:
php artisan queue:table
php artisan migrate
– Start the queue worker:
php artisan queue:work
—
### **8. Verify Registration & OTP via API**
#### **Register Request:**
POST http://your-app.test/api/register
Content-Type: application/json
{
"name": "John Doe",
"email": "johndoe@example.com",
"password": "password123",
"password_confirmation": "password123"
}
β **Response:**
{
"message": "OTP sent to your email. Please verify to continue."
}
#### **Verify OTP Request:**
POST http://your-app.test/api/verify-otp
Content-Type: application/json
{
"email": "johndoe@example.com",
"otp": "123456"
}
β **Response (On Success)**
{
"message": "User verified successfully",
"token": "your-generated-sanctum-token",
"user": {
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com"
}
}
—
### **9. Login in Vue.js (After OTP Verification)**
Once the OTP is verified and the token is received, **store the token in Vue.js** and use it for future API requests.
#### **Save Token in LocalStorage**
async function verifyOtp() {
const response = await axios.post('/api/verify-otp', {
email: email,
otp: otp
});
if (response.data.token) {
localStorage.setItem('authToken', response.data.token);
axios.defaults.headers.common['Authorization'] = `Bearer ${response.data.token}`;
router.push('/dashboard'); // Redirect user
}
}
#### **Set Token in Axios for Future Requests**
axios.defaults.headers.common['Authorization'] = `Bearer ${localStorage.getItem('authToken')}`;
—
### **π― Final Flow**
β
**User registers β OTP sent**
β
**User enters OTP β Verified β Returns Sanctum token**
β
**Vue stores token β Logs in automatically**
This ensures **email verification via OTP** only for API users while keeping Laravelβs default verification for web users. π
—
### **1. Add Method in `AuthController`**
Modify your `app/Http/Controllers/Api/AuthController.php`:
public function getUserByEmail(Request $request)
{
$request->validate([
'email' => 'required|email',
]);
$user = User::where('email', $request->email)->first();
if (!$user) {
return response()->json(['message' => 'User not found'], 404);
}
return response()->json([
'user' => $user
]);
}
—
### **2. Define API Route (`routes/api.php`)**
Route::get('/user', [AuthController::class, 'getUserByEmail']);
—
### **3. API Request Example**
#### **GET Request:**
GET http://your-app.test/api/user?email=johndoe@example.com
#### **Response (If user exists)**
{
"user": {
"id": 1,
"name": "John Doe",
"email": "johndoe@example.com",
"created_at": "2025-03-09T12:34:56.000000Z"
}
}
#### **Response (If user not found)**
{
"message": "User not found"
}
—
### **Optional: Restrict with Authentication**
If you only want **authenticated users** to access this route, add `auth:sanctum` middleware:
Route::get('/user', [AuthController::class, 'getUserByEmail'])->middleware('auth:sanctum');
Now, only **authenticated users with a valid Sanctum token** can fetch user details. π
<script>
import { ref,shallowRef,inject } from "vue";
import { useForm, useField } from 'vee-validate'
import SingleColumn from '@/layouts/SingleColumn.vue';
const apiUrl = inject('apiUrl');
console.log(apiUrl);
const step = ref(1);
const items = [
'Sign Up',
'Otp',
'Login',
]
const otp = shallowRef('')
const { handleSubmit, handleReset, values } = useForm({
validationSchema: {
name (value) {
if (value?.length >= 2) return true
return 'Name needs to be at least 2 characters.'
},
email (value) {
if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/i.test(value)) return true
return 'Must be a valid e-mail.'
},
password (value) {
if (value?.length >= 6) return true
return 'Password needs to be at least 6 characters.'
},
confirmPassword (value) {
if (value === values.password) return true;
return 'Passwords do not match.';
}
},
})
const name = useField('name')
const email = useField('email')
const password = useField('password')
const confirmPassword = useField('confirmPassword');
const submit = handleSubmit(values => {
alert(JSON.stringify(values, null, 2))
step.value = 2;
})
const loading = ref(false);
const message = ref("");
const showPassword = ref(false);
// const createUser = async () => {
// loading.value = true;
// const apiUrl = "https://autohauslange.carmar.digital/wp-json/wp/v2/users";
// const adminUsername = "plstr-admin";
// const applicationPassword = "MrYNs0r68Hb7ywed01JmiX7d";
// // Encode credentials for Basic Auth
// const authHeader = `Basic ${btoa(`${adminUsername}:${applicationPassword}`)}`;
// try {
// const response = await fetch(apiUrl, {
// method: "POST",
// headers: {
// "Authorization": authHeader,
// "Content-Type": "application/json",
// },
// body: JSON.stringify(user.value),
// });
// const result = await response.json();
// if (response.ok) {
// message.value = `User ${result.username} created successfully!`;
// } else {
// message.value = `Error: ${result.message}`;
// }
// } catch (error) {
// message.value = `Request failed: ${error.message}`;
// loading.value = false;
// } finally {
// loading.value = false;
// }
// };
</script>
Register
Sigh UP
submit
clear
Verify Your Account
We sent a verification code to {{ email.value.value ? email.value.value : 'test@gmail.com' }}
Please check your email and paste the code below.
Didn't receive the code? Resend
call register apii in vue
—
### **1. Update `submit` Method**
Modify your `submit` method to send a request to the Laravel API when the user registers.
import axios from 'axios';
const submit = handleSubmit(async (values) => {
try {
const response = await axios.post(`${apiUrl}/register`, {
name: values.name,
email: values.email,
password: values.password,
password_confirmation: values.confirmPassword
});
if (response.data.success) {
step.value = 2; // Move to OTP step
} else {
console.error("Registration failed:", response.data.message);
}
} catch (error) {
console.error("Error during registration:", error.response?.data || error.message);
}
});
—
### **2. Handle OTP Verification**
Modify the **Verify Button** inside the OTP step (`step 2`) to verify the OTP.
<v-btn
class="my-4"
color="purple"
height="40"
text="Verify"
variant="flat"
width="70%"
@click="verifyOtp"
>
Verify
</v-btn>
Now, define the `verifyOtp` method:
const verifyOtp = async () => {
try {
const response = await axios.post(`${apiUrl}/verify-otp`, {
email: email.value.value,
otp: otp.value
});
if (response.data.success) {
localStorage.setItem("token", response.data.token);
alert("OTP Verified! Logged in successfully.");
step.value = 3; // Move to the final step
} else {
console.error("OTP verification failed:", response.data.message);
}
} catch (error) {
console.error("Error verifying OTP:", error.response?.data || error.message);
}
};
—
### **3. Laravel API Routes (`routes/api.php`)**
Make sure your Laravel API has the correct routes:
use AppHttpControllersApiAuthController;
Route::post('/register', [AuthController::class, 'register']);
Route::post('/verify-otp', [AuthController::class, 'verifyOtp']);
—
### **4. Laravel Controller (`AuthController.php`)**
Ensure your controller handles registration, OTP generation, and verification.
#### **Register Method**
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|min:2',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|confirmed',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'otp' => rand(100000, 999999), // Generate OTP
]);
// Send OTP via email using queue
Mail::to($user->email)->queue(new OtpMail($user->otp));
return response()->json(['success' => true, 'message' => 'OTP sent to your email.']);
}
#### **Verify OTP Method**
public function verifyOtp(Request $request)
{
$request->validate([
'email' => 'required|email',
'otp' => 'required|numeric',
]);
$user = User::where('email', $request->email)->first();
if (!$user || $user->otp !== $request->otp) {
return response()->json(['success' => false, 'message' => 'Invalid OTP.'], 400);
}
// Mark user as verified and return token
$user->email_verified_at = now();
$user->otp = null; // Clear OTP after verification
$user->save();
$token = $user->createToken('authToken')->plainTextToken;
return response()->json(['success' => true, 'token' => $token, 'user' => $user]);
}
—
### **Now, When a User Registers:**
1. They fill in the form and click **Submit** β API sends an OTP.
2. They enter the OTP and click **Verify** β API verifies OTP and returns a token.
3. The Vue app stores the token in `localStorage`, logging in the user.
This ensures **secure authentication** using Laravel Sanctum & Vue! π
### Steps to Implement OTP Verification:
1. **Call the Register API** β Send user details to your backend.
2. **Move to OTP Step** β Once registration is successful, move to step 2.
3. **Call OTP Verification API** β Validate the OTP before allowing login.
—
### **Updated Code**
<script setup lang="ts">
import { ref, shallowRef, inject } from "vue";
import { useForm, useField } from "vee-validate";
import SingleColumn from "@/layouts/SingleColumn.vue";
import axios from "axios";
const apiUrl = inject("apiUrl");
const step = ref(1);
const items = ["Sign Up", "Otp", "Login"];
const otp = shallowRef("");
const loading = ref(false);
const message = ref("");
const showPassword = ref(false);
const userId = ref(null); // Store the user ID after registration
// Form Validation Schema
const { handleSubmit, handleReset, values } = useForm({
validationSchema: {
name(value) {
if (value?.length >= 2) return true;
return "Name needs to be at least 2 characters.";
},
email(value) {
if (/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/i.test(value))
return true;
return "Must be a valid e-mail.";
},
password(value) {
if (value?.length >= 6) return true;
return "Password needs to be at least 6 characters.";
},
confirmPassword(value) {
if (value === values.password) return true;
return "Passwords do not match.";
},
},
});
const name = useField("name");
const email = useField("email");
const password = useField("password");
const confirmPassword = useField("confirmPassword");
// **Register User API Call**
const submit = handleSubmit(async (values) => {
loading.value = true;
try {
const response = await axios.post(`${apiUrl}/register`, {
name: values.name,
email: values.email,
password: values.password,
});
if (response.data.success) {
userId.value = response.data.user_id; // Store user ID for OTP verification
step.value = 2;
} else {
message.value = response.data.message || "Registration failed.";
}
} catch (error) {
message.value = error.response?.data?.message || "Something went wrong.";
} finally {
loading.value = false;
}
});
// **Verify OTP API Call**
const verifyOtp = async () => {
if (!otp.value) {
message.value = "Please enter the OTP.";
return;
}
loading.value = true;
try {
const response = await axios.post(`${apiUrl}/verify-otp`, {
user_id: userId.value,
otp: otp.value,
});
if (response.data.success) {
step.value = 3; // Move to login step
} else {
message.value = response.data.message || "Invalid OTP.";
}
} catch (error) {
message.value = error.response?.data?.message || "OTP verification failed.";
} finally {
loading.value = false;
}
};
</script>
<template>
<SingleColumn :md="12" :mt="4">
<h3 class="text-h4 text-center mb-4">Register</h3>
<v-stepper v-model="step" :items="items" show-actions>
<!-- Step 1: Sign Up -->
<template v-slot:item.1>
<h3 class="text-h6">Sign Up</h3>
<v-sheet class="mx-auto mt-4 mb-4" max-width="600">
<form @submit.prevent="submit">
<v-text-field v-model="name.value.value" :error-messages="name.errorMessage.value" label="Name"></v-text-field>
<v-text-field v-model="email.value.value" :error-messages="email.errorMessage.value" label="E-mail"></v-text-field>
<v-text-field
v-model="password.value.value"
:type="showPassword ? 'text' : 'password'"
:error-messages="password.errorMessage.value"
prepend-inner-icon="mdi-lock"
:append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
@click:append-inner="showPassword = !showPassword"
label="Password"
></v-text-field>
<v-text-field
v-model="confirmPassword.value.value"
:type="showPassword ? 'text' : 'password'"
:error-messages="confirmPassword.errorMessage.value"
prepend-inner-icon="mdi-lock"
:append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
@click:append-inner="showPassword = !showPassword"
label="Confirm Password"
></v-text-field>
<v-btn class="me-4" type="submit" :loading="loading">Submit</v-btn>
<v-btn @click="handleReset">Clear</v-btn>
</form>
<p class="error-message">{{ message }}</p>
</v-sheet>
</template>
<!-- Step 2: OTP Verification -->
<template v-slot:item.2>
<v-card class="py-8 px-6 text-center mx-auto ma-4" elevation="12" max-width="400" width="100%">
<h3 class="text-h6 mb-4">Verify Your Account</h3>
<div class="text-body-2">
We sent a verification code to {{ email.value.value || "test@gmail.com" }} <br />
Please check your email and paste the code below.
</div>
<v-sheet color="surface">
<v-otp-input v-model="otp" type="password" variant="solo"></v-otp-input>
</v-sheet>
<v-btn class="my-4" color="purple" height="40" text="Verify" variant="flat" width="70%" @click="verifyOtp" :loading="loading">Verify</v-btn>
<div class="text-caption">
Didn't receive the code? <a href="#" @click.prevent="otp = ''">Resend</a>
</div>
<p class="error-message">{{ message }}</p>
</v-card>
</template>
<!-- Step 3: Login -->
<template v-slot:item.3>
<h3 class="text-h6">Login</h3>
<p>Your account is verified! You can now log in.</p>
<v-btn color="primary" to="/login">Go to Login</v-btn>
</template>
</v-stepper>
</SingleColumn>
</template>
<style scoped>
.error-message {
color: red;
text-align: center;
margin-top: 10px;
}
</style>
—
### **How It Works**
1. **User Registers**
– When the user submits the form, `submit()` calls the `/register` API.
– If successful, the app stores the `user_id` and moves to Step 2.
2. **User Enters OTP**
– The OTP is sent via email (backend handles this).
– The user enters OTP and clicks **Verify**.
– `verifyOtp()` sends the OTP to the `/verify-otp` API.
– If valid, the app moves to **Login Step**.
3. **Login Step**
– Once OTP is verified, the user can log in.
—
### **Next Steps**
– Add the actual API endpoints in Laravel.
– Implement an OTP resend feature.
– Handle errors and show appropriate messages.
Let me know if you need backend support! π
—
### **Troubleshooting Steps**
1. **Console Debugging:**
– Open **DevTools (F12) > Console** and check for any errors when clicking the **Verify** button.
2. **Network Request Check:**
– Go to **DevTools > Network > Fetch/XHR** to see if the OTP request is being sent.
– Look for any **failed requests** and check their responses.
3. **Fixing Button Click Handler:**
– Ensure `verifyOtp()` is correctly assigned to the button.
– Add a `console.log()` inside `verifyOtp()` to confirm it runs.
—
### **Updated Code (with Debugging)**
<!-- OTP Verification -->
<v-btn
class="my-4"
color="purple"
height="40"
text="Verify"
variant="flat"
width="70%"
@click="verifyOtp"
:loading="loading"
>
Verify
</v-btn>
// Verify OTP API Call
const verifyOtp = async () => {
console.log("Verify button clicked, OTP:", otp.value); // Debug log
if (!otp.value) {
message.value = "Please enter the OTP.";
return;
}
loading.value = true;
try {
console.log("Sending OTP verification request...");
const response = await axios.post(`${apiUrl}/verify-otp`, {
user_id: userId.value,
otp: otp.value,
});
console.log("Response received:", response.data);
if (response.data.success) {
step.value = 3; // Move to login step
} else {
message.value = response.data.message || "Invalid OTP.";
}
} catch (error) {
console.error("OTP Verification Error:", error.response?.data);
message.value = error.response?.data?.message || "OTP verification failed.";
} finally {
loading.value = false;
}
};
—
### **What This Fix Does**
– **Logs button click** β `"Verify button clicked, OTP: 123456"`
– **Logs API call** β `"Sending OTP verification request…"`
– **Shows API response** β `"Response received: { success: true }"`
– **Handles errors** β Shows error message in console & UI.
—
### **What You Should Check**
1. **Is `verifyOtp()` being logged in the console?**
– If **not**, check `@click="verifyOtp"` is correctly written.
– If Vue reactivity is broken, try `@click="() => verifyOtp()"`.
2. **Is the API request sent in Network tab?**
– If missing, check `apiUrl` value in `inject("apiUrl")`.
– Ensure `userId.value` is **set properly after registration**.
3. **Does the API return an error?**
– If `response.data.success` is `false`, check the **Laravel logs**:
tail -f storage/logs/laravel.log
—
### **Next Steps**
– If API is not called, check **Vue reactivity** issues.
– If API fails, debug **Laravel controller logic**.
Let me know what you find! π
### **Steps to Update the Register Link & Redirect Logged-in Users**
1. **Change "Register" to "My Account"** in your navbar.
2. **Check if the user is logged in** when they visit the page.
3. **Redirect logged-in users to the dashboard** instead of showing the registration form.
—
### **1οΈβ£ Change "Register" to "My Account"**
Update your navigation bar where the register link is defined:
<template>
<nav>
<router-link v-if="!isAuthenticated" to="/my-account">My Account</router-link>
<router-link v-else to="/dashboard">Dashboard</router-link>
</nav>
</template>
<script setup>
import { computed } from "vue";
import { useAuthStore } from "@/store/auth"; // Assuming you have an auth store
const authStore = useAuthStore();
const isAuthenticated = computed(() => authStore.isAuthenticated);
</script>
β **Now, the navbar shows "My Account" instead of "Register".**
—
### **2οΈβ£ Redirect Logged-in Users to Dashboard**
Modify your **MyAccount.vue** component to check authentication:
<script setup>
import { onMounted } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/store/auth";
const router = useRouter();
const authStore = useAuthStore();
onMounted(() => {
if (authStore.isAuthenticated) {
router.push("/dashboard"); // Redirect if already logged in
}
});
</script>
<template>
<div>
<h2>My Account</h2>
<RegisterForm v-if="!authStore.isAuthenticated" />
</div>
</template>
β **Now, if the user is logged in, they go straight to the dashboard!**
—
### **3οΈβ£ Ensure Login Stores Auth Status**
In your `auth.js` store (Vuex/Pinia):
import { defineStore } from "pinia";
export const useAuthStore = defineStore("auth", {
state: () => ({
user: JSON.parse(localStorage.getItem("user")) || null,
}),
getters: {
isAuthenticated: (state) => !!state.user,
},
actions: {
login(userData) {
this.user = userData;
localStorage.setItem("user", JSON.stringify(userData));
},
logout() {
this.user = null;
localStorage.removeItem("user");
},
},
});
β **This ensures authentication persists across reloads.**
—
### **4οΈβ£ Handle Logout Properly**
Add a logout button in the navbar:
<v-btn v-if="authStore.isAuthenticated" @click="authStore.logout()">Logout</v-btn>
—
### **Final Result**
– **Navbar shows "My Account"** for guests, **"Dashboard"** for logged-in users.
– **If logged in, `/my-account` redirects to `/dashboard` automatically.**
– **Login state is stored in `localStorage`, so users stay logged in.**
Let me know if you need tweaks! π
—
### **1οΈβ£ Create `auth.js` Store in Pinia**
Create a file:
π **`src/store/auth.js`**
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import axios from "axios";
export const useAuthStore = defineStore("auth", () => {
const user = ref(JSON.parse(localStorage.getItem("user")) || null);
const isAuthenticated = computed(() => !!user.value);
const login = async (credentials) => {
try {
const response = await axios.post("/api/login", credentials);
user.value = response.data.user;
localStorage.setItem("user", JSON.stringify(response.data.user));
} catch (error) {
console.error("Login failed", error);
throw error;
}
};
const logout = () => {
user.value = null;
localStorage.removeItem("user");
};
return { user, isAuthenticated, login, logout };
});
β **This Pinia store manages authentication state globally.**
—
### **2οΈβ£ Use the Store in Components**
#### **Navbar.vue**
<template>
<nav>
<router-link v-if="!isAuthenticated" to="/my-account">My Account</router-link>
<router-link v-else to="/dashboard">Dashboard</router-link>
<v-btn v-if="isAuthenticated" @click="logout">Logout</v-btn>
</nav>
</template>
<script setup>
import { useAuthStore } from "@/store/auth";
import { computed } from "vue";
const authStore = useAuthStore();
const isAuthenticated = computed(() => authStore.isAuthenticated);
const logout = () => authStore.logout();
</script>
β **Shows "My Account" for guests, "Dashboard" for logged-in users.**
—
### **3οΈβ£ Redirect Logged-in Users to Dashboard**
#### **MyAccount.vue**
<script setup>
import { onMounted } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/store/auth";
const router = useRouter();
const authStore = useAuthStore();
onMounted(() => {
if (authStore.isAuthenticated) {
router.push("/dashboard");
}
});
</script>
<template>
<div>
<h2>My Account</h2>
<RegisterForm v-if="!authStore.isAuthenticated" />
</div>
</template>
β **Redirects to Dashboard if the user is already logged in.**
—
### **4οΈβ£ Call Login API in Login Form**
#### **Login.vue**
“`vue
Login
import { ref } from "vue";
import { useAuthStore } from "@/store/auth";
import { useRouter } from "vue-router";
const email = ref("");
const password = ref("");
const authStore = useAuthStore();
const router = useRouter();
const handleLogin = async () => {
try {
await authStore.login
import { defineStore } from 'pinia';
interface User {
id: number;
name: string;
email: string;
}
export const useAuthStore = defineStore('auth', {
state: () => ({
user: localStorage.getItem('user') ? (JSON.parse(localStorage.getItem('user')!) as User) : null,
token: localStorage.getItem('token') || null,
}),
actions: {
setUser(user: User, token: string) {
this.user = user;
this.token = token;
localStorage.setItem('user', JSON.stringify(user));
localStorage.setItem('token', token);
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem('user');
localStorage.removeItem('token');
}
}
});
in above
### **Fixes & Improvements:**
1. **Persist state properly**
– You were directly accessing `localStorage` in `state`, which is fine, but in TypeScript, it's better to initialize it safely.
2. **Add `isAuthenticated` Getter**
– This will help in Vue components to check if the user is logged in.
3. **Use `try-catch` in `setUser` for safety**
– Ensures localStorage doesn't break if invalid JSON is stored.
—
### **β Improved `auth.ts` Store**
import { defineStore } from "pinia";
interface User {
id: number;
name: string;
email: string;
}
export const useAuthStore = defineStore("auth", {
state: () => ({
user: null as User | null,
token: null as string | null,
}),
getters: {
isAuthenticated: (state) => !!state.token, // Returns true if token exists
},
actions: {
setUser(user: User, token: string) {
try {
this.user = user;
this.token = token;
localStorage.setItem("user", JSON.stringify(user));
localStorage.setItem("token", token);
} catch (error) {
console.error("Failed to save user to localStorage", error);
}
},
loadUserFromStorage() {
const userData = localStorage.getItem("user");
const tokenData = localStorage.getItem("token");
if (userData && tokenData) {
this.user = JSON.parse(userData) as User;
this.token = tokenData;
}
},
logout() {
this.user = null;
this.token = null;
localStorage.removeItem("user");
localStorage.removeItem("token");
},
},
});
—
### **β
How to Use in Vue Components**
#### **1οΈβ£ Automatically Load User from Storage**
In your `App.vue` or `main.ts`, call `loadUserFromStorage()`:
import { createApp } from "vue";
import { createPinia } from "pinia";
import { useAuthStore } from "@/stores/auth";
import App from "./App.vue";
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
const authStore = useAuthStore();
authStore.loadUserFromStorage(); // Loads user on app start
app.mount("#app");
—
#### **2οΈβ£ Redirect Logged-in Users in `MyAccount.vue`**
<script setup>
import { onMounted } from "vue";
import { useRouter } from "vue-router";
import { useAuthStore } from "@/stores/auth";
const router = useRouter();
const authStore = useAuthStore();
onMounted(() => {
if (authStore.isAuthenticated) {
router.push("/dashboard"); // Redirect logged-in users
}
});
</script>
<template>
<div>
<h2>My Account</h2>
</div>
</template>
—
#### **3οΈβ£ Conditionally Show "My Account" or "Dashboard"**
<template>
<nav>
<router-link v-if="!authStore.isAuthenticated" to="/my-account">My Account</router-link>
<router-link v-else to="/dashboard">Dashboard</router-link>
<button v-if="authStore.isAuthenticated" @click="authStore.logout">Logout</button>
</nav>
</template>
<script setup>
import { useAuthStore } from "@/stores/auth";
const authStore = useAuthStore();
</script>
—
β
**Now:**
– Users stay logged in even after page refresh.
– "My Account" β Shows for guests.
– "Dashboard" β Shows for logged-in users.
– Users get redirected if already logged in. π
Let me know if you need further improvements! π