Secure Your Website Auth by Implementing JWT Authentication
In today's web development landscape, securing your application is paramount. JSON Web Tokens (JWT) have become a popular choice for implementing authentication in modern web applications. In this tutorial, we'll walk through the process of implementing JWT authentication in a Vue.js application, providing you with a robust and secure solution for user authentication.
Prerequisites
Before we begin, make sure you have the following:
- - Node.js and npm installed on your machine
- - Basic knowledge of Vue.js and JavaScript
- - Familiarity with HTTP requests and RESTful APIs
Setting Up the Project
First, let's create a new Vue.js project using the Vue CLI:
vue create vue-jwt-auth cd vue-jwt-auth
Next, we'll install the necessary dependencies:
npm install axios vuex vue-router jsonwebtoken
Project Structure
Our project structure will look like this:
src/ ├── components/ │ ├── Login.vue │ ├── Register.vue │ └── Dashboard.vue ├── router/ │ └── index.js ├── store/ │ └── index.js ├── services/ │ └── auth.service.js ├── App.vue └── main.js
Implementing the Vuex Store
Let's start by setting up our Vuex store to manage the authentication state:
import { createStore } from 'vuex' import AuthService from '../services/auth.service' export default createStore({ state: { user: null, token: localStorage.getItem('token') || null, }, mutations: { setUser(state, user) { state.user = user }, setToken(state, token) { state.token = token localStorage.setItem('token', token) }, logout(state) { state.user = null state.token = null localStorage.removeItem('token') }, }, actions: { async login({ commit }, credentials) { try { const response = await AuthService.login(credentials) commit('setUser', response.user) commit('setToken', response.token) return true } catch (error) { console.error('Login failed:', error) return false } }, async register({ commit }, userData) { try { const response = await AuthService.register(userData) commit('setUser', response.user) commit('setToken', response.token) return true } catch (error) { console.error('Registration failed:', error) return false } }, logout({ commit }) { commit('logout') }, }, getters: { isAuthenticated: state => !!state.token, currentUser: state => state.user, }, })
Creating the Authentication Service
Now, let's create the auth.service.js
file to handle API calls:
import axios from 'axios' const API_URL = 'https://your-api-url.com/api/auth/' class AuthService { async login(credentials) { const response = await axios.post(API_URL + 'login', credentials) return response.data } async register(userData) { const response = await axios.post(API_URL + 'register', userData) return response.data } } export default new AuthService()
Implementing Vue Router
Let's set up our router with protected routes:
import { createRouter, createWebHistory } from 'vue-router' import Login from '../components/Login.vue' import Register from '../components/Register.vue' import Dashboard from '../components/Dashboard.vue' import store from '../store' const routes = [ { path: '/login', component: Login }, { path: '/register', component: Register }, { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } }, ] const router = createRouter({ history: createWebHistory(), routes, }) router.beforeEach((to, from, next) => { if (to.matched.some(record => record.meta.requiresAuth)) { if (!store.getters.isAuthenticated) { next('/login') } else { next() } } else { next() } }) export default router
Creating Vue Components
Now, let's create our Vue components. We'll start with the Login component:
<template> <div class="login"> <h2>Login</h2> <form @submit.prevent="handleSubmit"> <div> <label for="email">Email:</label> <input type="email" id="email" v-model="email" required> </div> <div> <label for="password">Password:</label> <input type="password" id="password" v-model="password" required> </div> <button type="submit">Login</button> </form> </div> </template> <script> import { ref } from 'vue' import { useStore } from 'vuex' import { useRouter } from 'vue-router' export default { setup() { const store = useStore() const router = useRouter() const email = ref('') const password = ref('') const handleSubmit = async () => { const success = await store.dispatch('login', { email: email.value, password: password.value }) if (success) { router.push('/dashboard') } else { alert('Login failed. Please check your credentials.') } } return { email, password, handleSubmit } } } </script>
Next, let's create the Register component:
<template> <div class="register"> <h2>Register</h2> <form @submit.prevent="handleSubmit"> <div> <label for="name">Name:</label> <input type="text" id="name" v-model="name" required> </div> <div> <label for="email">Email:</label> <input type="email" id="email" v-model="email" required> </div> <div> <label for="password">Password:</label> <input type="password" id="password" v-model="password" required> </div> <button type="submit">Register</button> </form> </div> </template> <script> import { ref } from 'vue' import { useStore } from 'vuex' import { useRouter } from 'vue-router' export default { setup() { const store = useStore() const router = useRouter() const name = ref('') const email = ref('') const password = ref('') const handleSubmit = async () => { const success = await store.dispatch('register', { name: name.value, email: email.value, password: password.value }) if (success) { router.push('/dashboard') } else { alert('Registration failed. Please try again.') } } return { name, email, password, handleSubmit } } } </script>
Finally, let's create a simple Dashboard component:
<template> <div class="dashboard"> <h2>Welcome, {{ user.name }}!</h2> <p>This is your secure dashboard.</p> <button @click="handleLogout">Logout</button> </div> </template> <script> import { computed } from 'vue' import { useStore } from 'vuex' import { useRouter } from 'vue-router' export default { setup() { const store = useStore() const router = useRouter() const user = computed(() => store.getters.currentUser) const handleLogout = () => { store.dispatch('logout') router.push('/login') } return { user, handleLogout } } } </script>
Updating App.vue
Let's update our App.vue
to include navigation and router view:
<template> <div id="app"> <nav> <router-link to="/login" v-if="!isAuthenticated">Login</router-link> <router-link to="/register" v-if="!isAuthenticated">Register</router-link> <router-link to="/dashboard" v-if="isAuthenticated">Dashboard</router-link> </nav> <router-view></router-view> </div> </template> <script> import { computed } from 'vue' import { useStore } from 'vuex' export default { setup() { const store = useStore() const isAuthenticated = computed(() => store.getters.isAuthenticated) return { isAuthenticated } } } </script>
Implementing JWT Interceptors
To ensure our JWT is sent with every request, we'll set up an Axios interceptor. Create a new file src/services/api.js
:
import axios from 'axios' import store from '../store' const api = axios.create({ baseURL: 'https://your-api-url.com/api', }) api.interceptors.request.use( (config) => { const token = store.state.token if (token) { config.headers['Authorization'] = `Bearer ${token}` } return config }, (error) => { return Promise.reject(error) } ) api.interceptors.response.use( (response) => response, async (error) => { if (error.response.status === 401) { store.dispatch('logout') router.push('/login') } return Promise.reject(error) } ) export default api
Conclusion
Congratulations! You've successfully implemented JWT authentication in your Vue.js application. This setup provides a solid foundation for securing your app, managing user sessions, and protecting routes.
Remember to always follow security best practices:
- 1. Store JWTs securely (preferably in memory or secure HTTP-only cookies)
- 2. Implement token refresh mechanisms for long-lived sessions
- 3. Use HTTPS to encrypt data in transit
- 4. Validate and sanitize all user inputs
- 5. Regularly update your dependencies to patch any security vulnerabilities
By following these steps and best practices, you've taken a significant step towards creating a secure and robust Vue.js application with JWT authentication.
Happy coding!