├── .babelrc ├── .editorconfig ├── .env.example ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── .stylintrc ├── LICENSE ├── README.md ├── config ├── envparser.js └── helpers │ └── env.js ├── package-lock.json ├── package.json ├── quasar.conf.js └── src ├── App.vue ├── assets ├── quasar-logo-full.svg └── sad.svg ├── components └── .gitkeep ├── config ├── api.js ├── auth.js └── index.js ├── css ├── app.styl └── themes │ ├── common.variables.styl │ ├── variables.ios.styl │ └── variables.mat.styl ├── i18n ├── en │ └── index.js └── index.js ├── index.template.html ├── oauth ├── auth.service.js └── index.js ├── pages ├── 404.vue ├── index.vue ├── layouts │ └── default.vue └── login.vue ├── plugins ├── .gitkeep ├── axios.js ├── i18n.js └── oauth.js ├── router ├── index.js ├── middlewares │ ├── afterEach.js │ ├── auth.js │ ├── beforeEach.js │ ├── guest.js │ ├── index.js │ └── web.js └── routes.js ├── services ├── index.js └── user.service.js ├── statics ├── icons │ ├── apple-icon-152x152.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── icon-128x128.png │ ├── icon-192x192.png │ ├── icon-256x256.png │ ├── icon-384x384.png │ ├── icon-512x512.png │ └── ms-icon-144x144.png └── quasar-logo.png ├── store ├── index.js ├── module-example │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ └── state.js └── users │ ├── actions.js │ ├── getters.js │ ├── index.js │ ├── mutations.js │ └── state.js └── utils ├── findValue.js └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ "env", {"modules": false} ], 4 | "stage-2" 5 | ], 6 | "plugins": ["transform-runtime"], 7 | "comments": false 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | BASE_URL=http://localhost/ 2 | LOCALE=en 3 | # Values for OAuth 4 | DEFAULT_STORAGE=LocalStorage 5 | SERVER=local 6 | GRANT_TYPE=password 7 | CLIENT_ID= 8 | CLIENT_SECRET= 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist 2 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | parser: 'babel-eslint', 5 | sourceType: 'module' 6 | }, 7 | env: { 8 | browser: true 9 | }, 10 | extends: [ 11 | // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention 12 | // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. 13 | 'plugin:vue/essential', 14 | // https://github.com/standard/standard/blob/master/docs/RULES-en.md 15 | 'standard' 16 | ], 17 | // required to lint *.vue files 18 | plugins: [ 19 | 'vue' 20 | ], 21 | globals: { 22 | 'ga': true, // Google Analytics 23 | 'cordova': true, 24 | '__statics': true, 25 | 'env': true 26 | }, 27 | // add your custom rules here 28 | 'rules': { 29 | // allow async-await 30 | 'generator-star-spacing': 'off', 31 | 32 | // allow paren-less arrow functions 33 | 'arrow-parens': 0, 34 | 'one-var': 0, 35 | 'camelcase': 0, 36 | 37 | 'import/first': 0, 38 | 'import/named': 2, 39 | 'import/namespace': 2, 40 | 'import/default': 2, 41 | 'import/export': 2, 42 | 'import/extensions': 0, 43 | 'import/no-unresolved': 0, 44 | 'import/no-extraneous-dependencies': 0, 45 | 46 | // allow debugger during development 47 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .quasar 2 | .DS_Store 3 | .thumbs.db 4 | node_modules 5 | /dist 6 | /src-cordova/platforms 7 | /src-cordova/plugins 8 | /src-cordova/www 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # Editor directories and files 14 | .idea 15 | .vscode 16 | *.suo 17 | *.ntvs* 18 | *.njsproj 19 | *.sln 20 | 21 | .env 22 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.stylintrc: -------------------------------------------------------------------------------- 1 | { 2 | "blocks": "never", 3 | "brackets": "never", 4 | "colons": "never", 5 | "colors": "always", 6 | "commaSpace": "always", 7 | "commentSpace": "always", 8 | "cssLiteral": "never", 9 | "depthLimit": false, 10 | "duplicates": true, 11 | "efficient": "always", 12 | "extendPref": false, 13 | "globalDupe": true, 14 | "indentPref": 2, 15 | "leadingZero": "never", 16 | "maxErrors": false, 17 | "maxWarnings": false, 18 | "mixed": false, 19 | "namingConvention": false, 20 | "namingConventionStrict": false, 21 | "none": "never", 22 | "noImportant": false, 23 | "parenSpace": "never", 24 | "placeholder": false, 25 | "prefixVarsWithDollar": "always", 26 | "quotePref": "single", 27 | "semicolons": "never", 28 | "sortOrder": false, 29 | "stackedProperties": "never", 30 | "trailingWhitespace": "never", 31 | "universal": "never", 32 | "valid": true, 33 | "zeroUnits": "never", 34 | "zIndexNormalize": false 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Cesar Santana 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quasar App With Authentication using Laravel Passport (Not Laravel project included) 2 | 3 | ## Considerations to make oauth work in this project 4 | - Duplicate env.example to .env 5 | There you have to add the keys for your API. Maybe you want to take a look into src/config/auth.js 6 | - Set the configuration to get the current user in src/config/api.js, basicly is to define a route where our app will make the call the get the current user in your server. 7 | 8 | That's it, that's well enough to make your app work with Authentication using Laravel Passport 9 | -------------------------------------------------------------------------------- /config/envparser.js: -------------------------------------------------------------------------------- 1 | const DotEnv = require('dotenv') 2 | const parsedEnv = DotEnv.config().parsed 3 | 4 | module.exports = function () { 5 | // Let's stringify our variables 6 | for (key in parsedEnv) { 7 | if (typeof parsedEnv[key] === 'string') { 8 | parsedEnv[key] = JSON.stringify(parsedEnv[key]) 9 | } 10 | } 11 | return parsedEnv 12 | } 13 | -------------------------------------------------------------------------------- /config/helpers/env.js: -------------------------------------------------------------------------------- 1 | module.exports = function (key, fallback) { 2 | let value = process.env[key] 3 | if (!value) { 4 | return fallback 5 | } 6 | return value.replace(/["]/g, '') 7 | } 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-oauth", 3 | "version": "1.0.0", 4 | "description": "Vue OAuth Made with Quasar 0.15", 5 | "productName": "Vue OAuth", 6 | "cordovaId": "org.cordova.quasar.app", 7 | "author": "Cesar Santana ", 8 | "private": true, 9 | "scripts": { 10 | "lint": "eslint --ext .js,.vue src", 11 | "test": "echo \"No test specified\" && exit 0" 12 | }, 13 | "dependencies": { 14 | "axios": "^0.18.0", 15 | "easy-requests": "^1.2.2", 16 | "node-sass": "^4.9.0", 17 | "pug": "^2.0.3", 18 | "pug-loader": "^2.4.0", 19 | "sass-loader": "^7.0.1", 20 | "vue-i18n": "^7.3.3", 21 | "vue-routisan": "^2.1.1" 22 | }, 23 | "devDependencies": { 24 | "babel-eslint": "^8.2.1", 25 | "dotenv": "^5.0.1", 26 | "eslint": "^4.18.2", 27 | "eslint-config-standard": "^11.0.0", 28 | "eslint-friendly-formatter": "^3.0.0", 29 | "eslint-loader": "^2.0.0", 30 | "eslint-plugin-import": "^2.9.0", 31 | "eslint-plugin-node": "^6.0.1", 32 | "eslint-plugin-promise": "^3.7.0", 33 | "eslint-plugin-standard": "^3.0.1", 34 | "eslint-plugin-vue": "^4.3.0", 35 | "quasar-cli": "^0.15.14" 36 | }, 37 | "engines": { 38 | "node": ">= 8.9.0", 39 | "npm": ">= 5.6.0" 40 | }, 41 | "browserslist": [ 42 | "> 1%", 43 | "last 2 versions", 44 | "not ie <= 10" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /quasar.conf.js: -------------------------------------------------------------------------------- 1 | // Configuration for your app 2 | var webpack = require('webpack') 3 | var path = require('path') 4 | 5 | // Get our env variables 6 | const envparser = require('./config/envparser') 7 | module.exports = function (ctx) { 8 | return { 9 | // app plugins (/src/plugins) 10 | plugins: [ 11 | 'oauth', 12 | 'i18n', 13 | 'axios' 14 | ], 15 | css: [ 16 | 'app.styl' 17 | ], 18 | extras: [ 19 | ctx.theme.mat ? 'roboto-font' : null, 20 | 'material-icons' 21 | // 'ionicons', 22 | // 'mdi', 23 | // 'fontawesome' 24 | ], 25 | supportIE: true, 26 | vendor: { 27 | add: [], 28 | remove: [] 29 | }, 30 | build: { 31 | scopeHoisting: true, 32 | vueRouterMode: 'history', 33 | env: envparser(), 34 | // gzip: true, 35 | // analyze: true, 36 | // extractCSS: false, 37 | // useNotifier: false, 38 | extendWebpack (cfg) { 39 | // Create alias 'env' to access to env.js 40 | cfg.resolve.alias.env = path.resolve(__dirname, 'config/helpers/env.js') 41 | 42 | // Make our helper function Global, for example to use it in js files you should call it env('MY_VALUE') 43 | cfg.plugins.push( 44 | new webpack.ProvidePlugin({ 45 | 'env': 'env' // this variable is our alias, it's not a string 46 | }) 47 | ) 48 | 49 | cfg.module.rules.push({ 50 | enforce: 'pre', 51 | test: /\.(js|vue)$/, 52 | loader: 'eslint-loader', 53 | exclude: /(node_modules|quasar)/ 54 | }) 55 | } 56 | }, 57 | devServer: { 58 | // https: true, 59 | // port: 8080, 60 | open: true // opens browser window automatically 61 | }, 62 | // framework: 'all' --- includes everything; for dev only! 63 | framework: { 64 | components: [ 65 | 'QLayout', 66 | 'QLayoutHeader', 67 | 'QLayoutDrawer', 68 | 'QPageContainer', 69 | 'QPage', 70 | 'QToolbar', 71 | 'QToolbarTitle', 72 | 'QBtn', 73 | 'QIcon', 74 | 'QList', 75 | 'QListHeader', 76 | 'QItem', 77 | 'QItemMain', 78 | 'QItemSide' 79 | ], 80 | directives: [ 81 | 'Ripple' 82 | ], 83 | // Quasar plugins 84 | plugins: ['Notify', 'LocalStorage', 'SessionStorage', 'Cookies'] 85 | }, 86 | // animations: 'all' --- includes all animations 87 | animations: [ 88 | ], 89 | pwa: { 90 | cacheExt: 'js,html,css,ttf,eot,otf,woff,woff2,json,svg,gif,jpg,jpeg,png,wav,ogg,webm,flac,aac,mp4,mp3', 91 | manifest: { 92 | // name: 'Quasar App', 93 | // short_name: 'Quasar-PWA', 94 | // description: 'Best PWA App in town!', 95 | display: 'standalone', 96 | orientation: 'portrait', 97 | background_color: '#ffffff', 98 | theme_color: '#027be3', 99 | icons: [ 100 | { 101 | 'src': 'statics/icons/icon-128x128.png', 102 | 'sizes': '128x128', 103 | 'type': 'image/png' 104 | }, 105 | { 106 | 'src': 'statics/icons/icon-192x192.png', 107 | 'sizes': '192x192', 108 | 'type': 'image/png' 109 | }, 110 | { 111 | 'src': 'statics/icons/icon-256x256.png', 112 | 'sizes': '256x256', 113 | 'type': 'image/png' 114 | }, 115 | { 116 | 'src': 'statics/icons/icon-384x384.png', 117 | 'sizes': '384x384', 118 | 'type': 'image/png' 119 | }, 120 | { 121 | 'src': 'statics/icons/icon-512x512.png', 122 | 'sizes': '512x512', 123 | 'type': 'image/png' 124 | } 125 | ] 126 | } 127 | }, 128 | cordova: { 129 | // id: 'org.cordova.quasar.app' 130 | }, 131 | electron: { 132 | extendWebpack (cfg) { 133 | // do something with cfg 134 | }, 135 | packager: { 136 | // OS X / Mac App Store 137 | // appBundleId: '', 138 | // appCategoryType: '', 139 | // osxSign: '', 140 | // protocol: 'myapp://path', 141 | 142 | // Window only 143 | // win32metadata: { ... } 144 | } 145 | }, 146 | 147 | // leave this here for Quasar CLI 148 | starterKit: '1.0.2' 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 15 | -------------------------------------------------------------------------------- /src/assets/quasar-logo-full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 63 | 66 | 69 | 75 | 79 | 83 | 87 | 91 | 95 | 99 | 103 | 104 | 105 | 106 | 107 | 113 | 118 | 126 | 133 | 142 | 151 | 160 | 169 | 178 | 187 | 188 | 189 | 190 | 191 | 192 | -------------------------------------------------------------------------------- /src/assets/sad.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/components/.gitkeep -------------------------------------------------------------------------------- /src/config/api.js: -------------------------------------------------------------------------------- 1 | /* 2 | * In javascript the variables are declared in camelcase but I like use snake case, 3 | * if you want you can rename them as you want and remove the rule in .eslintrc line 35 4 | */ 5 | const api_url = env('BASE_URL', 'http://localhost:8000/') + 'api/' 6 | export default { 7 | api_url, 8 | token_url: env('BASE_URL', 'http://localhost:8000/') + 'oauth/token', 9 | current_user_url: api_url + 'me', // you can change it as you want 10 | endpoints: { 11 | users_url: api_url + 'users' 12 | // resource_url : api_url + 'resource' 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/config/auth.js: -------------------------------------------------------------------------------- 1 | export default { 2 | oauth: { 3 | grant_type: env('GRANT_TYPE', 'password'), 4 | client_id: env('CLIENT_ID', null), 5 | client_secret: env('CLIENT_SECRET', null), 6 | scope: '*' 7 | }, 8 | default_storage: env('DEFAULT_STORAGE', 'LocalStorage'), // Supported Types 'Cookies', 'Localstorage', 9 | oauth_type: 'Bearer' 10 | } 11 | -------------------------------------------------------------------------------- /src/config/index.js: -------------------------------------------------------------------------------- 1 | import { findValue } from 'src/utils' 2 | import api from './api' 3 | import auth from './auth' 4 | // import newconfig from './newconfigfile' 5 | 6 | export default function (value = '', fallback = null) { 7 | const values = { 8 | api, 9 | auth 10 | // newconfig 11 | } 12 | return findValue(value, values) || fallback 13 | } 14 | -------------------------------------------------------------------------------- /src/css/app.styl: -------------------------------------------------------------------------------- 1 | // app global css 2 | -------------------------------------------------------------------------------- /src/css/themes/common.variables.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // To customize the look and feel of this app, you can override 4 | // the Stylus variables found in Quasar's source Stylus files. Setting 5 | // variables before Quasar's Stylus will use these variables rather than 6 | // Quasar's default Stylus variable values. Stylus variables specific 7 | // to the themes belong in either the variables.ios.styl or variables.mat.styl files. 8 | 9 | // Check documentation for full list of Quasar variables 10 | 11 | 12 | // App Shared Color Variables 13 | // -------------------------------------------------- 14 | // It's highly recommended to change the default colors 15 | // to match your app's branding. 16 | 17 | $primary = #027be3 18 | $secondary = #26A69A 19 | $tertiary = #555 20 | 21 | $neutral = #E0E1E2 22 | $positive = #21BA45 23 | $negative = #DB2828 24 | $info = #31CCEC 25 | $warning = #F2C037 26 | -------------------------------------------------------------------------------- /src/css/themes/variables.ios.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // iOS only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/css/themes/variables.mat.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // Material only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/i18n/en/index.js: -------------------------------------------------------------------------------- 1 | // This is just an example, 2 | // so you can safely delete all default props below 3 | 4 | export default { 5 | failed: 'Action failed', 6 | success: 'Action was successful' 7 | } 8 | -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import en from './en' 2 | 3 | export default { 4 | en 5 | } 6 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.productName %> 10 | 11 | 12 | 13 | 14 | 15 | <% if (htmlWebpackPlugin.options.ctx.mode.pwa) { %> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | <% } %> 28 | 29 | <%= htmlWebpackPlugin.options.headScripts %> 30 | 31 | 35 | <% if (!['cordova', 'electron'].includes(htmlWebpackPlugin.options.ctx.modeName) && htmlWebpackPlugin.options.ctx.prod) { 36 | for (var chunk of webpack.chunks) { 37 | for (var file of chunk.files) { 38 | if (file.match(/\.(js|css)$/)) { %> 39 | 40 | <% }}}} %> 41 | 42 | 43 | <% if (!htmlWebpackPlugin.options.ctx.mode.electron) { %> 44 | 47 | <% } %> 48 | 49 | 50 |
51 | 52 | 53 | <%= htmlWebpackPlugin.options.bodyScripts %> 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /src/oauth/auth.service.js: -------------------------------------------------------------------------------- 1 | import Http from 'axios' 2 | import Config from 'src/config' 3 | export default { 4 | async attemptLogin (credentials) { 5 | try { 6 | let response = await Http.post(Config('api.token_url'), credentials) 7 | return new Promise(resolve => resolve(response)) 8 | } catch (error) { 9 | return new Promise((resolve, reject) => reject(error)) 10 | } 11 | }, 12 | currentUser () { 13 | const currentUser = Http.get(Config('api.current_user_url')) 14 | return new Promise((resolve, reject) => { 15 | currentUser 16 | .then(user => resolve(user.data)) 17 | .catch(error => reject(error)) 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/oauth/index.js: -------------------------------------------------------------------------------- 1 | /*************************************************** 2 | * [Quasar Cookies] http://quasar-framework.org/components/cookies.html 3 | * [Quasar LocalStorage] http://quasar-framework.org/components/web-storage.html 4 | **************************************************/ 5 | 6 | import { Cookies, LocalStorage } from 'quasar' 7 | import HttpService from './auth.service' 8 | import Config from 'src/config' 9 | import Store from 'src/store' 10 | class OAuth { 11 | constructor () { 12 | this.storages = { 13 | Cookies, 14 | LocalStorage 15 | } 16 | this.session = this.storages[Config('auth.default_storage')] 17 | } 18 | 19 | logout () { 20 | this.session.remove('access_token') 21 | this.session.remove('refresh_token') 22 | Store.dispatch('users/destroyCurrentUser') 23 | } 24 | 25 | guest () { 26 | return !this.session.has('access_token') 27 | } 28 | 29 | isAuthenticated () { 30 | return this.session.has('access_token') 31 | } 32 | 33 | login (username, password) { 34 | let data = { 35 | username, 36 | password 37 | } 38 | 39 | // We merge grant type and client secret stored in configuration 40 | Object.assign(data, Config('auth.oauth')) 41 | return new Promise((resolve, reject) => { 42 | HttpService.attemptLogin(data) 43 | .then(response => { 44 | this.storeSession(response.data) 45 | resolve(response) 46 | }) 47 | .catch(error => { 48 | console.log('OAUTH Authentication error: ', error) 49 | reject(error) 50 | }) 51 | }) 52 | } 53 | 54 | currentUser () { 55 | if (this.session.has('access_token')) { 56 | return new Promise((resolve, reject) => { 57 | HttpService.currentUser() 58 | .then(response => { 59 | resolve(response) 60 | }) 61 | .catch(error => { 62 | if (error.response && (error.response.status === 401 || error.response.status === 429)) { 63 | this.logout() 64 | } 65 | reject(error) 66 | }) 67 | }) 68 | } 69 | return new Promise(resolve => resolve(null)) 70 | } 71 | 72 | getAuthHeader () { 73 | if (this.session.has('access_token')) { 74 | let access_token = this.getItem('access_token') 75 | return Config('auth.oauth_type') + ' ' + access_token 76 | } 77 | return null 78 | } 79 | 80 | getItem (key) { 81 | if (Config('auth.default_storage') === 'LocalStorage') { 82 | return this.session.get.item(key) 83 | } 84 | return this.session.get(key) 85 | } 86 | 87 | storeSession (data) { 88 | let hourInMilliSeconds = 86400 89 | let time = data.expires_in / hourInMilliSeconds 90 | 91 | if (Config('auth.default_storage') === 'LocalStorage') { 92 | this.session.set('access_token', data.access_token) 93 | this.session.set('refresh_token', data.access_token) 94 | } else { 95 | /* 96 | ** when the Storage is type Cookies 97 | ** we send the expires property given in days 98 | */ 99 | this.session.set('access_token', data.access_token, { 100 | expires: time 101 | }) 102 | /* 103 | ** We duplicate the time because, 104 | ** in theory it lasts the double of time access token duration 105 | */ 106 | this.session.set('refresh_token', data.access_token, { 107 | expires: time * 2 108 | }) 109 | } 110 | } 111 | } 112 | 113 | export default OAuth 114 | -------------------------------------------------------------------------------- /src/pages/404.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 10 | 18 | -------------------------------------------------------------------------------- /src/pages/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 93 | 94 | 96 | -------------------------------------------------------------------------------- /src/pages/login.vue: -------------------------------------------------------------------------------- 1 | 27 | 77 | 109 | -------------------------------------------------------------------------------- /src/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/plugins/.gitkeep -------------------------------------------------------------------------------- /src/plugins/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | export default ({ Vue }) => { 4 | Vue.prototype.$axios = axios 5 | } 6 | -------------------------------------------------------------------------------- /src/plugins/i18n.js: -------------------------------------------------------------------------------- 1 | import VueI18n from 'vue-i18n' 2 | import messages from 'src/i18n' 3 | 4 | export default ({ app, Vue }) => { 5 | Vue.use(VueI18n) 6 | 7 | // Set i18n instance on app 8 | app.i18n = new VueI18n({ 9 | locale: 'en', 10 | fallbackLocale: 'en', 11 | messages 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/plugins/oauth.js: -------------------------------------------------------------------------------- 1 | import OAuth from 'src/oauth' 2 | import Services from 'src/services' 3 | 4 | export default ({ app, router, Vue }) => { 5 | const oauth = new OAuth() 6 | Vue.prototype.$oauth = oauth 7 | Services.run({ 8 | oauth 9 | }) 10 | } 11 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import routes from './routes' 4 | import { beforeEach, afterEach } from 'src/router/middlewares' 5 | 6 | Vue.use(VueRouter) 7 | 8 | const Router = new VueRouter({ 9 | /* 10 | * NOTE! Change Vue Router mode from quasar.conf.js -> build -> vueRouterMode 11 | * 12 | * When going with "history" mode, please also make sure "build.publicPath" 13 | * is set to something other than an empty string. 14 | * Example: '/' instead of '' 15 | */ 16 | 17 | // Leave as is and change from quasar.conf.js instead! 18 | mode: process.env.VUE_ROUTER_MODE, 19 | base: process.env.VUE_ROUTER_BASE, 20 | scrollBehavior: () => ({ y: 0 }), 21 | routes 22 | }) 23 | 24 | Router.beforeEach(beforeEach) 25 | Router.afterEach(afterEach) 26 | 27 | export default Router 28 | -------------------------------------------------------------------------------- /src/router/middlewares/afterEach.js: -------------------------------------------------------------------------------- 1 | export default function (to, from) { 2 | } 3 | -------------------------------------------------------------------------------- /src/router/middlewares/auth.js: -------------------------------------------------------------------------------- 1 | import OAuth from 'src/oauth' 2 | const auth = new OAuth() 3 | export default async function (to, from, next) { 4 | try { 5 | const user = await auth.currentUser() 6 | if (user) { 7 | next() 8 | } else { 9 | next('/login') 10 | } 11 | } catch (error) { 12 | console.log(error) 13 | next() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/router/middlewares/beforeEach.js: -------------------------------------------------------------------------------- 1 | export default function (to, from, next) { 2 | next() 3 | } 4 | -------------------------------------------------------------------------------- /src/router/middlewares/guest.js: -------------------------------------------------------------------------------- 1 | import OAuth from 'src/oauth' 2 | const auth = new OAuth() 3 | export default async function (to, from, next) { 4 | const user = await auth.currentUser() 5 | if (user) { 6 | next('/') 7 | } else { 8 | next() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/router/middlewares/index.js: -------------------------------------------------------------------------------- 1 | import auth from './auth' 2 | import web from './web' 3 | import guest from './guest' 4 | import afterEach from './afterEach' 5 | import beforeEach from './beforeEach' 6 | export { 7 | auth, 8 | web, 9 | guest, 10 | beforeEach, 11 | afterEach 12 | } 13 | -------------------------------------------------------------------------------- /src/router/middlewares/web.js: -------------------------------------------------------------------------------- 1 | import OAuth from 'src/oauth' 2 | import Store from 'src/store' 3 | const auth = new OAuth() 4 | export default async function (to, from, next) { 5 | const user = auth.currentUser() 6 | 7 | user.then(user => { 8 | return Store.dispatch('users/setCurrentUser', user) 9 | }).then(result => { 10 | next() 11 | }).catch(error => { 12 | next() 13 | throw error 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Using this package https://github.com/raniesantos/vue-routisan 3 | * to beautify routes. For example 4 | * { 5 | * path: '/', 6 | * component: () => import('layouts/default'), 7 | * children: [ 8 | * { path: '', component: () => import('pages/index') } 9 | * ] 10 | * } 11 | * 12 | */ 13 | import { web, auth, guest } from 'src/router/middlewares' 14 | import Route from 'vue-routisan' 15 | // Define path where your views are stored 16 | Route.setViewResolver(component => require('src/pages/' + component).default) 17 | 18 | Route.view('/', 'layouts/default') 19 | .guard(web) 20 | .children(() => { 21 | Route.view('/', 'index').guard(auth) 22 | }) 23 | 24 | Route.view('/', 'layouts/default') 25 | .guard(web) 26 | .children(() => { 27 | Route.view('/login', 'login').name('app.login').guard(guest) 28 | }) 29 | Route.view('*', '404') 30 | 31 | export default Route.all() 32 | -------------------------------------------------------------------------------- /src/services/index.js: -------------------------------------------------------------------------------- 1 | import http from 'axios' 2 | export default { 3 | run (session) { 4 | const { oauth } = session 5 | http.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest' 6 | 7 | // Add a request interceptor 8 | http.interceptors.request.use( 9 | function (config) { 10 | // Handle requests here 11 | config.headers['Authorization'] = oauth.getAuthHeader() 12 | return config 13 | }, 14 | function (error) { 15 | // Do something with request error 16 | return Promise.reject(error) 17 | } 18 | ) 19 | // Add a response interceptor 20 | http.interceptors.response.use( 21 | function (response) { 22 | // Do something with response data 23 | return response 24 | }, 25 | function (error) { 26 | // Do something with response error 27 | if (error.response && error.response.status === 401) { 28 | oauth.logout() 29 | } 30 | return Promise.reject(error) 31 | } 32 | ) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/services/user.service.js: -------------------------------------------------------------------------------- 1 | import Service from 'easy-requests' 2 | import Config from 'src/config' 3 | 4 | class User extends Service { 5 | constructor () { 6 | super() 7 | // this.config.origin = Env('BASE_URL') 8 | this.config.origin = Config('api.api_url') 9 | this.config.endpoint = '/users/' 10 | } 11 | 12 | static currentUser () { 13 | let UserService = new User() 14 | let response = UserService.http.get(Config('api.current_user_url')) 15 | return new Promise((resolve, reject) => { 16 | response 17 | .then(user => resolve(user.data)) 18 | .catch(error => reject(error)) 19 | }) 20 | } 21 | } 22 | 23 | export default User 24 | -------------------------------------------------------------------------------- /src/statics/icons/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/apple-icon-152x152.png -------------------------------------------------------------------------------- /src/statics/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/favicon-16x16.png -------------------------------------------------------------------------------- /src/statics/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/favicon-32x32.png -------------------------------------------------------------------------------- /src/statics/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/icon-128x128.png -------------------------------------------------------------------------------- /src/statics/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/icon-192x192.png -------------------------------------------------------------------------------- /src/statics/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/icon-256x256.png -------------------------------------------------------------------------------- /src/statics/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/icon-384x384.png -------------------------------------------------------------------------------- /src/statics/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/icon-512x512.png -------------------------------------------------------------------------------- /src/statics/icons/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/icons/ms-icon-144x144.png -------------------------------------------------------------------------------- /src/statics/quasar-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blogui91/quasar-oauth-laravel/325e47f7c30672e3c5457d927653bcceb3981617/src/statics/quasar-logo.png -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import example from './module-example' 5 | import users from './users' 6 | 7 | Vue.use(Vuex) 8 | 9 | const store = new Vuex.Store({ 10 | modules: { 11 | example, 12 | users 13 | } 14 | }) 15 | 16 | export default store 17 | -------------------------------------------------------------------------------- /src/store/module-example/actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | export const someAction = (state) => { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/module-example/getters.js: -------------------------------------------------------------------------------- 1 | /* 2 | export const someGetter = (state) => { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/module-example/index.js: -------------------------------------------------------------------------------- 1 | import state from './state' 2 | import * as getters from './getters' 3 | import * as mutations from './mutations' 4 | import * as actions from './actions' 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | } 13 | -------------------------------------------------------------------------------- /src/store/module-example/mutations.js: -------------------------------------------------------------------------------- 1 | /* 2 | export const someMutation = (state) => { 3 | } 4 | */ 5 | -------------------------------------------------------------------------------- /src/store/module-example/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 3 | } 4 | -------------------------------------------------------------------------------- /src/store/users/actions.js: -------------------------------------------------------------------------------- 1 | import User from 'src/services/user.service' 2 | 3 | export const getCurrentUser = async ({ commit, state }, payload) => { 4 | if (state.currentUser) { 5 | return state.currentUser 6 | } 7 | let user_promise = User.currentUser() 8 | user_promise 9 | .then(user => { 10 | commit('users/setCurrentUser', user, { 11 | root: true 12 | }) 13 | }) 14 | .catch(error => { 15 | console.log('There was an error :c') 16 | throw error 17 | }) 18 | } 19 | 20 | export const setCurrentUser = (vuex, user) => { 21 | const { commit } = vuex 22 | commit('users/setCurrentUser', user, { 23 | root: true 24 | }) 25 | } 26 | 27 | export const destroyCurrentUser = ({ commit, state }, payload) => { 28 | commit('users/setCurrentUser', null, { 29 | root: true 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /src/store/users/getters.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | export const roles = (state) => { 3 | const user = state.currentUser 4 | if (user) { 5 | user.roles.push(user.role) 6 | return user.roles 7 | } 8 | return [] 9 | } 10 | 11 | export const permissions = (state) => { 12 | return _.groupBy(_.uniqBy(_.flatten(roles(state).map(role => role.permissions)), 'key'), 'table_name') 13 | } 14 | -------------------------------------------------------------------------------- /src/store/users/index.js: -------------------------------------------------------------------------------- 1 | import state from './state' 2 | import * as getters from './getters' 3 | import * as mutations from './mutations' 4 | import * as actions from './actions' 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | } 13 | -------------------------------------------------------------------------------- /src/store/users/mutations.js: -------------------------------------------------------------------------------- 1 | export const setCurrentUser = (state, payload) => { 2 | state.currentUser = payload 3 | } 4 | -------------------------------------------------------------------------------- /src/store/users/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | currentUser: null 3 | } 4 | -------------------------------------------------------------------------------- /src/utils/findValue.js: -------------------------------------------------------------------------------- 1 | export function findValue (value, values) { 2 | // Convert nested levels to array 3 | let keys = value.toString().split('.') 4 | let val 5 | // If it has only one level we take the first key and find it in values 6 | if (keys.length === 1) { 7 | const first_key = keys[0] 8 | val = values[first_key] 9 | // Otherwise we should check in the next nested level 10 | } else if (keys.length > 1) { 11 | const first_key = JSON.parse(JSON.stringify(keys[0])) 12 | let object_property = values[first_key] 13 | keys = keys.slice(1) 14 | if (typeof object_property === 'undefined') { 15 | val = null 16 | } else { 17 | // Remove the first item of keys because we have already accessed 18 | keys.forEach((key, index) => { 19 | if ((index + 1) === keys.length) { 20 | if (typeof object_property === 'undefined') { 21 | val = null 22 | } else { 23 | val = object_property[key] 24 | } 25 | } else { 26 | if (typeof object_property === 'undefined') { 27 | val = null 28 | } else { 29 | object_property = object_property[key] 30 | } 31 | } 32 | }) 33 | } 34 | } 35 | 36 | return val 37 | } 38 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | import { findValue } from './findValue' 2 | export { 3 | findValue 4 | } 5 | --------------------------------------------------------------------------------