For compliance reasons, we need to prevent users from reusing their passwords. I found this Q&A, which is very, very helpful, but I can't find an example implementation for a Grails application.
In our app, a user's password may be reset in two ways: an "edit user" view visible to admins and a self-service password reset flow. Ideally, I would like to perform this check during the Spring Security password encoding step to ensure it is applied regardless of which user action changes the password.
What's the best way to do that in Grails?
Answering my own question here:
Short answer:
I found the
personPasswordEncoderListenerdefined inresources.groovyreferenced aPersonPasswordEncoderListenerclass insrc/main/groovy. That's where I needed to insert my logic.Full answer:
First, I added a
Passwordclass to record the password history for each userThen, a
PasswordDataServiceto encapsulate persistence logic.Then, I added the
PasswordServiceto handle the business logic.Then, I added calls to the
PasswordServiceinPersonPasswordEncoderListener.encodePasswordForEvent(..). The vast majority ofPersonPasswordEncoderListenerwas already written (I only added 4 lines), but I'll include it in its entirety here for context and annotate the new lines with//addedThis created a problem because Spring Security wrapped the
InvalidResourceExceptionthat I was throwing inPasswordService.preventPasswordReusein anUndeclaredThrowableException, which hid the exception message I needed to pass along to my view. I resolved this by adding an exception handler to the exception handling trait that all our controllers implement to uncover the cause of theUndeclaredThrowableException.