JWT Authentication in a Laravel and Vue.js Application
L earn how to implement JWT authentication in your Laravel and Vue.js application with this step-by-step guide. Discover how to configure Axios for API requests, secure routes with middleware, create an authentication controller, and build a login component. Enhance your app's security and user experience with these essential techniques.
Setting Up Axios for API Requests in Vue.js
To start, we'll configure Axios to handle API requests, including setting the base URL and attaching the JWT token if it exists.
import axios from 'axios';
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.axios.defaults.baseURL = ${import.meta.env.VITE_BASE_URL};
// Set the JWT token if available
const token = localStorage.getItem('token');
if (token) {
window.axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
}
Route::middleware('auth.jwt')->group(function () {
// Protected routes go here
});
Configuring Laravel Application with Middleware
We configure the Laravel application to include routing and middleware settings.
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'auth.jwt' => \App\Http\Middleware\EnsureTokenIsValid::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Creating an AuthController for User Authentication
We create an AuthController to handle user registration, login, token validation, and other related actions.
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;
class AuthController extends Controller
{
public function register(Request $request)
{
$credentials = $request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
]);
if ($credentials) {
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = JWTAuth::customClaims(['site' => env('APP_URL')])->fromUser($user);
return $this->respondWithToken($token);
} else {
return response()->json($credentials, 422);
}
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
$credentials = $request->only('email', 'password');
try {
if (!$token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'Invalid credentials'], 401);
}
} catch (JWTException $e) {
return response()->json(['error' => 'Could not create token'], 500);
}
$token = JWTAuth::customClaims(['site' => env('APP_URL')])->attempt($credentials);
return $this->respondWithToken($token);
}
public function validateToken(Request $request)
{
try {
$user = JWTAuth::parseToken()->authenticate();
$payload = JWTAuth::getPayload();
if ($payload['site'] !== env('APP_URL')) {
return response()->json(['valid' => false, 'error' => 'Invalid site identifier'], 401);
}
return response()->json(['valid' => true], 200);
} catch (JWTException $e) {
return response()->json(['valid' => false, 'error' => 'Token is invalid or expired'], 401);
}
}
public function me()
{
$user = JWTAuth::parseToken()->authenticate();
return response()->json($user);
}
public function logout()
{
JWTAuth::invalidate(JWTAuth::getToken());
return response()->json(['message' => 'Successfully logged out']);
}
public function refresh()
{
return $this->respondWithToken(JWTAuth::refresh());
}
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => JWTAuth::factory()->getTTL() * 60,
'user' => auth()->user(),
]);
}
}
Building the Login Component in Vue.js
We create a login component to handle user login requests.
<template>
<div class="row justify-content-center">
<div class="col-xl-10 col-lg-12 col-md-9">
<div class="card shadow-sm my-5">
<div class="card-body p-0">
<div class="row">
<div class="col-lg-12">
<div class="login-form">
<div class="text-center">
<h1 class="h4 text-gray-900 mb-4">Login</h1>
</div>
<form @submit.prevent="login">
<div class="form-group">
<input
type="email"
v-model="form.email"
class="form-control"
id="exampleInputEmail"
aria-describedby="emailHelp"
placeholder="Enter Email Address"
/>
<span v-if="errors.email" class="text-danger">{{ errors.email[0] }}</span>
</div>
<div class="form-group">
<input
type="password"
v-model="form.password"
class="form-control"
id="exampleInputPassword"
placeholder="Password"
/>
<span v-if="errors.password" class="text-danger">{{ errors.password[0] }}</span>
</div>
<div class="form-group">
<div class="custom-control custom-checkbox small" style="line-height: 1.5rem;">
<input type="checkbox" class="custom-control-input" id="customCheck" />
<label class="custom-control-label" for="customCheck">Remember Me</label>
</div>
</div>
<div class="form-group">
<button :disabled="loading" type="submit" class="btn btn-primary btn-block">
<span v-if="loading">Processing...</span>
<span v-else>Login</span>
</button>
</div>
<div v-if="errorMessage" class="alert alert-danger" role="alert">
{{ errorMessage }}
</div>
</form>
<div class="text-center">
<RouterLink class="font-weight-bold small" to="/register">Create an Account!</RouterLink>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { RouterLink } from 'vue-router';
export default {
name: 'Loginpage',
data() {
return {
form: {
email: '',
password: '',
},
successMessage: '',
errorMessage: '',
errors: {},
loading: false,
};
},
methods: {
async login() {
try {
this.loading = true;
const response = await axios.post('/api/auth/login', this.form);
if (response.status === 200) {
// Store the token and user name in localStorage
const token = response.data.access_token;
localStorage.setItem('token', token);
localStorage.setItem('user_name', response.data.user.name);
Toast.fire({
icon: 'success',
title: 'Signed in successfully',
});
// Update Axios headers
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
// Redirect to dashboard or next page
this.$router.push('/dashboard');
this.errorMessage = '';
this.errors = {};
// Clear the form
this.form.email = '';
this.form.password = '';
}
} catch (error) {
if (error.response && error.response.status === 422) {
this.errors = error.response.data.errors;
this.errorMessage = 'Please fix the errors above and try again.';
} else {
this.errorMessage = error.response ? error.response.data.error : 'An error occurred.';
console.error(error);
}
} finally {
this.loading = false;
}
},
},
};
</script>
<style scoped>
/* Add any custom styles here */
</style>
methods: {
async categoryInsert() {
this.loading = true;
const token = localStorage.getItem('token'); // Retrieve the token from localStorage
try {
const response = await axios.post('/api/category', this.form, {
headers: {
'Authorization': `Bearer ${token}`, // Add the Authorization header
'Content-Type': 'application/json'
}
});
if (response.data.success) { // Check if the response is successful
this.$router.push({ name: "ViewCategory" });
Notification.success(response.data.message); // Display success message
} else {
Notification.error(response.data.message);
}
} catch (error) {
if (error.response) {
if (error.response.status === 400) {
Notification.error(error.response.data.message);
}
this.errors = error.response.data.errors || {};
} else {
console.error('An error occurred:', error.message);
}
} finally {
this.loading = false;
}
},
},Explanation:
Authorization Header: The Authorization header is added to the request using the headers property in the axios.post method. The token is retrieved from localStorage and included as Bearer ${token}.
Content-Type Header: The Content-Type header is set to 'application/json' to indicate that the request body is in JSON format.
Error Handling: The method still includes error handling to manage any issues that arise during the API request.
By following these steps, you've successfully set up JWT authentication in your Laravel and Vue.js application. This includes configuring Axios for API requests, securing routes with middleware, creating an authentication controller, and building a login component. This setup ensures secure and efficient user authentication, enhancing the overall functionality of your application.
0 Comments