Goals:And for bonus points:Desired User Flow[This is pretty standard for web services] ComponentsJAASTo make it easy to add authentication systems to wave-in-a-box, we use Java's JAAS service to do authentication. [Java Authentication and Authorization Service - Wikipedia] People with custom authentication needs should be able to configure their own JAAS LoginModule and easily hook it up to wave-in-a-box. A password based JAAS login module is provided and used by default. This stores a password digest in each user's account information object. Session dataLike any other web service, once a user is authenticated, they are provided with a session token in an HTTP cookie. The user provides this cookie during each subsequent HTTP request. The server uses this token to evaluate the user's identity.Authentication servletThe wave in a box server contains a servlet which performs authentication. Calling GET on this servlet's url (by default /auth) presents the user with a login form. The form POSTs authentication information to the server. The server uses its JAAS configuration to check the provided credentials and, if valid, generate a session token and provide it to the user.JAAS login modules provide no standard method for generating a web form, so the form must be configured manually. A standard form which asks for a login address and password is configured by default. An optional redirect URL can be provided. When the user authenticates, the server redirects the user's browser to this URL. Authenticating the websocket connectionAs of websockets draft v76, websockets support cookie-based authentication like any other HTTP service. However, websockets don't have differentiated error messages. We will add an Unauthenticated error message to the protocol, which tells the client that they cannot access information without re-authenticating.ImplementationInteresting new classes:org.waveprotocol.---.authentication. SessionManager - Helper code for sessions, which allows any servlet to discover which user is logged in.org.waveprotocol.---.rpc. AuthenticationServlet - Servlet for authenticating users. GET fetches the login HTML page, POST sends authentication information to JAAS. Considered, and rejected approachesUse Jetty's JAAS moduleJetty provides a JAAS authentication container (Jetty/Feature/JAAS). This does much of what is described above, which could simplify the code involved. However:If we want people to be able to provide their own authentication modules, a lack of documentation is actually a pretty big problem. Use stateless, signed session tokensInstead of generating an unguessable string and using that as the session token, its possible to create a session token by signing the user's session data (username, signed in date, ..?) with a certificate. Doing this means the server doesn't have to store any state for the user's session, which should make scaling a bit easier and sessions would persist across server restarts.It also bloats out the size of the session token from ~20 bytes to ~200 bytes, and requires a bunch more crypto code. (It is dangerous to implement this yourself.) The standard authentication token system should be fine. We could revisit this technique later. Use OpenID, Google Friend Connect, Facebook Connect, or something similarOpenID is a standard authentication system which allows a user to authenticate to a single host, and then direct other sites (like wave in a box) to use the authentication information at that site to log them in.Wave in a box could use OpenID as a way to authenticate users. All wave users will have a wave address anyway ('joseph@example.com'), but the wave in a box frontend could authenticate users to their wave address using OpenID authentication. The same is true of Facebook Connect, Google Friend Connect, etc. This would be really nice for users, because they wouldn't to need to remember a new password for wave in a box. However, even if we used OpenID for authentication, we would still need a local user database (to associate the OpenID URLs with local usernames). We would also still need a simple password-based authentication system so the system works out of the box. The best way to implement OpenID-based authentication would (architecturally) be almost identical to the authentication system described above. We would just need to add an OpenID-based login module, and a login form which allows the user to elect to login via OpenID. The same is true of FacebookConnect, or any of the other similar systems. The architecture described above will support any of these other authentication systems if anyone wants to write an authenticator for them. Other authenticators are welcome in the source tree. An aside: XMPP-based AuthenticationUnfortunately, the XMPP component API doesn't provide any way to use the XMPP server to authenticate users. This would be ideal for installations which already use XMPP, as all the XMPP users would magically become valid wave users out-of-the-box.There is a nasty workaround which can be implemented to make this work: We could create a custom LoginModule that takes the user's authentication information and makes a login attempt as that user on the connected XMPP server. If the XMPP server allows the login attempt, we disconnect and allow the wave login attempt. This is nasty architecturally, but should be secure and stable. It would also put pressure on the XMPP standards body to provide a method to do authentication through the component API. It would be great for somebody to add this to wave in a box as a custom authenticator, if XMPP authentication is important enough to your users. |