feathersjs / feathers

The API and real-time application framework
https://feathersjs.com
MIT License
15.04k stars 751 forks source link

Does feathers support traditional sessions? #672

Closed jtlapp closed 7 years ago

jtlapp commented 7 years ago

Does feathers support traditional sessions? (Rather than just JWTs?)

I cannot find the answer to this question anywhere. I attempted to get into Slack to ask there, but Slack tells me that I'm a robot.

daffl commented 7 years ago

Yes. Since you can use any Express middleware with Feathers you can just use Express usual session mechanisms and then map it as a service parameter to e.g. req.feathers.session.

The easiest way to get session-like functionality however is to enable the authentication cookie (done automatically when using oAuth):

"authentication": {
    "cookie": {
      "enabled": true,
      "name": "feathers-jwt",
      "httpOnly": false,
      "secure": false
    }
}

And then adding the cookie parser and the Express authentication middleware before routes that should be accessed by a "session". Because the browser always sends the cookie automatically this is kind of a session while still remaining stateless, so you don't have the pain of figuring out session storages etc:

const cookieParser = require('cookie-parser');
const { authenticate } = require('feathers-authentication').express;

app.get('/my-route',
  cookieParser(),
  authenticate('jwt'),
  function(req, res) {
    console.log(req.user);
    res.end('This will only show if there is a valid JWT in the cookie');
  }
);

We still recommend against enabling this for all API routes since it is much easier to capture a browser/session than it is to get the JWT.

jtlapp commented 7 years ago

Thank you!

mogadanez commented 6 years ago

how to use session from custom service?

module.exports = function (options1 = {}) {
    debug('service being configured.');
    const options = Object.assign({}, optionsDefault, options1);

    return function () {
        return myService(options, this);
    };
};

function myService (options, app) { 
    debug('service initialized');
    options.app = app;

    options.app.use(options.path, {
        find (data) {
            var session= options.app.session; //???

        }
    });
}
daffl commented 6 years ago

Add it as params via req.feathers.session in a middleware: https://docs.feathersjs.com/api/express.html#params

mogadanez commented 6 years ago

I have code as above

const cookieParser = require('cookie-parser'); const { authenticate } = require('feathers-authentication').express;

app.get('/my-route',
  cookieParser(),
    (req,res,next)=>{
        // feathers-jwt cookie is present in req
        next()
    },
  authenticate('jwt'),
  function(req, res) {
    console.log(req.user);
    res.end('This will only show if there is a valid JWT in the cookie');
  }
);

cookies is enabled in config

  "authentication": {

    "cookie": {
      "enabled": true,
      "name": "feathers-jwt",
      "httpOnly": false,
      "secure": false
    },

but it any way show me 401 Not Authorized Is something else missing?

mogadanez commented 6 years ago

looks like feathers-jwt not have cookie extractor by default https://github.com/feathersjs/authentication-jwt/blob/master/lib/index.js#L51

daffl commented 6 years ago

Yes, the Express middleware has to be updated parse the cookie if available and set it so that the JWT authenticator can parse it.

TimNZ commented 6 years ago

Appreciate the great work you have done and time put in.

Figuring out what to do is frustratingly confusing though. You have built a great framework with a lack of decent examples and/or documentation for common real world scenarios.

A little bit of time from you creating short examples is a huge amount of time saving for us, beginners and even advanced users, please consider this. Authentication is most probably one of the most common area issues are raised against.

In previous frameworks I have used req.user just works with cookies out of the box when calling a url.

I'm supporting local and Facebook strategies and this has been very challenging to figure out the flows and config.

I've ended up with a feathers-jwt value in localStorage set for local strategy which is different from the feathers-jwt cookie which is set on accessing /auth/facebook or /auth/facebook/callback.

I am supporting local and facebook (oauth2) strategies.

What is a working way today to get the server to set req.user based on the feathers-jwt cookie existing?

Is it the @feathersjs/authentication-jwt that needs to be modified to pull out the JWT from feathers-jwt cookie? Is passport-cookie needed?

I don't know. Apologies for rambling, but I've spent a bit of time on this and it's hard to figure out things if the docs don't cover them and you aren't an Express/Passport expert.

TimNZ commented 6 years ago

I use standard client.authenticate() in browser with local login (email,password) it sets 'feathers-jwt' in localStorage (as the storage I am using). I'm using the chat example modified to work with Buzzard release.

This means when I use oauth2 strategy by going to /auth/facebook, another JWT is generated which is different from what the user already has in their browser, and stored in a cookie.

Subsequently loading the client again it uses the localStorage feathers-jwt first, fallback to using the cookie. https://github.com/feathersjs/authentication-client/blob/master/lib/passport.js#L262

What would be the right approach to support this mixed auth so the same JWT is on the client regardless of authentication mechanism?

When the client logs in using 'login' strategy, and I then allow them to authorise via /auth/facebook, there is no 'feathers-jwt' cookie

It seems we need the cookie to be set on client login, not just in storage, or alternatively to storage. Or maybe cookie is checked first before storage?

The other issue is that authentication-oauth2 Verifier is not called if there is already an authenticated user, so the existingUser reference in lib/express/verifier.js in @feathers/authentication-oauth2 doesn't make sense to me as the verifier would never be called.

All of this affects being able to

I can't see anything in this blog that is different from what I've already tried.

https://blog.feathersjs.com/how-to-setup-oauth-flow-with-featherjs-522bdecb10a8

Let me know if I need to simplify what I'm saying to make sense, or it makes sense and you have some suggestions to resolve, look forward to solving this so I can make a sample app to share and save people pain.

TimNZ commented 6 years ago

After a 1hr break and letting the days efforts marinade and reading the blog post again I'm feeling I might have this under control now.

I'll extract out what I create into a starter kit when it's humming.

kigenofkenya commented 6 years ago

Eagerly awaiting your findings TimNZ

TimNZ commented 6 years ago

@daffl has updated authentication-jwt to re-introduce parsing of cookies for JWT tokens.

For now I'm going to use express-session for sessions in addition to JWT, and might do this via a new feathers-sessions module to connect JWT authenticated user with the session. In the future you can easily revoke a JWT along with the session

I'm also going to use oauth module to do standalone oauth2 flow outside of the feathers/passport authentication flow, to link social accounts to existing user sessions.

This is due to Authentication not processing any more strategies once a user is authenticated.

The mix of SSR with REST/Socket authentication scenarios is a bit painful right now for certain scenarios. I will hopefully come up with something that handles that as a module as well.

FeathersJS is a nice wrapper around Express adding a more robust authentication framework, service oriented API, and mixed REST and Socket support for calling services regardless of initiator, but it's not quite perfect yet.

I will enable feathers-sync with Redis for scaling.

If I get that humming nicely I'll publish that as my ideal starter kit that others may want to use.

TimNZ commented 6 years ago

The server app.on('login/logoff') events can be used to hook authentication events, where the accessToken can be extracted.

Since auth can occurs outside the Express pipeline via websockets, I can't get a session id to do any persisted session data management i.e. link the JWT to the session.

On the client side, as long as httpOnly = false, you can grab the session cookie id and pass it to the authenticate() call to send to the server and hook into that perhaps.

Will continue to play to understand the architecture and nail down my scenarios to cater for and how to support them.

mflalor commented 6 years ago

@TimNZ Did you ever publish your "starter kit"? Sounds interesting and I'd like to have a look if possible? Cheers

lock[bot] commented 5 years ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue with a link to this issue for related bugs.