kanidm icon indicating copy to clipboard operation
kanidm copied to clipboard

Clientside password feedback

Open tennox opened this issue 11 months ago β€’ 15 comments

Change summary

I've implemented async loading of zxcvbn.js so password feedback can already happen interactively clientside.

Demo

https://github.com/user-attachments/assets/c94a8af4-f0a3-4795-b352-34653639a125

Details

The zxcvbn lib I've downloaded from here as I didn't understand if you have a js package manager workflow: https://raw.githubusercontent.com/dropbox/zxcvbn/67c4ece9efc40c9d0a1d7d995b2b22a91be500c2/dist/zxcvbn.js

The lib will be loaded as <script async> so not to delay page load, and until it's loaded, client-side feedback on the password will not happen. (only check if repeat box has same content)

  • [x] This PR contains no AI generated code
  • [ ] ~~book chapter included (if relevant)~~
  • [ ] ~~design document included (if relevant)~~

tennox avatar May 19 '25 14:05 tennox

I won't support adding an 800KB client-side dependency for this, since we also do additional checks server-side.

yaleman avatar May 20 '25 00:05 yaleman

It's not a dependency - it's loaded in the background asynchronously, so it doesn't delay loading the page. The password form is usable without it, just doesn't give feedback until the lib is loaded.

We could also use a smaller wordlist for clientside - but that would make it inconsistent with the serverside check.

I think the UX for choosing a password, repeating it, submitting form - and only then seeing feedback is quite bad (and having to re-type both passwords and submit for a new attempt). It's well worth loading something in the background in my opinion.

tennox avatar May 20 '25 10:05 tennox

I think the issue here is that we want there to be "one place where this is checked". The worst case would be that the client side rejects a password that the cli accepts, or that the server rejects a password that the client said was acceptable. We have to make trades here, and I think that server-side checking gives us the most reliable method for ensuring that the experience is ultimately consistent. There are possibly ways we can check this or expose this validate api in a better way, but I'm not sure clientside feedback generation is what we want to do here.

The other thing about doing this server side, is we then have an api that other things can use (like the cli) which also needs to have this feedback too.

Firstyear avatar May 21 '25 09:05 Firstyear

Like another option could be to have a "delay" that when typing stops for some small time, we can submit the pw for check to an end point to get the feedback. That way we can get the feedback faster (solving your use case) but also ours (single source of truth).

Firstyear avatar May 21 '25 09:05 Firstyear

@Firstyear The server should never see your plain text password though.

yonas avatar Jun 08 '25 13:06 yonas

@Firstyear The server should never see your plain text password though.

In our case, it should. The server is the one keeping track of the password πŸ˜„ This is one of the many reasons we have strong opinions about TLS πŸ˜„

yaleman avatar Jun 08 '25 23:06 yaleman

@Firstyear The server should never see your plain text password though.

In our case, it should. The server is the one keeping track of the password πŸ˜„ This is one of the many reasons we have strong opinions about TLS πŸ˜„

On the server, you should be using password hashing, no? Like Argon2id.

https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html https://appwrite.io/blog/post/goodbye-plaintext-passwords

yonas avatar Jun 08 '25 23:06 yonas

@Firstyear The server should never see your plain text password though.

In our case, it should. The server is the one keeping track of the password πŸ˜„ This is one of the many reasons we have strong opinions about TLS πŸ˜„

On the server, you should be using password hashing, no? Like Argon2id.

https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html https://appwrite.io/blog/post/goodbye-plaintext-passwords

It is STORING the password in hashed form.

yaleman avatar Jun 09 '25 00:06 yaleman

@yaleman True. Can you use OPAQUE here?

yonas avatar Jun 09 '25 01:06 yonas

@yaleman True. Can you use OPAQUE here?

No, and please keep discussions specifically related to the PR and its changes πŸ˜„

yaleman avatar Jun 09 '25 05:06 yaleman

@yaleman True. Can you use OPAQUE here?

No because then we never see the password so we can't enforce strength rules on it. OPAQUE is a PAKE (Password Authenticated Key Exchange) which means that even though we won't see the password, and it "shouldn't" be possible to attack if disclosed from the server, we only get client side validation of the password complexity, and thus open ourselves to new attacks.

Firstyear avatar Jun 11 '25 00:06 Firstyear

Yeah it's a good point that JS implementation is probably different sometimes (also mentioned in ZXcvbn paper as non-goal to have parity).

So what do you prefer: server API or compiling zxcvbn-rs to wasm and load in the client? πŸ€“ It's officially supported: https://github.com/shssoichiro/zxcvbn-rs/blob/master/.github%2Fworkflows%2Fzxcvbn.yml#L70

tennox avatar Sep 10 '25 08:09 tennox

Server API is the requirement. ZXCVBN isn’t the only check that’s done, see also credential deny listing

yaleman avatar Sep 11 '25 12:09 yaleman

Yeah if I get time I want to add this api soon.

Firstyear avatar Sep 11 '25 23:09 Firstyear

As a reminder https://github.com/kanidm/kanidm/pull/3847 is now done if you want to update the PR.

Firstyear avatar Nov 07 '25 07:11 Firstyear