Vuejs Rest authentication with axios + jwt + Vuetify | extrovert.dev -->

Vuejs Rest authentication with axios + jwt + Vuetify

Vuejs Rest authentication with  axios + jwt + Vuetify
Sunday, August 9, 2020
We rocked in our previous posts, where we successfully built a backend for our music streaming app using Django, Django rest framework and enhanced security using JWT. In this, we will be using vuejs to build a beautiful frontend for our music API. However, this tutorial should work for any backend you built. Let's not make this posting boring with traditional UI instead, let's try to make our music streaming app look professional. we will be using vuetify for this, a material design UI framework for vuejs.
Music Streaming app with vuejs

Let's get started
vue create ronix //using vue_cli
Select manual preset and select vue router, vuex, Babel
cd ronix
vue add vuetify //adding vuetify using Vue CLI
Vuex documentation says " Vuex is a state management pattern + library for Vue.js applications. It serves as a centralized store for all the components in an application, with rules ensuring that the state can only be mutated in a predictable fashion". For us, we do not want our users to login every time. So we use vuex to manage user's state whether he/she is logged in or not according to the life cycle of jwt.
Let's create our homepage so that the user can register and login.
<template lang="html" class="primary_bg">
<div class="primary_bg">
<v-container fluid class="mb-15">
<v-layout row>
<v-flex md6 lg6 sm12 xs12 x6>
<img src="../assets/Chilling-at-Home/main_files/chilling.svg" width="95%">
</v-flex>
<v-flex md6 lg6 sm12 xs12 x6 pl-3>
<h1 class="white--text font-weight-medium mt-16 d-sm-pl-8 d-xs-pl-6">Roni<span class="red--text">X</span></h1>
<h2 class="white--text d-sm-pl-6">Discover great music <span class="yellow--text">that rules out </span></h2>
<p class="white--text d-sm-pl-6">Music is eternal that has limitless potential in grasping and treating of great forces on the earth.However to get started login to your existing account or create one if you dont have.</p>
<v-dialog v-model="dialog" scrollable max-width="300px">
<!-- Login popup modal -->
<template v-slot:activator="{ on, attrs }">
<v-btn
color="black--text white"
dark
v-bind="attrs"
v-on="on"
>
Login
</v-btn>
</template>
<v-card>
<v-card-title>Login</v-card-title>
<v-divider></v-divider>
<v-card-text class="mt-2">
<v-text-field
v-model="email"
solo
label="Email"
prepend-inner-icon="mdi-email"
></v-text-field>
<!-- Allowing user to toggle visibility of password -->
<v-text-field
v-model="password"
:type="show1 ? 'text' : 'password'"
name="input-10-1"
@click:append="show1 = !show1"
solo
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
label="Password"
prepend-inner-icon="mdi-account-key"
></v-text-field>
</v-card-text>
<v-divider></v-divider>
<v-card-actions>
<v-btn color="blue darken-1" text @click="dialog = false">Close</v-btn>
<v-btn color="blue darken-1" text @click.prevent="authenticate">login</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<v-btn class="ml-2" outlined color="white"> <router-link to="/signup" tag='span'>Create account</router-link> </v-btn>
<p><v-chip
color="black"
text-color="white"
class="mt-12"
>
<v-avatar
left
class="red darken-4"
>
<v-icon size="16">mdi-newspaper-plus</v-icon>
</v-avatar>
Updated with Private Music streams >
</v-chip></p>
</v-flex>
</v-layout>
</v-container>
<v-footer
color="primary_bg"
padless
>
<v-row
justify="center"
no-gutters
>
<v-col
class="text-center white--text"
cols="12"
>
{{ new Date().getFullYear() }} — <strong>Ronix music</strong>
<p>Glammingspace</p>
</v-col>
</v-row>
</v-footer>
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
show1: false,
email: '',
password: '',
dialog: false,
}
},
methods: {
authenticate () {
const payload = {
email: this.email,
password: this.password
}
axios.post(this.$store.state.endpoints.obtainJWT, payload)
.then((response) => {
this.$store.commit('updateToken', response.data.token)
// get and set auth user
const base = {
baseURL: this.$store.state.endpoints.baseUrl,
headers: {
// Set your Authorization to 'JWT' !!
Authorization: `JWT ${this.$store.state.jwt}`,
'Content-Type': 'application/json'
},
xhrFields: {
withCredentials: true
}
}
const axiosInstance = axios.create(base)
axiosInstance({
url: "/rest-auth/user/",
method: "get",
params: {}
})
.then((response) => {
this.$store.commit("setAuthUser",
{authUser: response.data, isAuthenticated: true}
)
this.$router.push({name: 'Songs'})
})
})
.catch((error) => {
console.log(error);
console.debug(error);
console.dir(error);
})
}
}
}
</script>
<style lang="css" scoped>
.primary_bg{
background-color:#9921e8;
background-image:linear-gradient(315deg, #9921e8 0%, #5f72be 74%);
}
.v-text-field{
width: 400px;
}
</style>
view raw Portfolio.vue hosted with ❤ by GitHub

Now we handle our user state using vuex
//add to store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'
Vue.use(Vuex)
// Make Axios good with django csrf
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = 'X-CSRFToken'
export default new Vuex.Store({
state: {
authUser: {},
isAuthenticated: false,
jwt: localStorage.getItem('token'),
APIData:'',
endpoints: {
// Change these with your endpoints.
obtainJWT: 'https://127.0.0.1/api/v1/auth/obtain_token/',
refreshJWT: 'https://127.0.0.1/api/v1/auth/refresh_token/',
baseUrl: 'https://127.0.0.1/'
}
},
mutations: {
setAuthUser(state, {
authUser,
isAuthenticated
}) {
Vue.set(state, 'authUser', authUser)
Vue.set(state, 'isAuthenticated', isAuthenticated)
},
//You should find a better alternative to storing in local storage
updateToken(state, newToken) {
localStorage.setItem('token', newToken);
state.jwt = newToken;
},
removeToken(state) {
localStorage.removeItem('token');
state.jwt = null;
}
}
})
view raw storeindex.js hosted with ❤ by GitHub

Let's add beautiful navbar in views/App.vue
<template>
<v-app>
<v-app-bar
app
color="primary_bg"
dark
dense
>
<div class="d-flex align-center">
<v-toolbar-title>Ronix</v-toolbar-title>
</div>
<v-spacer></v-spacer>
<v-btn icon>
<router-link to="/" tag="icon"><v-icon>mdi-home</v-icon></router-link>
</v-btn>
<v-btn icon>
<router-link to="/songs" tag="icon"><v-icon>mdi-playlist-music</v-icon></router-link>
</v-btn>
<v-btn icon>
<v-icon>mdi-magnify</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-account-circle</v-icon>
</v-btn>
</v-app-bar>
<v-content>
<router-view></router-view>
</v-content>
</v-app>
</template>
<script>
export default {
name: 'App',
components: {
},
data: () => ({
links: [
'Home',
'About Us',
'Team',
'Services',
'Blog',
'Contact Us',
],
}),
};
</script>
<style lang="css">
.primary_bg{
background-color:#9921e8;
background-image:linear-gradient(315deg, #9921e8 0%, #5f72be 74%);
}
</style>
view raw App.vue hosted with ❤ by GitHub

Let's create a signup vue in views
<!--add to views/signup.vue-->
<template lang="html">
<v-form>
<v-container>
<v-row>
<v-col cols="12" sm="12" md="12" lg="6">
<p class="mx-auto" justify-center>Login</p>
<v-text-field
v-model="email"
solo
label="Email"
prepend-inner-icon="mdi-email"
></v-text-field>
<v-text-field
v-model="password1"
:type="show1 ? 'text' : 'password'"
name="input-10-1"
@click:append="show1 = !show1"
solo
:append-icon="show1 ? 'mdi-eye' : 'mdi-eye-off'"
label="Password"
prepend-inner-icon="mdi-lock"
></v-text-field>
<v-text-field
v-model="password2"
:type="show2 ? 'text' : 'password'"
name="input-10-1"
@click:append="show2 = !show2"
solo
:append-icon="show2 ? 'mdi-eye' : 'mdi-eye-off'"
label="Repeat Password"
prepend-inner-icon="mdi-lock-reset"
></v-text-field>
<v-btn
:loading="loading3"
:disabled="loading3"
color="blue-grey"
class="ma-2 white--text"
@click.prevent="register"
>
signup
<v-icon right dark>mdi-account</v-icon>
</v-btn>
<p class="ma-3 ma-md-1">Have an account ?</p>
<v-btn> <router-link to="/" tag='span'>Login</router-link> </v-btn>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
email: '',
password1: '',
password2: '',
}
},
methods: {
register () {
const payload = {
email: this.email,
password1: this.password1,
password2:this.password2
}
axios.post('https://127.0.0.1/rest-auth/registration/', payload)
}
}
}
</script>
<style lang="css">
</style>
view raw signup.vue hosted with ❤ by GitHub

Now create our songs component in Components
<!-- Components/Songs.vue-->
<template>
<v-container>
<v-layout wrap>
<v-flex xs4 md2
v-for="(item, index) in posts"
:key="index"
mb-16>
<v-card
class="mx-auto"
max-width="100"
max-height="90"
>
<v-img
class="white--text align-end rounded-lg"
height="110px"
width="120px"
:src="item.album"
>
<!--playing using native audio player-->
<audio :src="item.song" controls></audio>
</v-img>
<!-- <audio ref="audiofile" :src="item.song" controls></audio> -->
<div class="overflow_prevent">{{ item.title}}</div>
<div class="caption overflow_prevent" style="line-height: 100%;">{{ item.artist}}</div>
</v-card>
</v-flex>
</v-layout>
</v-container>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
posts: [],
errors: [],
file: '',
multiLine: true,
snackbar: true,
}
},
methods:{
play() {
console.log('playing')
this.audio.play();
}
},
created() {
this.audio = document.getElementById('audio');
},
mounted() {
const base = {
baseURL: this.$store.state.endpoints.baseUrl,
headers: {
// Set your Authorization to 'JWT'!!!
Authorization: `JWT ${this.$store.state.jwt}`,
'Content-Type': 'application/json'
},
xhrFields: {
withCredentials: true
}
}
const axiosInstance = axios.create(base)
axiosInstance({
url: "https://127.0.0.1/api/v1/songs/",
method: "get",
params: {}
})
.then((response) => {
this.posts = response.data.results
})
.catch(e => {
this.errors.push(e)
})
}
}
</script>
<style lang="css">
.overflow_prevent{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
view raw Songs.vue hosted with ❤ by GitHub

However, this needs lots of improvement like building up a music player instead of a native media player, playlist etc.. Stay connected with this blog we will make updates.



Finally, let's make routes to pages using vue router

import Vue from 'vue'
import VueRouter from 'vue-router'
import Signup from '../views/Signup.vue'
import Portfolio from '@/components/Portfolio';
import Songs from '@/components/Songs'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Portfolio
},
{
path: '/signup',
name: 'Signup',
component: Signup
},
{
path: '/songs',
name: 'Songs',
component: Songs
},
/*{
path: '',
name: '',
component: ,
}*/
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router
view raw routerindex.js hosted with ❤ by GitHub

Yay! we have built a music streaming app entirely from scratch using Django, DRF and vuejs. Thanks for your huge support! As always proud to be a Dev 👨‍💻 .

Kindly share this post with your fellow Devs!

wb_incandescent You may like

2 Responses to Vuejs Rest authentication with axios + jwt + Vuetify