├── .gitignore
├── README.md
├── babel.config.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ └── logo.png
├── components
│ ├── BoardAdmin.vue
│ ├── BoardModerator.vue
│ ├── BoardUser.vue
│ ├── Home.vue
│ ├── Login.vue
│ ├── Profile.vue
│ └── Register.vue
├── main.js
├── plugins
│ └── font-awesome.js
├── router.js
├── services
│ ├── auth-header.js
│ ├── auth.service.js
│ └── user.service.js
└── store
│ ├── auth.module.js
│ └── index.js
├── vue-3-authentication-jwt-example-flow.png
├── vue-3-authentication-jwt-example-vuex-form-validation.png
├── vue-3-authentication-jwt-example-vuex-user-login.png
├── vue-3-authentication-jwt-example-vuex-user-registration.png
└── vue.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | pnpm-debug.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Vue 3 Authentication with JWT, Vuex and Vue Router
2 |
3 | ## Flow for User Registration and User Login
4 |
5 | 
6 |
7 | - Signup Page:
8 |
9 | 
10 |
11 | - Form Validation could look like this:
12 |
13 | 
14 |
15 | - Login Page & Profile Page (for successful Login):
16 |
17 | 
18 |
19 | For instruction, please visit:
20 | > [Vue 3 Authentication & Authorization with JWT, Vuex and Vue Router](https://bezkoder.com/vue-3-authentication-jwt/)
21 |
22 | ## Note:
23 | Open `src/services/auth-header.js` and modify `return` statement for appropriate back-end.
24 |
25 | ```js
26 | export default function authHeader() {
27 | let user = JSON.parse(localStorage.getItem('user'));
28 |
29 | if (user && user.accessToken) {
30 | return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
31 | // return { 'x-access-token': user.accessToken }; // for Node.js Express back-end
32 | } else {
33 | return {};
34 | }
35 | }
36 | ```
37 |
38 | Related Posts:
39 | > [Vue 2 JWT Authentication with Vuex and Vue Router](https://bezkoder.com/jwt-vue-vuex-authentication/)
40 |
41 | > [Using Typescript](https://bezkoder.com/vuex-typescript-jwt-auth/)
42 |
43 | > [Vue 3 CRUD example with Axios and Vue Router](https://bezkoder.com/vue-3-crud/)
44 |
45 | Fullstack with Spring Boot Back-end:
46 | > [Spring Boot + Vue.js: Authentication with JWT & Spring Security Example](https://bezkoder.com/spring-boot-vue-js-authentication-jwt-spring-security/)
47 |
48 | Fullstack with Node.js Express Back-end:
49 | > [Node.js Express + Vue.js: JWT Authentication & Authorization example](https://bezkoder.com/node-express-vue-jwt-auth/)
50 |
51 | Fullstack CRUD:
52 | > [Vue.js + Node.js + Express + MySQL example](https://bezkoder.com/vue-js-node-js-express-mysql-crud-example/)
53 |
54 | > [Vue.js + Node.js + Express + PostgreSQL example](https://bezkoder.com/vue-node-express-postgresql/)
55 |
56 | > [Vue.js + Node.js + Express + MongoDB example](https://bezkoder.com/vue-node-express-mongodb-mevn-crud/)
57 |
58 | > [Vue.js + Spring Boot + MySQL/PostgreSQL example](https://bezkoder.com/spring-boot-vue-js-crud-example/)
59 |
60 | > [Vue.js + Spring Boot + MongoDB example](https://bezkoder.com/spring-boot-vue-mongodb/)
61 |
62 | > [Vue.js + Django example](https://bezkoder.com/django-vue-js-rest-framework/)
63 |
64 | Integration (run on same server/port):
65 | > [Integrate Vue.js with Spring Boot](https://bezkoder.com/integrate-vue-spring-boot/)
66 |
67 | > [Integrate Vue App with Node.js Express](https://bezkoder.com/serve-vue-app-express/)
68 |
69 |
70 | ## Project setup
71 | ```
72 | npm install
73 | ```
74 |
75 | ### Compiles and hot-reloads for development
76 | ```
77 | npm run serve
78 | ```
79 |
80 | ### Compiles and minifies for production
81 | ```
82 | npm run build
83 | ```
84 |
85 | ### Lints and fixes files
86 | ```
87 | npm run lint
88 | ```
89 |
90 | ### Customize configuration
91 | See [Configuration Reference](https://cli.vuejs.org/config/).
92 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-3-authentication-jwt",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@fortawesome/fontawesome-svg-core": "^1.2.35",
12 | "@fortawesome/free-solid-svg-icons": "^5.15.3",
13 | "@fortawesome/vue-fontawesome": "^3.0.0-3",
14 | "axios": "^0.21.1",
15 | "bootstrap": "^4.6.0",
16 | "core-js": "^3.6.5",
17 | "jquery": "^3.6.0",
18 | "popper.js": "^1.16.1",
19 | "vee-validate": "^4.3.5",
20 | "vue": "^3.0.0",
21 | "vue-router": "^4.0.6",
22 | "vuex": "^4.0.0",
23 | "yup": "^0.32.9"
24 | },
25 | "devDependencies": {
26 | "@vue/cli-plugin-babel": "~4.5.0",
27 | "@vue/cli-plugin-eslint": "~4.5.0",
28 | "@vue/cli-service": "~4.5.0",
29 | "@vue/compiler-sfc": "^3.0.0",
30 | "babel-eslint": "^10.1.0",
31 | "eslint": "^6.7.2",
32 | "eslint-plugin-vue": "^7.0.0"
33 | },
34 | "eslintConfig": {
35 | "root": true,
36 | "env": {
37 | "node": true
38 | },
39 | "extends": [
40 | "plugin:vue/vue3-essential",
41 | "eslint:recommended"
42 | ],
43 | "parserOptions": {
44 | "parser": "babel-eslint"
45 | },
46 | "rules": {}
47 | },
48 | "browserslist": [
49 | "> 1%",
50 | "last 2 versions",
51 | "not dead"
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= htmlWebpackPlugin.options.title %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
85 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/src/assets/logo.png
--------------------------------------------------------------------------------
/src/components/BoardAdmin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
36 |
--------------------------------------------------------------------------------
/src/components/BoardModerator.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
36 |
--------------------------------------------------------------------------------
/src/components/BoardUser.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
36 |
--------------------------------------------------------------------------------
/src/components/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
36 |
--------------------------------------------------------------------------------
/src/components/Login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

9 |
37 |
38 |
39 |
40 |
41 |
96 |
97 |
135 |
--------------------------------------------------------------------------------
/src/components/Profile.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{currentUser.username}} Profile
6 |
7 |
8 |
9 | Token:
10 | {{currentUser.accessToken.substring(0, 20)}} ... {{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}}
11 |
12 |
13 | Id:
14 | {{currentUser.id}}
15 |
16 |
17 | Email:
18 | {{currentUser.email}}
19 |
20 |
Authorities:
21 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/Register.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

9 |
38 |
39 |
44 | {{ message }}
45 |
46 |
47 |
48 |
49 |
50 |
124 |
125 |
163 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 | import store from "./store";
5 | import "bootstrap";
6 | import "bootstrap/dist/css/bootstrap.min.css";
7 | import { FontAwesomeIcon } from './plugins/font-awesome'
8 |
9 | createApp(App)
10 | .use(router)
11 | .use(store)
12 | .component("font-awesome-icon", FontAwesomeIcon)
13 | .mount("#app");
14 |
--------------------------------------------------------------------------------
/src/plugins/font-awesome.js:
--------------------------------------------------------------------------------
1 | import { library } from "@fortawesome/fontawesome-svg-core";
2 | import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
3 | import {
4 | faHome,
5 | faUser,
6 | faUserPlus,
7 | faSignInAlt,
8 | faSignOutAlt,
9 | } from "@fortawesome/free-solid-svg-icons";
10 |
11 | library.add(faHome, faUser, faUserPlus, faSignInAlt, faSignOutAlt);
12 |
13 | export { FontAwesomeIcon };
14 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import { createWebHistory, createRouter } from "vue-router";
2 | import Home from "./components/Home.vue";
3 | import Login from "./components/Login.vue";
4 | import Register from "./components/Register.vue";
5 | // lazy-loaded
6 | const Profile = () => import("./components/Profile.vue")
7 | const BoardAdmin = () => import("./components/BoardAdmin.vue")
8 | const BoardModerator = () => import("./components/BoardModerator.vue")
9 | const BoardUser = () => import("./components/BoardUser.vue")
10 |
11 | const routes = [
12 | {
13 | path: "/",
14 | name: "home",
15 | component: Home,
16 | },
17 | {
18 | path: "/home",
19 | component: Home,
20 | },
21 | {
22 | path: "/login",
23 | component: Login,
24 | },
25 | {
26 | path: "/register",
27 | component: Register,
28 | },
29 | {
30 | path: "/profile",
31 | name: "profile",
32 | // lazy-loaded
33 | component: Profile,
34 | },
35 | {
36 | path: "/admin",
37 | name: "admin",
38 | // lazy-loaded
39 | component: BoardAdmin,
40 | },
41 | {
42 | path: "/mod",
43 | name: "moderator",
44 | // lazy-loaded
45 | component: BoardModerator,
46 | },
47 | {
48 | path: "/user",
49 | name: "user",
50 | // lazy-loaded
51 | component: BoardUser,
52 | },
53 | ];
54 |
55 | const router = createRouter({
56 | history: createWebHistory(),
57 | routes,
58 | });
59 |
60 | // router.beforeEach((to, from, next) => {
61 | // const publicPages = ['/login', '/register', '/home'];
62 | // const authRequired = !publicPages.includes(to.path);
63 | // const loggedIn = localStorage.getItem('user');
64 |
65 | // // trying to access a restricted page + not logged in
66 | // // redirect to login page
67 | // if (authRequired && !loggedIn) {
68 | // next('/login');
69 | // } else {
70 | // next();
71 | // }
72 | // });
73 |
74 | export default router;
--------------------------------------------------------------------------------
/src/services/auth-header.js:
--------------------------------------------------------------------------------
1 | export default function authHeader() {
2 | let user = JSON.parse(localStorage.getItem('user'));
3 |
4 | if (user && user.accessToken) {
5 | return { Authorization: 'Bearer ' + user.accessToken }; // for Spring Boot back-end
6 | // return { 'x-access-token': user.accessToken }; // for Node.js Express back-end
7 | } else {
8 | return {};
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/services/auth.service.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | const API_URL = 'http://localhost:8080/api/auth/';
4 |
5 | class AuthService {
6 | login(user) {
7 | return axios
8 | .post(API_URL + 'signin', {
9 | username: user.username,
10 | password: user.password
11 | })
12 | .then(response => {
13 | if (response.data.accessToken) {
14 | localStorage.setItem('user', JSON.stringify(response.data));
15 | }
16 |
17 | return response.data;
18 | });
19 | }
20 |
21 | logout() {
22 | localStorage.removeItem('user');
23 | }
24 |
25 | register(user) {
26 | return axios.post(API_URL + 'signup', {
27 | username: user.username,
28 | email: user.email,
29 | password: user.password
30 | });
31 | }
32 | }
33 |
34 | export default new AuthService();
35 |
--------------------------------------------------------------------------------
/src/services/user.service.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import authHeader from './auth-header';
3 |
4 | const API_URL = 'http://localhost:8080/api/test/';
5 |
6 | class UserService {
7 | getPublicContent() {
8 | return axios.get(API_URL + 'all');
9 | }
10 |
11 | getUserBoard() {
12 | return axios.get(API_URL + 'user', { headers: authHeader() });
13 | }
14 |
15 | getModeratorBoard() {
16 | return axios.get(API_URL + 'mod', { headers: authHeader() });
17 | }
18 |
19 | getAdminBoard() {
20 | return axios.get(API_URL + 'admin', { headers: authHeader() });
21 | }
22 | }
23 |
24 | export default new UserService();
25 |
--------------------------------------------------------------------------------
/src/store/auth.module.js:
--------------------------------------------------------------------------------
1 | import AuthService from '../services/auth.service';
2 |
3 | const user = JSON.parse(localStorage.getItem('user'));
4 | const initialState = user
5 | ? { status: { loggedIn: true }, user }
6 | : { status: { loggedIn: false }, user: null };
7 |
8 | export const auth = {
9 | namespaced: true,
10 | state: initialState,
11 | actions: {
12 | login({ commit }, user) {
13 | return AuthService.login(user).then(
14 | user => {
15 | commit('loginSuccess', user);
16 | return Promise.resolve(user);
17 | },
18 | error => {
19 | commit('loginFailure');
20 | return Promise.reject(error);
21 | }
22 | );
23 | },
24 | logout({ commit }) {
25 | AuthService.logout();
26 | commit('logout');
27 | },
28 | register({ commit }, user) {
29 | return AuthService.register(user).then(
30 | response => {
31 | commit('registerSuccess');
32 | return Promise.resolve(response.data);
33 | },
34 | error => {
35 | commit('registerFailure');
36 | return Promise.reject(error);
37 | }
38 | );
39 | }
40 | },
41 | mutations: {
42 | loginSuccess(state, user) {
43 | state.status.loggedIn = true;
44 | state.user = user;
45 | },
46 | loginFailure(state) {
47 | state.status.loggedIn = false;
48 | state.user = null;
49 | },
50 | logout(state) {
51 | state.status.loggedIn = false;
52 | state.user = null;
53 | },
54 | registerSuccess(state) {
55 | state.status.loggedIn = false;
56 | },
57 | registerFailure(state) {
58 | state.status.loggedIn = false;
59 | }
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import { createStore } from "vuex";
2 | import { auth } from "./auth.module";
3 |
4 | const store = createStore({
5 | modules: {
6 | auth,
7 | },
8 | });
9 |
10 | export default store;
11 |
--------------------------------------------------------------------------------
/vue-3-authentication-jwt-example-flow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/vue-3-authentication-jwt-example-flow.png
--------------------------------------------------------------------------------
/vue-3-authentication-jwt-example-vuex-form-validation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/vue-3-authentication-jwt-example-vuex-form-validation.png
--------------------------------------------------------------------------------
/vue-3-authentication-jwt-example-vuex-user-login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/vue-3-authentication-jwt-example-vuex-user-login.png
--------------------------------------------------------------------------------
/vue-3-authentication-jwt-example-vuex-user-registration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bezkoder/vue-3-authentication-jwt/ca3b629b055918d61f5ff64c3fe403bd1441427c/vue-3-authentication-jwt-example-vuex-user-registration.png
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | devServer: {
3 | port: 8081
4 | }
5 | }
--------------------------------------------------------------------------------