Skip to content

Commit a8bfaaa

Browse files
committed
Part 10 - Vue Authentication
1 parent a6c9896 commit a8bfaaa

File tree

7 files changed

+272
-11
lines changed

7 files changed

+272
-11
lines changed

src/components/auth/Login.vue

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
<template>
2-
<div class="flex-center">
3-
Content for Login Page
2+
<div class="login-form">
3+
<h2 class="login-heading">Login</h2>
4+
<form action="#" @submit.prevent="login">
5+
6+
<div class="form-control">
7+
<label for="email">Username/Email</label>
8+
<input type="email" name="username" id="username" class="login-input" v-model="username">
9+
</div>
10+
11+
<div class="form-control mb-more">
12+
<label for="password">Password</label>
13+
<input type="password" name="password" id="password" class="login-input" v-model="password">
14+
</div>
15+
16+
<div class="form-control">
17+
<button type="submit" class="btn-submit">Login</button>
18+
</div>
19+
20+
</form>
421
</div>
522
</template>
23+
24+
<script>
25+
export default {
26+
name: 'login',
27+
data() {
28+
return {
29+
username: '',
30+
password: '',
31+
}
32+
},
33+
methods: {
34+
login() {
35+
this.$store.dispatch('retrieveToken', {
36+
username: this.username,
37+
password: this.password,
38+
})
39+
.then(response => {
40+
this.$router.push({ name: 'todo' })
41+
})
42+
}
43+
}
44+
}
45+
</script>
46+

src/components/auth/Logout.vue

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<template>
2+
3+
</template>
4+
5+
<script>
6+
export default {
7+
created() {
8+
this.$store.dispatch('destroyToken')
9+
.then(response => {
10+
this.$router.push({ name: 'home' })
11+
})
12+
}
13+
}
14+
</script>
15+

src/components/auth/Register.vue

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,51 @@
11
<template>
2-
<div class="flex-center">
3-
Content for Register Page
2+
<div class="login-form">
3+
<h2 class="login-heading">Register</h2>
4+
<form action="#" @submit.prevent="register">
5+
6+
<div class="form-control">
7+
<label for="name">Name</label>
8+
<input type="text" name="name" id="name" class="login-input" v-model="name">
9+
</div>
10+
11+
<div class="form-control">
12+
<label for="email">Email</label>
13+
<input type="email" name="email" id="email" class="login-input" v-model="email">
14+
</div>
15+
16+
<div class="form-control mb-more">
17+
<label for="password">Password</label>
18+
<input type="password" name="password" id="password" class="login-input" v-model="password">
19+
</div>
20+
21+
<div class="form-control">
22+
<button type="submit" class="btn-submit">Create Account</button>
23+
</div>
24+
25+
</form>
426
</div>
527
</template>
28+
29+
<script>
30+
export default {
31+
data() {
32+
return {
33+
name: '',
34+
email: '',
35+
password: '',
36+
}
37+
},
38+
methods: {
39+
register() {
40+
this.$store.dispatch('register', {
41+
name: this.name,
42+
email: this.email,
43+
password: this.password,
44+
})
45+
.then(response => {
46+
this.$router.push({ name: 'login' })
47+
})
48+
}
49+
}
50+
}
51+
</script>

src/components/layouts/Master.vue

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
<li><router-link :to="{ name: 'home' }">Home</router-link></li>
55
<li><router-link :to="{ name: 'todo' }">App</router-link></li>
66
<li><router-link :to="{ name: 'about' }">About</router-link></li>
7-
<li><router-link :to="{ name: 'login' }">Login</router-link></li>
8-
<li><router-link :to="{ name: 'register' }">Register</router-link></li>
7+
<li v-if="!loggedIn"><router-link :to="{ name: 'login' }">Login</router-link></li>
8+
<li v-if="!loggedIn"><router-link :to="{ name: 'register' }">Register</router-link></li>
9+
<li v-if="loggedIn"><router-link :to="{ name: 'logout' }">Logout</router-link></li>
910

1011
</ul>
1112

@@ -15,11 +16,15 @@
1516

1617
<script>
1718
export default {
18-
19+
computed: {
20+
loggedIn() {
21+
return this.$store.getters.loggedIn
22+
}
23+
}
1924
}
2025
</script>
2126

22-
<style>
27+
<style lang="scss">
2328
2429
* {
2530
box-sizing: border-box;
@@ -62,5 +67,54 @@ export default {
6267
text-transform: uppercase;
6368
}
6469
70+
// Auth Pages
71+
72+
label {
73+
display: block;
74+
margin-bottom: 4px;
75+
}
76+
77+
.login-heading {
78+
margin-bottom: 16px;
79+
}
80+
81+
.form-control {
82+
margin-bottom: 24px;
83+
}
84+
85+
.mb-more {
86+
margin-bottom: 42px;
87+
}
88+
89+
.login-form {
90+
max-width: 500px;
91+
margin: auto;
92+
}
93+
94+
.login-input {
95+
width: 100%;
96+
font-size: 16px;
97+
padding: 12px 16px;
98+
outline: 0;
99+
border-radius: 3px;
100+
border: 1px solid lightgrey;
101+
}
102+
103+
.btn-submit {
104+
width: 100%;
105+
padding: 14px 12px;
106+
font-size: 18px;
107+
font-weight: bold;
108+
background: #60BD4F;
109+
color: white;
110+
border-radius: 3px;
111+
cursor: pointer;
112+
113+
&:hover {
114+
background: darken(#60BD4F, 10%);
115+
}
116+
117+
}
118+
65119
</style>
66120

src/main.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ const router = new VueRouter({
1616
mode: 'history'
1717
})
1818

19+
router.beforeEach((to, from, next) => {
20+
if (to.matched.some(record => record.meta.requiresAuth)) {
21+
if (!store.getters.loggedIn) {
22+
next({
23+
name: 'login',
24+
})
25+
} else {
26+
next()
27+
}
28+
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
29+
if (store.getters.loggedIn) {
30+
next({
31+
name: 'todo',
32+
})
33+
} else {
34+
next()
35+
}
36+
} else {
37+
next()
38+
}
39+
})
40+
1941
/* eslint-disable no-new */
2042
new Vue({
2143
el: '#app',

src/routes.js

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import App from './App'
22
import LandingPage from './components/marketing/LandingPage'
33
import About from './components/marketing/About'
44
import Login from './components/auth/Login'
5+
import Logout from './components/auth/Logout'
56
import Register from './components/auth/Register'
67

78
const routes = [
@@ -13,7 +14,10 @@ const routes = [
1314
{
1415
path: '/todo',
1516
name: 'todo',
16-
component: App
17+
component: App,
18+
meta: {
19+
requiresAuth: true,
20+
}
1721
},
1822
{
1923
path: '/about',
@@ -23,12 +27,23 @@ const routes = [
2327
{
2428
path: '/login',
2529
name: 'login',
26-
component: Login
30+
component: Login,
31+
meta: {
32+
requiresVisitor: true,
33+
}
2734
},
2835
{
2936
path: '/register',
3037
name: 'register',
31-
component: Register
38+
component: Register,
39+
meta: {
40+
requiresVisitor: true,
41+
}
42+
},
43+
{
44+
path: '/logout',
45+
name: 'logout',
46+
component: Logout
3247
}
3348
]
3449

src/store/store.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@ axios.defaults.baseURL = 'http://todo-laravel.test/api'
77

88
export const store = new Vuex.Store({
99
state: {
10+
token: localStorage.getItem('access_token') || null,
1011
filter: 'all',
1112
todos: [],
1213
},
1314
getters: {
15+
loggedIn(state) {
16+
return state.token !== null
17+
},
1418
remaining(state) {
1519
return state.todos.filter(todo => !todo.completed).length
1620
},
@@ -64,9 +68,73 @@ export const store = new Vuex.Store({
6468
},
6569
retrieveTodos(state, todos) {
6670
state.todos = todos
71+
},
72+
retrieveToken(state, token) {
73+
state.token = token
74+
},
75+
destroyToken(state) {
76+
state.token = null
6777
}
6878
},
6979
actions: {
80+
register(context, data) {
81+
return new Promise((resolve, reject) => {
82+
axios.post('/register', {
83+
name: data.name,
84+
email: data.email,
85+
password: data.password,
86+
})
87+
.then(response => {
88+
resolve(response)
89+
})
90+
.catch(error => {
91+
reject(error)
92+
})
93+
})
94+
},
95+
destroyToken(context) {
96+
axios.defaults.headers.common['Authorization'] = 'Bearer ' + context.state.token
97+
98+
if (context.getters.loggedIn) {
99+
return new Promise((resolve, reject) => {
100+
axios.post('/logout')
101+
.then(response => {
102+
localStorage.removeItem('access_token')
103+
context.commit('destroyToken')
104+
resolve(response)
105+
// console.log(response);
106+
// context.commit('addTodo', response.data)
107+
})
108+
.catch(error => {
109+
localStorage.removeItem('access_token')
110+
context.commit('destroyToken')
111+
reject(error)
112+
})
113+
})
114+
}
115+
},
116+
retrieveToken(context, credentials) {
117+
118+
return new Promise((resolve, reject) => {
119+
axios.post('/login', {
120+
username: credentials.username,
121+
password: credentials.password,
122+
})
123+
.then(response => {
124+
const token = response.data.access_token
125+
126+
localStorage.setItem('access_token', token)
127+
context.commit('retrieveToken', token)
128+
resolve(response)
129+
// console.log(response);
130+
// context.commit('addTodo', response.data)
131+
})
132+
.catch(error => {
133+
console.log(error)
134+
reject(error)
135+
})
136+
})
137+
},
70138
retrieveTodos(context) {
71139
axios.get('/todos')
72140
.then(response => {

0 commit comments

Comments
 (0)