├── .gitignore ├── README.md ├── index.html ├── package.json ├── src ├── auth │ └── index.js ├── components │ ├── App.vue │ ├── Home.vue │ ├── Login.vue │ ├── SecretQuote.vue │ └── Signup.vue └── index.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | 13 | # Directory for instrumented libs generated by jscoverage/JSCover 14 | lib-cov 15 | 16 | # Coverage directory used by tools like istanbul 17 | coverage 18 | 19 | # nyc test coverage 20 | .nyc_output 21 | 22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 23 | .grunt 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Compiled binary addons (http://nodejs.org/api/addons.html) 29 | build/Release 30 | 31 | # Dependency directories 32 | node_modules 33 | jspm_packages 34 | 35 | # Optional npm cache directory 36 | .npm 37 | 38 | # Optional REPL history 39 | .node_repl_history 40 | .idea/ 41 | .env 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Vue.js Frontend Authentication 2 | === 3 | 4 | ![](https://vuejs.org/images/logo.png) 5 | 6 | A frontend codebase for authentication with Vue.js and JWT. 7 | 8 | ## About the app 9 | 10 | To log users in, we'll make an HTTP request to our authentication endpoint and save the JWT that is returned in localStorage. 11 | 12 | **Vue components** allow us to specify a template, a script, and style rules all in one file. 13 | 14 | 15 | ## Setup 16 | 17 | Launch server: 18 | 19 | ``` 20 | $ webpack-dev-server --inline --hot 21 | ``` 22 | 23 | Sample backend: https://github.com/auth0-blog/nodejs-jwt-authentication-sample. Clone the repo, install dependencies and then we can use it as an api endpoint to hit up `http://localhost:3001/api/random-quote` 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hello Vue 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuejs-auth-frontend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "babel-core": "^6.1.2", 13 | "babel-loader": "^6.1.0", 14 | "babel-plugin-transform-runtime": "^6.1.2", 15 | "babel-preset-es2015": "^6.1.2", 16 | "babel-runtime": "^6.0.14", 17 | "css-loader": "^0.21.0", 18 | "style-loader": "^0.13.0", 19 | "vue-hot-reload-api": "^1.2.1", 20 | "vue-html-loader": "^1.0.0", 21 | "vue-loader": "^7.0.1", 22 | "webpack": "^1.12.3", 23 | "webpack-dev-server": "^1.12.1" 24 | }, 25 | "dependencies": { 26 | "bootstrap": "^3.3.5", 27 | "vue-resource": "^0.1.17", 28 | "vue-router": "^0.7.5", 29 | "vue": "^1.0.7" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/auth/index.js: -------------------------------------------------------------------------------- 1 | import {router} from '../index' 2 | 3 | // endpoints 4 | const API_URL = 'http://localhost:3001/' 5 | const LOGIN_URL = API_URL + 'sessions/create' 6 | const SIGNUP_URL = API_URL + 'users/' 7 | 8 | export default { 9 | // user object is how we check auth status 10 | user: { 11 | authenticated: false 12 | }, 13 | 14 | login(context, creds, redirect){ 15 | context.$http.post(LOGIN_URL, creds, (data) => { 16 | localStorage.setItem('id_token', data.id_token) 17 | 18 | this.user.authenticated = true 19 | 20 | // redirect 21 | if(redirect){ 22 | router.go(redirect) 23 | } 24 | }).error((err) => { 25 | context.error = err 26 | }) 27 | }, 28 | 29 | signup(context, creds, redirect){ 30 | context.$http.post(SIGNUP_URL, creds, (data) => { 31 | localStorage.setItem('id_token', data.id_token) 32 | 33 | this.user.authenticated = true 34 | 35 | // redirect 36 | if(redirect){ 37 | router.go(redirect) 38 | } 39 | }).error((err) => { 40 | context.error = err 41 | }) 42 | }, 43 | 44 | logout(){ 45 | localStorage.removeItem('id_token') 46 | this.user.authenticated = false 47 | }, 48 | 49 | checkAuth(){ 50 | var jwt = localStorage.getItem('id_token') 51 | if(jwt){ 52 | this.user.authenticated = true 53 | } else { 54 | this.user.authenticated = false 55 | } 56 | }, 57 | 58 | getAuthHeader(){ 59 | return { 60 | 'Authorization': 'Bearer ' + localStorage.getItem('id_token') 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /src/components/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/components/Home.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/components/SecretQuote.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/Signup.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import VueResource from 'vue-resource' 4 | 5 | // components 6 | import App from './components/App.vue' 7 | import Home from './components/Home.vue' 8 | import Login from './components/Login.vue' 9 | import Signup from './components/Signup.vue' 10 | import SecretQuote from './components/SecretQuote.vue' 11 | 12 | // services 13 | import auth from './auth' 14 | 15 | Vue.use(VueResource) 16 | Vue.use(VueRouter) 17 | 18 | // Check the users auth status when the app starts 19 | auth.checkAuth() 20 | 21 | // export so we can use in components 22 | export var router = new VueRouter() 23 | 24 | // define routes 25 | router.map({ 26 | '/home': { 27 | component: Home 28 | }, 29 | '/login': { 30 | component: Login 31 | }, 32 | '/signup': { 33 | component: Signup 34 | }, 35 | '/secretquote': { 36 | component: SecretQuote 37 | } 38 | }) 39 | 40 | // fallback route 41 | router.redirect({ 42 | '*': '/home' 43 | }) 44 | 45 | // expose the whole thing on element with 'app' as an id 46 | router.start(App, '#app') -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | // the main entry of our app 4 | entry: ['./src/index.js', './src/auth/index.js'], 5 | // output configuration 6 | output: { 7 | path: __dirname + '/build/', 8 | publicPath: 'build/', 9 | filename: 'build.js' 10 | }, 11 | 12 | module: { 13 | loaders: [ 14 | // process *.vue files using vue-loader 15 | { test: /\.vue$/, loader: 'vue' }, 16 | // process *.js files using babel-loader 17 | // the exclude pattern is important so that we don't 18 | // apply babel transform to all the dependencies! 19 | { test: /\.js$/, loader: 'babel', exclude: /node_modules/ } 20 | ] 21 | }, 22 | 23 | babel: { 24 | presets: ['es2015'], 25 | plugins: ['transform-runtime'] 26 | } 27 | } --------------------------------------------------------------------------------