OlivierBlanvillain / scalajs-transport

MIT License
25 stars 4 forks source link

Session handling #3

Open dejvid opened 9 years ago

dejvid commented 9 years ago

Can someone please explain how to check for valid session before websocket is initialized in project example/rpc ? I want user to log in before using ws stuff.

OlivierBlanvillain commented 9 years ago

Hi, sorry for the late reply. That's definitely something missing from the project at the moment. I've commited a possible solution to a new branch, let me know it that works for you.

dejvid commented 9 years ago

I think this should do it. I'll try.

mkotsbak commented 9 years ago

How is this used? Is it possible to know who the authenticated identity is from the API handlers?

OlivierBlanvillain commented 9 years ago

This change exposes the RequestHeader of the current request, which contains all the user data send along with the request (mainly cookies).

Let me know if you can make something useful out of this, I publish it as 0.11 or something :)

mkotsbak commented 9 years ago

Ah, you authenticate at the web page that initiate the socket connection first japp. Then the Session variable in RequestHeader should be possible to use to check if any user is authenticated.

But the branch referenced above, does it require the protocol to use Akka actors? And how to get the RequestHeader from inside the API implementation?

OlivierBlanvillain commented 9 years ago

But the branch referenced above, does it require the protocol to use Akka actors?

No. Play uses Akka internaly, but you don't have to use actors on your code.

And how to get the RequestHeader from inside the API implementation?

When you wire your Play action with the transport (here for example), instead of

val sockJS = sockJStransport.action()

you can do

def authenticate(r: RequestHeader): Boolean =
  r.cookies.get("username").contains("Alice".==)

val sockJS = sockJStransport.action(authenticate)
mkotsbak commented 9 years ago

Oki, japp, that solves the autentication, but how to get the username inside for example "def list(path: String): Seq[String]" in https://github.com/OlivierBlanvillain/scalajs-transport/blob/master/examples/rpc/jvm/app/controllers/Application.scala#L32 ? It might be that you want to let the user get only its own data.

OlivierBlanvillain commented 9 years ago

I'm not sure I understand your question... Couldn't you simply send this information by the connection?

You could change the server side to:

object Server extends Api { 
  def list(path: String, username: String): Seq[String] = ???
}

And the client side to:

Client[Api].list(inputBox.value, "Alice").call().foreach { ... }
mkotsbak commented 9 years ago

Well, yes, it adds some redundancy, but the problem is that you have to trust the client. If the client is authenticated and you want to authorize it to view its data, it could lie about its username (being authenticated with another username) and then access other users data.

Then either the Api implementation needs to verify the username matches or the socket authentication verify the username in the method parameter.

What is needed is something like this in Spring:

Authentication auth = SecurityContextHolder.getContext().getAuthentication(); String name = auth.getName(); //get logged in username

Is that possible in Play via some implicit variables or globals?

OlivierBlanvillain commented 9 years ago

Well, yes, it adds some redundancy, but the problem is that you have to trust the client. If the client is authenticated and you want to authorize it to view its data, it could lie about its username (being authenticated with another username) and then access other users data.

I think the situation is not different than HTTP. If you give the client a session token, keep a map of tokens to username, and transmit this token with every request/remote procedure call, you should get to something as secured as your online bank account (assuming you use a secured connection). I guess this is what Spring is doing on your example, except than the data is passed around via cookies instead of explicit function arguments.

Then either the Api implementation needs to verify the username matches or the socket authentication verify the username in the method parameter.

I would say that's out of the scope of this project. You might want to check out some of the Play authentication projects, to see if one fits your needs and is not too hard to integrate with scala.js:

https://github.com/t2v/play2-auth https://github.com/mohiva/play-silhouette https://github.com/jaliss/securesocial

mkotsbak commented 9 years ago

Hmm, the case here is that we have a request to Play that is first authenticated, open a socket stream and then through that sockets the API calls are made. Given that we can trust the connection (SSL probably could be used here to ensure that), the RpcWrapper needs to save the authenticated username and possibly roles from any of the above mentioned authentication plugins.

Then when afterwards getting API calls through the socket, maybe it could supply the username/roles saved through implicit variable(s) that could be used by the API methods that needs them?

OlivierBlanvillain commented 9 years ago

That sounds reasonable. I was thinking about something more ad-hoc like in the example above where I added a "username: String" argument to all RPC functions, but with a secured authentication argument, such as a token.

Keep in mind that at the moment, everything is decoupled. You can freely swtich transport implementation and still use the same RPCWrapper. I'm no sure if we could make something at the level of the library that would be more convenient than function arguments, but still flexible enough to work on top of Play, Netty and two browsers using WebRTC.