On Fri, Jun 30, 2023 at 12:43 PM Michael Jumper <[email protected]> wrote:
>
> On 6/30/23 08:13, Nick Couchman wrote:
> > I decided to take a shot at writing a One-Time Password extension that
> > sends the OTP via e-mail, providing multi-factor authentication by
> > implementing it as a decorating authentication extension. I've got
> > quite a bit of it written and mostly working, but I'm running into one
> > behavior that I cannot figure out.
> >
> > One of my criteria when implementing this extension was that the OTP
> > communicated via e-mail should only be used one time - that is, as
> > soon as it is used, it is invalidated and/or removed from the storage
> > mechanism that tracks the OTPs for the user. Unfortunately this is
> > causing an issue, because it seems that the decorate() method for the
> > UserContext object gets called twice, which means the OTP validation
> > happens twice. I'm not sure if this is because it's getting called to
> > decorate both the Postgresql and Postgresql Shared authentication
> > providers? I've gone through my code a few times to make sure that I'm
> > not accidentally calling it multiple times, myself, and I'm not seeing
> > that.
> >
> > So, my questions are:
> > 1) Is this (multiple decorations/verifications during a single login) 
> > expected?
>
> Yep - it'll be called for every UserContext to provide an opportunity to
> decorate that specific UserContext. Depending on how many extensions are
> installed and how many AuthenticationProviders return UserContexts for
> that auth attempt, there could be any number of these.
>
> > 2) Assuming it is expected, any guidance on how to implement in a way
> > that works with this? I suppose I could relax the requirement for only
> > using it once and allow it to be used multiple times during the time
> > it is valid, but ideally it would truly be a single-use password.
>
> You could track based on uniqueness of the email address, not resending
> the code so long as there is an existing code having already been sent
> to that email, maintaining sent codes in memory.

It's actually not the sending e-mailing of the code that is causing
issues - this only happens once, probably due to the fact that the
authentication extension is looking for the presence of a specific
field in the authentication request that contains the OTP. But, once
this field exists and it checks it once (which happens during the call
to decorate() in the AuthenticationProvider class), it gets removed,
and the second decorate() call + OTP check then fails.

>
> You'll end up in a similar situation to what we were encountering with
> SAML+TOTP where anti-replay defenses conflict. You can work around that
> with the same approach (pending merge): allow reuse of the code/link
> while authentication is being refused only for transient reasons
> (insufficient credentials, non-security client exceptions), and fully
> invalidate the code/link only after authentication has 100% succeeded
> (auth success event) or 100% failed (auth failure event from any other
> exception).

Yeah, five minutes after I sent this original e-mail I saw the update
to the PR from James for that issue and went, "Huh, I bet these are
related." I was looking at that PR and it seems like I'll need to do
something similar to what James did with the EventListener class.

-Nick

Reply via email to