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!