Using cookie authenticator with multiple cookies


#1

Hi

I’m currently integrating silhouette into our frontend application. Before we simply used Play’s session to store an identifier and a token to use when querying against our backend system. We are planning on adding a “Remember Me”-functionality, but it is not the cookie-cutter kind, where you can just alter the cookies expiration.

The issue I am having, is that we use a third-party authentication service not a social site, think government. And now I’m using the CookieAuthenticator instead of Play’s session handling. I’m using the stateful cookie approach, since I have to send the token to the backend system for it to authenticate the request. And I therefore can’t one way hash the token on the frontend server. Therefore I’m serializing information to JSON to store in the LoginInfo providerKey. (which feels kind of like a hack, but I have no other way to store information in the cookie.)

Well the migration of that part works fine, but now I want to issue a Remember Me Cookie in addition to the other cookie. But this isn’t supported by Silhouette? The only way to specify which cookie is handled is when the CookieAuthenticatorService is created. The reason I want two cookies, is that the “Remember Me”-functionality only lets you use our login, instead of using the third-party authentication service. I’m not much for mutating the cookie excessively, and therefore would rather have two cookies, is this the wrong mindset?

I thought at first I’d just make two different Environments one for the Login cookie, and one for the Remember Me cookie. But injecting two Silhouette Stacks with different Environments into a controller doesn’t seem intended. Also how would I then handle the SecuredAction for the one cookie compared to the other.

At the moment I’ve done a crazy thing, where my Identity is a case class with just one value which is an Either, and I’ve then made a RequestProvider which checks if the Remember Me cookie is set and the other cookie isn’t, which then returns a Left. The problem with this is it makes any kind of handling in my SecuredActions an absolute mess.


#2

Hi,

If you need two cookies created in the authenticator, then it’s better to create a custom authenticator service. As you said, all other is a kind of hack.

But I’m not sure if I understand your use case correctly! Especially the part with the authenticator. You say that you store a token in the authenticator, which will be used to authenticate to the backend server. Is this correct? Normally, the authenticator is only used to identify a logged in user to the current system and not to a backend system. What you need is a provider implementation to your third-party authentication service. The token this third-party authentication service does return after a successful authentication, can then be stored in the AuthInfo implementation of your custom provider. The AuthInfo has a relation to a LoginInfo, so if you need the token for a user you can query the AuthInfo for the LoginInfo stored in your cookie.

Also, on login a user authenticates to your third-party authentication provider with the help of your custom provider. Your provider creates an AuthInfo for your token and stores it in relation to the LoginInfo. The provider returns then the LoginInfo for which you can create an authenticator(Cookie). On subsequent requests, the cookie authenticates the user against your frontend application. If you need to access your backend API, then you can query the AuthInfo(token) with the LoginInfo. You can cache the AuthInfo so that you do not need to query it on every request.

Does this mage sense to you?

Best regards,
Christian


#3

Thank you for your wonderful answer, Christian.

I’ve sorted most of my problems by switching to a SessionAuthenticator and a CookieAuthenticator approach. So when authenticated the SessionAuthticator is active, while the CookieAuthenticator serves purely as a RememberMe-functionality.

I do have one new questions if you don’t mind, not completly related.

When you have two Silhouette Environment Stacks in your controller, I’ve made an Action like this:

def someAction = (firstSilhouetteStackEnv.UnsecuredAction andThen secondSilhouetteStackEnv.UserAwareAction) { implicit request => ....

When I do this the type of request should be UserAwareRequest[Env, AnyContent], IntelliJ agrees with this, but compile fails, since it is raised to a WrappedRequest[A]. When I debug I can see it is a UserAwareRequest[Env, AnyContent] and if I cast it as so with asInstanceOf[] it works, but it is not a very nice solution. Any idea what is going on and how to solve it?

Best regards and thanks again,
Alexander


#4

Sorry for the late answer. Both actions UnsecuredAction and UserAwareAction are not compatible. The UnsecuredAction handles a normal Play Request and the UserAwareAction handles a UserAwareRequest. Anyway this combination doesn’t make sense. What do you try to achieve?

I’ve the strong feeling that you try to bend Silhouette to fit your needs. I’ve suggested the correct approach in my first answer. But you’ve entirely ignored it.

Best regards,
Christian