Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Using /auth/facebook gives a 404 from vue-router #643

Closed
musicformellons opened this Issue Mar 5, 2018 · 18 comments

Comments

Projects
None yet
4 participants
@musicformellons
Copy link

musicformellons commented Mar 5, 2018

To be redirected to facebook oauth using feathers social auth I need to put an href to /auth/facebook. When I do this in my vue SPA (including vue-router) I get a 404 as the route does not exist. How should this link/ redirect be done?

@musicformellons musicformellons changed the title Using /auth/facebook give a 404 from vue-router Using /auth/facebook gives a 404 from vue-router Mar 5, 2018

@daffl

This comment has been minimized.

Copy link
Member

daffl commented Mar 6, 2018

If your Vue app is running on a different server, it is worth noting that the auth/facebook has to link to the Feathers API server (default in development: localhost:3030/auth/facebook), not the frontend server. The only other reason is that authentication-oauth2 hasn't been set up properly. Running your application with the DEBUG=feathers* environment variable set should show

Registering 'facebook' Express OAuth middleware

On the console during startup.

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 6, 2018

Thanks for the suggestions, it got me further! My api does indeed run on a remote server, so using a link to http://remoteserverdomain:8089/auth/facebook gives me the facebook oauth2 page. For the record, I do not exactly get the facebook debug message. I do get more general messages, e.g.:

  feathers-authentication:express:expose-headers Registering exposeHeaders middleware +0ms
  feathers-authentication:express:expose-cookies Registering exposeCookies middleware +0ms
  feathers-authentication:middleware:set-cookie Registering setCookie middleware +0ms
  feathers-authentication:middleware:success-redirect Registering successRedirect middleware +0ms
  feathers-authentication:middleware:failure-redirect Registering failureRedirect middleware +0ms

Anyway, for both facebook and google I get their oauth2 pages, but on continuation I get:

error:  NotAuthenticated: invalid signature
    at new NotAuthenticated (/home/usr/api_server/node_modules/@feathersjs/errors/lib/index.js:93:17)
    at app.authenticate.then (/home/usr/api_server/node_modules/@feathersjs/authentication/lib/hooks/authenticate.js:76:31)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)

As mentioned here, I suppose this is a Cross-domain issue. As I want to keep the cookie, I try using the proxy API requests approach.

My app.js with PROXY on the remote server:

const path = require('path');
const favicon = require('serve-favicon');
const compress = require('compression');
const cors = require('cors');
const helmet = require('helmet');
const logger = require('winston');

const feathers = require('@feathersjs/feathers');
const configuration = require('@feathersjs/configuration');
const express = require('@feathersjs/express');
const socketio = require('@feathersjs/socketio');

const proxy = require('http-proxy-middleware');

const middleware = require('./middleware');
const services = require('./services');
const appHooks = require('./app.hooks');
const channels = require('./channels');

const sequelize = require('./sequelize');

const authentication = require('./authentication');

const app = express(feathers());

app.configure(configuration());

// PROXY
app.use('/auth', proxy({
  target: 'http://remoteserverdomain:8089/',
  changeOrigin: true,
  logLevel: 'debug'
}));

app.use(cors());
app.use(helmet());
app.use(compress());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(favicon(path.join(app.get('public'), 'favicon.ico')));

app.use('/', express.static(app.get('public')));

app.configure(express.rest());
app.configure(socketio({
  wsEngine: 'uws',
  timeout: 120000
}));
app.configure(sequelize);

app.configure(middleware);
app.configure(authentication);
app.configure(services);
app.configure(channels);

app.use(express.notFound());
app.use(express.errorHandler({ logger }));

app.hooks(appHooks);

module.exports = app;

The default.json on the remote server:

{
  "host": "localhost",
  "port": 8089,
  "public": "../public/",
  "uploads": "../uploads",
  "paginate": {
    "default": 10,
    "max": 50
  },
  "authentication": {
    "secret": "secret",
    "strategies": [
      "jwt",
      "local"
    ],
    "path": "/authentication",
    "service": "users",
    "jwt": {
      "header": {
        "typ": "access"
      },
      "audience": "http://localhost:8080",
      "subject": "anonymous",
      "issuer": "feathers",
      "algorithm": "HS256",
      "expiresIn": "1d"
    },
    "local": {
      "entity": "user",
      "usernameField": "email",
      "passwordField": "password"
    },
    "google": {
      "clientID": "secret",
      "clientSecret": "secret",
      "callbackURL": "http://localhost:8080/auth/google/callback",
      "successRedirect": "http://localhost:8080/",
      "scope": [
        "https://www.googleapis.com/auth/userinfo.profile",
        "https://www.googleapis.com/auth/userinfo.email"
      ]
    },
    "facebook": {
      "clientID": "secret",
      "clientSecret": "secret",
      "callbackURL": "http://localhost:8080/auth/facebook/callback",
      "successRedirect": "http://localhost:8080/",
      "scope": [
        "public_profile",
        "email"
      ],
      "profileFields": [
        "id",
        "displayName",
        "first_name",
        "last_name",
        "email",
        "gender",
        "profileUrl",
        "birthday",
        "picture",
        "permissions"
      ]
    },
    "cookie": {
      "enabled": true,
      "name": "feathers-jwt",
      "httpOnly": false,
      "secure": false
    }
  },
  "postgres": "postgres://user:pw@localhost:5432/db"
}

I think this is in line with the mentioned tutorial, but I get:

[HPM] Error occurred while trying to proxy request /auth/facebook from pqbq.xyz:8089 to http://pqbq.xyz:8089 (ENOTFOUND) (https://nodejs.org/api/errors.html#errors_common_system_errors)

Trying to solve this - does not make sense to the same url... ?! - I change the proxy into (the target matches the url of the frontend):

// PROXY
app.use('/auth', proxy({
  target: 'http://localhost:8080/',
  changeOrigin: true,
  logLevel: 'debug'
}));

Now the url in the browser reads remoteserverdomain:8089/auth/facebook and the page gives "Not Found" and in the remote server console:

error:  NotAuthenticated: invalid signature
    at new NotAuthenticated (/home/usr/api_server/node_modules/@feathersjs/errors/lib/index.js:93:17)
    at app.authenticate.then (/home/usr/api_server/node_modules/@feathersjs/authentication/lib/hooks/authenticate.js:76:31)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:160:7)
[HPM] GET /auth/facebook -> http://localhost:8080/

Any idea how to solve this/ get the proxy working?

@daffl

This comment has been minimized.

Copy link
Member

daffl commented Mar 6, 2018

https://gist.github.com/marshallswain/3c9e5b3b177b977468b5b711b6254f67 has a recipe for allowing oAuth authentication across different domains.

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 6, 2018

Yeah, that is the alternative mentioned, but then I quit using cookies, right? Are you implying the 'proxy method' won't work with a remote server? Do you prefer the querystring method?

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 6, 2018

Mmmh, sorry, I deleted my comment re Nginx proxy, that was another process on the remote server and thus irrelevant.

@claustres

This comment has been minimized.

Copy link

claustres commented Mar 6, 2018

I have a working setup with a different server from the client app but not with facebook (google, github and onelogin). My proxy config is pretty similar to yours:

proxyTable: {
      '/api': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        logLevel: 'debug'
      },
      '/apiws': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        ws: true,
        logLevel: 'debug'
      },
      // The auth endpoints are not easy to prefix so we manage it manually
      '/auth': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        logLevel: 'debug'
      }
    }

My client app is running on the server localhost:8080 and my API localhost:8081. My OAuth2 config:

google: {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: 'http://localhost:8080/auth/google/callback',
      successRedirect: 'http://localhost:8080/',
      failureRedirect: 'http://localhost:8080/#/login?error_message=xxx',
      scope: ['https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email']
    },
    // Required for OAuth2 to work correctly
    cookie: {
      enabled: true,
      name: 'feathers-jwt',
      httpOnly: false,
      secure: false
    }
@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 6, 2018

Tried the querystring method. I get first oauth screen. Upon continuation my browser address url becomes:

http://localhost:8080/auth/facebook/callback?code=4%2FAAClbxfBNM2G3F-FQx7FPtjr9O_bTBGJOhtCQXXw0FSH7tUU5chWQXEgE_TRnVvJCTBNHNlD70vSjjGRh77A

And this 'page' return a 404.

@claustres

This comment has been minimized.

Copy link

claustres commented Mar 6, 2018

On which domain/port is your fronted running ? When you showed your app.js was it the one of your frontend ? Because it contains both the proxy setup, which should be on the frontend, and the feathers setup, which should be on the API, or your frontend and API are running both Feathers ? Sorry but it is not really clear...

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

@claustres That's it! The proxy code must be in the client. The proxy code in your tutorial is straight after some backend code and all http-express-middleware examples are with express, which to me is confusing. Thanks for helping me out!

As I am using Quasar (0.15.6) I can set the dev proxy for the client in quasar.conf.js like this:

    devServer: {
      proxy: {
        '/auth': {
          target: 'http://remoteserverdomain:8089/',
          changeOrigin: true,
          ws: true,
          logLevel: 'debug'
        }
      }
    },

I then get after continuing after the first oauth screen, "Oh no, something went wrong" in the browser, and on the server:

error:  BadRequest: notNull Violation: users.email cannot be null,
notNull Violation: users.password cannot be null
    at new BadRequest (/home/usr/api_server/node_modules/@feathersjs/errors/lib/index.js:86:17)
    at errorHandler (/home/usr/api_server/node_modules/feathers-sequelize/lib/utils.js:13:25)
    at tryCatcher (/home/usr/api_server/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:689:18)
    at Async._drainQueue (/home/usr/api_server/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/home/usr/api_server/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/home/usr/api_server/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:757:18)
    at tryOnImmediate (timers.js:718:5)
    at processImmediate [as _immediateCallback] (timers.js:698:5)
  feathers-authentication:middleware:failure-redirect Clearing old 'feathers-jwt' cookie +1m
error:  BadRequest: notNull Violation: users.email cannot be null,
notNull Violation: users.password cannot be null
    at new BadRequest (/home/usr/api_server/node_modules/@feathersjs/errors/lib/index.js:86:17)
    at errorHandler (/home/usr/api_server/node_modules/feathers-sequelize/lib/utils.js:13:25)
    at tryCatcher (/home/usr/api_server/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/usr/api_server/node_modules/bluebird/js/release/promise.js:689:18)
    at Async._drainQueue (/home/usr/api_server/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/home/usr/api_server/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/home/usr/api_server/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:757:18)
    at tryOnImmediate (timers.js:718:5)
    at processImmediate [as _immediateCallback] (timers.js:698:5)

This might be a step forward...!? Any idea how to tackle these errors? I thought setting email and pw should be taken care of 'automatically', but looks like I have write a hook?

@claustres

This comment has been minimized.

Copy link

claustres commented Mar 7, 2018

You have to extract the email from the github profile, there is a part about it in my article.

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

Yeah, cool, this looks like it is going to work, thanks! I will close this issue as soon is I have it working.

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

@claustres It works now with this hook:

module.exports = function (options = {}) {
  return function processFacebookProfile(hook) {
    if (hook.data.facebook) {
      hook.data.email = hook.data.facebook.profile.emails[0].value;
      hook.data.password = 'defaultpassword';
    }

    return Promise.resolve(hook);
  };
};

What should I put as password?

@claustres

This comment has been minimized.

Copy link

claustres commented Mar 7, 2018

No password, it is managed by your OAuth provider (eg Facebook)

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

But then I get the "oh no, something went wrong" page and server has error:
error: BadRequest: notNull Violation: users.password cannot be null

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

Ah, the error comes from my sequelize user model. When I set allowNull: true for the password there is no error. Isn't that tricky though, as the local authentication does always need a password!?

@claustres

This comment has been minimized.

Copy link

claustres commented Mar 7, 2018

you need a finer control, DB rules are app-wide, you can check using a hook with local auth and by-pass it with external auth providers.

@musicformellons

This comment has been minimized.

Copy link
Author

musicformellons commented Mar 7, 2018

Yeah, will do! Thanks!

@ivkosoft

This comment has been minimized.

Copy link

ivkosoft commented Feb 3, 2019

Tried the querystring method. I get first oauth screen. Upon continuation my browser address url becomes:

http://localhost:8080/auth/facebook/callback?code=4%2FAAClbxfBNM2G3F-FQx7FPtjr9O_bTBGJOhtCQXXw0FSH7tUU5chWQXEgE_TRnVvJCTBNHNlD70vSjjGRh77A

And this 'page' return a 404.

Did you find solution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.