Authenticating users with Dream - mTLS, passkeys/WebAuthn

Hi,

I wonder whether Dream allows mutual TLS authentication and if not if it is in the plan ?

Thanks.

Can you give more details about what you mean? Eg can you show a web framework that does mTLS and if so how it does it?

I don’t know any web framework at all, to make things clear. I am learning one with Dream.

I know mTLS exists, and I wonder why this isn’t (much ?) used for the web. I understand there is a public key issue among other things, but I was thinking maybe instead of sharing a password when people register for the first time to a website, one could have them generate a CSR, send to the server, signed, and sent back. The user would then have to ‘install’ the certificate which is another issue I guess, but then mTLS would be possible.

I assume mTLS needs to be configured on the server side, and I don’t see that option in Dream documentation hence my question. Though given yours…I feel know I am missing a few things :slight_smile:

There is already an existing web standard technology that works very similarly to this but with actual UI/UX supported by browsers: passkeys. Also known as WebAuthn. See https://webauthn.guide/

There is an OCaml library webauthn 0.2.0 (latest) ¡ OCaml Package that implements WebAuthn support. They have an example in their repo.

But there is a catch: the UI and UX around passkeys is somewhat tricky to get right. For example, passkeys are keypairs so people may lose them and need to recover their account. So you need to provide some account recovery mechanism. Typically this will be ‘login with email’, so effectively for passkeys to work your webapp needs an email provider.

I have been thinking about this problem and want to provide some helpers that wrap the OCaml webauthn package. But of course anyone interested in this topic should look into it on their own as well.

Thanks for the pointers. This looks indeed very similar. But at the end, when the client and the server are authenticated, how does Dream framwork take that into account : this is not clear to me.

Once the user is authenticated, you can use Dream.set_session_field to mark their session as such. Eg, typically, Dream.set_session_field request "email" "bob@example.com". Then in future requests, you can use Dream.get_session_field to check that the email field exists and if so the user is considered as logged in. If not, they are not.

A quick look over the Dream interface it doesn’t seem you get access to client authentication with TLS. As far as I can tell there’s a function val tls : request -> bool to check whether TLS was used in the request. And otherwise there’s a boolean option for enabling TLS in Dream.run and options for specifying key & certificate.

If you have questions about WebAuthn or the webauthn library please feel free to reach out. It’s not a complete match with client certificates, but in some use cases you can get the similar benefits with WebAuthn.

Thinking about it some more I think I investigated this early on when working on builder-web to authenticate our builders when uploading a finished build. But I also started out using Opium instead of Dream. I ended up using basic password authentication (I think client certificates would be nicer).

Ok but the question is what you mean by “their session”. Who tells you which session is what user : the tls layer. So there needs to be some info lifted up from the tls layer up to the application layer in order to rely on trusted things.

More over, it is the server who asks for client certificate, and I see nowhere in Dream api to specify this.

Dream foresees third parties authentications providers, but not mTLS apparently.

I would be interessted in @aantron point of view ?

By ‘session’ in the context of web frameworks people typically mean ‘session cookie’. This is the simplest approach–you set a session field like the email field I showed above, and Dream puts it in the dream.session cookie and puts a Set-Cookie header with this cookie value on the next response. The browser stores the cookie and sends it with every request. You then have a middleware in your Dream server that checks that the session is valid. If it is, you are good to go. If not, you redirect to the login page.

This is all happening through the HTTP layer. The TLS layer just makes sure that nobody can eavesdrop and steal the session cookie.

EDIT: this is explained with a nice example here: dream/example/b-session at master ¡ aantron/dream ¡ GitHub

Sure. But if you do not verify that the cookie you get from the user correspond to the right tls session, you might get abused. Especially if the browser stores the cookie unencrypted.

What would be the point of a session cookie if the TLS layer establishes itself a secure (because mutually authenticated) session ?

Yes, that is a weakness of the session cookie approach. Anyone who has the cookie can masquerade as the logged-in user. Ie what is known as session hijacking. Hence why we use HTTPS to transmit the cookies securely.

But I think you are overestimating the risk of session hijacking. Cookies are actually really good at securing sessions. And Dream takes security very seriously and sets secure cookies by default. This is the same level of security you get with your online bank.

What would be the point of a session cookie

The point is that it’s way easier to use than mTLS, it doesn’t require users to jump through hoops to send their certificates to each server.

Let me put a similar question to you: what’s the point of mTLS for user-facing apps when we have passkeys?

Ok, I think we (or I) reached a common level of understanding on the topic. That’s already an achievement (for me)

I would say that mTLS offers authentication AND confidentiality whereas passkeys only offers authentication. To get confidentiality you need https, and make sure both are smartly integrated. You get it for free in mTLS, whereas you rely on integrators for passkeys.

I have seen Dream is taking sessions security very seriously with CSRF tags and co, but I belief it is less safe than mTLS.

From the user point of view, having to set up a complex password for each website is a nightmare, I guess everyone agrees, even with modern vaults.

I consider setting up a passkey once for authentication is way easier. The same applies for personal certificates, and since it is the same technology underneath (private/public key pairs), I believe there should not be much difference in the implementations. Finally, a certificate is nothing more than a signed passkey…

I think why @yawaramin is suggesting using WebAuthn or cookies is partly because in web browsers client certificates have had pretty bad usability for decades[1][2]. However, it still has merits in some cases. For example, if the users expected to use client certificates are manageable to distribute certificates to, or if it’s an API that is talked to by applications where as well it’s manageable to implement a certificate authority.

Some digging around it seems gluten (which is used by httpun which again in turn is used by dream to abstract over the connection) doesn’t make client certificates (or any other TLS information) available. My guess is there are no immediate plans then as it would involve at least 2 different people in 3 different projects.

[1]: see for example this bug report about Firefox not remembering certificate choice despite the “remember this choice” button. It was unresolved for a decade, and Sir Tim Berners-Lee also chimes in and is very unhappy about the situation: 634697 - Remember user's client certificate selection across sessions
[2]: a big problem is how to request and sign certificates for the client(s). Some(!) browsers had the element for that, but it was removed in Firefox (and earlier in Chrome). Keygen support dropped with Firefox 69 | PKI Platform

3 Likes