Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

OAuth with Google and feathers-vuex #33

Closed
jhlabs opened this issue Aug 6, 2017 · 4 comments
Closed

OAuth with Google and feathers-vuex #33

jhlabs opened this issue Aug 6, 2017 · 4 comments

Comments

@jhlabs
Copy link

jhlabs commented Aug 6, 2017

Thanks for this fantastic plugin!

I cannot seem to properly construct the OAuth login flow with Google and hope the explanation to this issue could be helpful for many.

Steps to reproduce

I have connected the feathers-vuex plugin with the client as in the docs here:

'use strict'

import 'babel-polyfill'
import feathers from 'feathers/client'
import hooks from 'feathers-hooks'
import auth from 'feathers-authentication-client'
import restClient from 'feathers-rest/client'
import feathersVuex from 'feathers-vuex'
import axios from 'axios'
import store from '@/store/'

const feathersClient = feathers()
  .configure(hooks())
  .configure(restClient('http://localhost:3030').axios(axios))
  .configure(auth({ storage: window.localStorage }))
  .configure(feathersVuex(store, {
    idField: 'id',
    auth: {
      userService: '/users',
    }
  }))

feathersClient.service('/users')

export default feathersClient

On the server side I have created local and oauth authentication with the feathers generator. I added my google client secret and ID in the config.

Now I have created a # page:

<template>
  <v-card raised class="center-text white">
    <v-btn @click.native="google#" secondary raised dark># with Google</v-btn>
    <p class="my-3 center-text">or</p>
    <v-card-text>
       <v-text-field 
        class="center-input" 
        label="Your work email"
        type="text"
        prepend-icon="email"
        required
        v-model="email"
        name="email"
      ></v-text-field> 
      <v-text-field 
        class="center-input"
        label="Enter a secure password"
        required
        prepend-icon="fingerprint"
        v-model="password"
        :append-icon="passwordVisible ? 'visibility' : 'visibility_off'"
        :append-icon-cb="() => (passwordVisible = !passwordVisible)"
        :type="passwordVisible ? 'password' : 'text'"
      ></v-text-field>
    </v-card-text>
    <v-card-actions>
      <v-btn class="center-btn" primary raised dark @click.native="register(email,password)">Get started now</v-btn>
    </v-card-actions>
    <v-card-text>
      <p class="my-3 center-text">Already have an account?
        <router-link :to="{ name: 'login' }">Login here.</router-link>
      </p>
    </v-card-text>
  </v-card>
</template>

<script>
import { mapActions } from 'vuex'
export default {
  data () {
    return {
      company: undefined,
      email: undefined,
      password: undefined,
      passwordVisible: true
    }
  },
  methods: {
    register (email, password) {
      this.createUser({ email, password })
        .then(res => this.authenticate({strategy: 'local', email, password}))
        .then(this.$router.push('/'))
        .then(err => {
          console.log(err)
        })
    },
    google# () {
      window.location.href = '/auth/google'
    },
    ...mapActions('users', {
      createUser: 'create'
    }),
    ...mapActions('auth', [
      'authenticate'
    ])
  }
}
</script>

The local registration works flawlessly. For the oauth registration all that should be required on the client side is to link to the relative path '/auth/google' that has been created by feathers:

image

Expected behavior

The user should be directed to the Google OAuth page and then redirected to the pre-specified callback URL.

Actual behavior

If I use a relative path, the browser directs to a blank page with the given URL. When I change the URL to direct to the same URL on the server (by changing the port), the user is redirected to the Google OAuth page. However, the redirect does not seem to work back to the client.

I wonder which URL is the correct one (client or server side), and how the flow can be implemented correctly. Thanks a lot for your help!

System configuration

{
  "name": "seads-app",
  "version": "1.0.0",
  "description": "The client for the seads app.",
  "author": "Johannes Herrmann <johannes.herrmann2012@gmail.com>",
  "private": true,
  "scripts": {
    "dev": "node build/dev-server.js",
    "start": "node build/dev-server.js",
    "build": "node build/build.js",
    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
    "test": "npm run unit",
    "lint": "eslint --ext .js,.vue src test/unit/specs"
  },
  "dependencies": {
    "axios": "^0.16.2",
    "babel-polyfill": "^6.23.0",
    "feathers": "^2.1.4",
    "feathers-authentication-client": "^0.3.2",
    "feathers-hooks": "^2.0.1",
    "feathers-rest": "^1.8.0",
    "feathers-vuex": "^0.7.0",
    "vee-validate": "^2.0.0-rc.8",
    "vue": "^2.3.4",
    "vue-router": "^2.3.1",
    "vuetify": "^0.13.0",
    "vuex": "^2.3.1"
  },
  "devDependencies": {
    "autoprefixer": "^6.7.2",
    "babel-core": "^6.22.1",
    "babel-eslint": "^7.1.1",
    "babel-loader": "^6.2.10",
    "babel-plugin-istanbul": "^4.1.1",
    "babel-plugin-transform-object-rest-spread": "^6.23.0",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-env": "^1.3.2",
    "babel-preset-es2017": "^6.24.1",
    "babel-preset-stage-2": "^6.22.0",
    "babel-register": "^6.22.0",
    "chai": "^3.5.0",
    "chalk": "^1.1.3",
    "connect-history-api-fallback": "^1.3.0",
    "copy-webpack-plugin": "^4.0.1",
    "cross-env": "^4.0.0",
    "css-loader": "^0.28.0",
    "eslint": "^3.19.0",
    "eslint-config-airbnb-base": "^11.1.3",
    "eslint-config-standard": "^10.2.1",
    "eslint-friendly-formatter": "^2.0.7",
    "eslint-import-resolver-webpack": "^0.8.1",
    "eslint-loader": "^1.7.1",
    "eslint-plugin-html": "^2.0.0",
    "eslint-plugin-import": "^2.7.0",
    "eslint-plugin-node": "^5.1.1",
    "eslint-plugin-promise": "^3.5.0",
    "eslint-plugin-standard": "^3.0.1",
    "eventsource-polyfill": "^0.9.6",
    "express": "^4.14.1",
    "extract-text-webpack-plugin": "^2.0.0",
    "file-loader": "^0.11.1",
    "friendly-errors-webpack-plugin": "^1.1.3",
    "html-webpack-plugin": "^2.28.0",
    "http-proxy-middleware": "^0.17.3",
    "inject-loader": "^3.0.0",
    "karma": "^1.4.1",
    "karma-coverage": "^1.1.1",
    "karma-mocha": "^1.3.0",
    "karma-phantomjs-launcher": "^1.0.2",
    "karma-phantomjs-shim": "^1.4.0",
    "karma-sinon-chai": "^1.3.1",
    "karma-sourcemap-loader": "^0.3.7",
    "karma-spec-reporter": "0.0.30",
    "karma-webpack": "^2.0.2",
    "lolex": "^1.5.2",
    "mocha": "^3.2.0",
    "opn": "^4.0.2",
    "optimize-css-assets-webpack-plugin": "^1.3.0",
    "ora": "^1.2.0",
    "phantomjs-prebuilt": "^2.1.14",
    "rimraf": "^2.6.0",
    "semver": "^5.3.0",
    "shelljs": "^0.7.6",
    "sinon": "^2.1.0",
    "sinon-chai": "^2.8.0",
    "stylus": "^0.54.5",
    "stylus-loader": "^3.0.1",
    "url-loader": "^0.5.8",
    "vue-loader": "^11.3.4",
    "vue-style-loader": "^2.0.5",
    "vue-template-compiler": "^2.3.4",
    "webpack": "^2.3.3",
    "webpack-bundle-analyzer": "^2.2.1",
    "webpack-dev-middleware": "^1.10.0",
    "webpack-hot-middleware": "^2.18.0",
    "webpack-merge": "^4.1.0"
  },
  "engines": {
    "node": ">= 4.0.0",
    "npm": ">= 3.0.0"
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]
}
@dortamiguel
Copy link

dortamiguel commented Aug 8, 2017

In your backend you have to add on the config, in the "authentication": {} object, your oauth settings.

Also don't forget to add the redirect url on success login on the provider (Google in this case)

I also think that this problem doesn't belong to this repo, better try this one https://github.com/feathersjs/feathers-authentication

@dortamiguel
Copy link

Also, this code helps me a lot using authentication with feathers-vuex, maybe you can use it.

<template lang='pug'>

#app
	navbar(v-show='show_nav')

	#app_content
		router-view.view(
			v-if='check_meta()'
			v-bind='{ current_user }'
		)

</template>

<script>
import './app.sass'
import Navbar from './ui/navbar/navbar.vue'
import { mapActions, mapState, mapGetters } from 'vuex'

export default {
	name: 'app',

	components: { Navbar },

	data() {
		return { show_nav: true }
	},

	computed: {
		...mapState('auth', ['user']),
		...mapGetters('users', { getUserInStore: 'get' }),

		current_user() {
			if (this.user) return this.getUserInStore(this.user._id)
		},
	},

	methods: {
		...mapActions('users', { getUser: 'get', patchUser: 'patch' }),

		fetch() {
			this.getUser(this.user._id)
		},

		check_meta() {
			const { auth, hide_nav } = this.$route.meta

			// Hides the navbar if necessary
			if (hide_nav) this.show_nav = false
			else this.show_nav = true

			// Wait until user is ready for auth required routes
			if (auth === true) {
				if (this.current_user) return true
				else return false
			} else return true
		},
	},

	watch: {
		user() {
			if (this.user) this.fetch()
		},
	},

	async created() {
		try {
			const not_oauth_browser = !!navigator.userAgent.indexOf('cordova')

			if (not_oauth_browser) {
				await this.$store.dispatch('auth/authenticate')

				// Redirect from home to profile select
				// if (this.$route.path === '/')
				// 	this.$router.replace({ name: 'User' })
			}
		} catch (error) {
			if (
				error.message.includes('Could not find stored JWT') ||
				error.message.includes('_id is missing from current user.')
			) {
				const token = localStorage.getItem('feathers-jwt')

				if (token) {
					localStorage.removeItem('feathers-jwt')
					document.cookie =
						'feathers-jwt=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'
					location.reload()
				}
			} else {
				console.error(error)
			}
		}
	},
}
</script>

@jhlabs
Copy link
Author

jhlabs commented Aug 8, 2017

@ellipticaldoor thank you! I eventually got it to work with the help of this issue feathersjs-ecosystem/authentication#493. The key for me was to use the server to call the '/auth/google' link and use the handler provided by @marshallswain. I also had to activate the Google + API in their console, which caused an error before.

Instead of using the parameter extraction that is recommended in the linked post, you can simply use this.$route.query.token to handle the final token with vue-router.

@jhlabs jhlabs closed this as completed Aug 8, 2017
@jappyjan
Copy link

hello,

i'm having kinda the same problem (using github, but i don't think this makes a difference)...

how do i implement github/oauth flow?
gui (vue) redirects to localhost:3030/oauth/github
github is set to redirect on success to gui/oauth/github/callback

and then?

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants