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

Server-Side AuthTokens/Sessions #27

Open
khoerling opened this issue Feb 11, 2016 · 7 comments
Open

Server-Side AuthTokens/Sessions #27

khoerling opened this issue Feb 11, 2016 · 7 comments

Comments

@khoerling
Copy link
Contributor

Hey Mike! For applications requiring social integration with FB/Twitter, etc... server-side auth token generation is needed. What are your thoughts on either:

  1. Sending the authToken to the client on socket connection (less secure), which can be then used to filter rows per your examples.
  2. Extending server-side validations (so client needn't know its own auth token) to automatically filter only rows containing the authToken for specific queries, eg:

// client observe
myMessages: new QueryRequest({r.table('messages'), validateAuthToken: true, changes: true, initial: []})

// server validate
r.table('messages')
 .changes({include-initial: true})
 .filter(r.row('authToken').eq(session.authToken))  // automatically filtered

Is this already possible?

Really digging your software,
Keith

@mikemintz
Copy link
Owner

@khoerling Could you elaborate and give a more specific example? I don't quite follow how it's possible for a client not to store any sort of authToken, or else how would the server confirm the client is logged in?

If you just mean ordinary authentication (not OAuth with a third-party provider), the chat example app shows how to do authentication. There are a few separate ajax routes to do bcrypt for login/signup, but in general the client sends the authToken at the beginning of the websocket connection, the server validates it on session creation, and then the server knows which user is currently connected on all queries in the session. I don't believe this is insecure, since as long as you use https/wss, that's the traditional way of authenticating with cookies or HTTP authentication headers.

If you're thinking of OAuth, then it depends a lot on the use case, but we'd have to write custom logic to request the access token from the third party provider, and either store it on the server or send it back to the client. If we store it on the server, we still need a way of knowing that a particular client is authenticated with us. If we don't send the client any kind of authToken to store, then they'd have to re-authenticate with OAuth every time they reconnect. We can send the client an internal authToken to store (like in the chat example) so the client doesn't know our OAuth access token.

Not sure if that's what you were asking about, so let me know a more concrete example if I'm misunderstanding.

@khoerling
Copy link
Contributor Author

Yes, the client will store a server-generated token. This would be compatible with OAuth and many npms like express-cookie-session. Rather than send that token to the client via an additional HTTP/GET request, would it be possible to send it over the websocket on connection?

It would be clean to have a backwards compatible way of pulling this off. If the authToken is inside the sessionPromise, then that whole session object could be sent-- thoughts?

khoerling added a commit to DimensionSoftware/instant-skeleton that referenced this issue Feb 13, 2016
- better to do this over the socket, on connection: mikemintz/react-rethinkdb#27
@mikemintz
Copy link
Owner

I think I see what you're getting at, but I'm still having trouble understanding the exact use case. Could you describe what you imagine the messages sent back and forth between client and server would look like, so I can better understand how it might fit in?

@khoerling
Copy link
Contributor Author

Absolutely and thank you for following:

  1. client connects to server, gets cookie'd session (eg. express-cookie-session)
  2. client rethink-websocket-client connects to server
  3. server immediately sends session (returned from sessionCreator) over the websocket (includes authToken)
  4. client makes rethinkdb queries using authToken

I sent a pull-request for a simple change to pass websocket headers through to the sessionCreator, so it can be securely matched up with a cookie'd session:
mikemintz/rethinkdb-websocket-server#13

The remaining bit is sending that session over the websocket to the client. Any thoughts on the cleanest integration?

@mikemintz
Copy link
Owner

I get that people may want to use cookies instead of url params to pass the session key. I just made a couple comments on your PR, but otherwise good to merge.

I agree it's a bit tricky to send session data back to the client. I have some thoughts, but it's a lot of complexity which seems like it can be solved by just having the developer add a single ajax route that looks at the cookie and returns session data to the client. I expect most production apps are going to have to have a few out-of-band ajax routes like this anyway, e.g. for bcrypt. Would having that ajax route solve your particular use case? Are there other use cases that it doesn't work for? Or is it just for the convenience of not having to define separate routes?

I don't think it makes sense to always send the session (from sessionCreator) to the client, since it may have sensitive data the server doesn't want the client to have, e.g. the full User object.

For your particular use case, I think it makes more sense to have the server send the client the userId instead of the authToken, since the userId is what they would use in queries to filter to only the data they should have access to. Of course that depends on your particular schema, but generally the sensitive information will only be stored once, associated with the less sensitive userId which is stored everywhere else.

You'd also mentioned having the server automatically modify queries to filter for authToken. Is that equivalent to mikemintz/rethinkdb-websocket-server#12 ? If so, let's continue that particular discussion there, since that sounds like a very different approach.

@khoerling
Copy link
Contributor Author

Exactly-- userId would be best (or any bit of non-sensitive user-identifying data to start a session) and it'd be much faster to reuse the socket for sending that back to the client in a single shot on connection. Do you think mimic'ing a ReQL query result on the way up to the client would be the best tactic?

Yes, mividtim's automatic filters precisely describe 2. above!

@mikemintz
Copy link
Owner

I think the cleanest way to reuse the socket to send back information would be having before-execute query callbacks like in mikemintz/rethinkdb-websocket-server#2 and also add support for sending back "fake" query results.

For example, here's some pseudo-code we might want to allow in the whitelist:

r.db('fake').table('fake').insert(RP.ref('command')).intercept((refs, session) => {
  const command = refs.command;
  if (command.cmdType === 'getUserId') {
    const resultRows = [{userId: session.userId}];
    return Promise.successful(resultRows);
  } else if (command.cmdType === 'encryptPassword') {
    [...]
  }
  return Promise.reject('invalid command');
});

Then in rethinkdb-websocket-client, one could write something like:

runQuery(r.db('fake').table('fake').insert({cmdType: 'getUserId'})).then(rows => {
  const userId = rows[0].userId;
  this.setState({userId: userId});
});

Does that kind of API (along with your PR) work for the use case? If so, let's continue discussing it at mikemintz/rethinkdb-websocket-server#2

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

No branches or pull requests

2 participants