More TOTP questions

Wagner, referring to your recent blog "Two-Factor Authentication (2FA) in Delphi with TMS Sphinx": it's not clear where you would do those things. And as Sphinx has no GUI, apart from the login and TOTP prompts, where do you go from there to show the QR?

I'm trying to do this during user registration, for example. Is this a valid approach?

So, there's SphinxConfig1UserCreated event handler, where I have the user, so I can do "Args.User.TwoFactorEnabled := TRUE" right away there. I assume this would update the table right away, right?

But then there's no TUser interface to do anything TOTP-related (except to retrieve the existing user tokens list), so I would probably need to CreateContext, FindByName, ResetAuthenticatorKey, GetAuthenticatorKey and TOtpUri.Build, as you have shown, I assume. I could not find how to get to the Context or the UserManager from Args.User.

But in the end, once I have all that, there's no GUI to show the QR, nor can I see any functionality to redirect the user from here. Where do I go from here?

Just to clarify: I want the users to self-register, but at the same time I want them to be created disabled / unable to actually access anything. So, I need to figure out where to do that and where to go from there, i.e.: redirect the users to some page that explains it.

And I have in-house developed TOTP registration components that can do webpage, etc. In fact they can do the whole thing start to end. And as Sphinx only does a part of it, I need to figure out where to plug in what I have to fill in the rest of the process. And it's really a three-fold issue:

  1. Get the secret and pass it to the component that does the webpage.
  2. Get the user redirected there to do the registration.
  3. Pass the result back to Sphinx.

With 1), I can see where to get it when the user has just been created, but then I may want to 2FA some specific operations later on, doing my own 2FA, so I need to be able to retrieve that secret at will, from outside of Sphinx. Where is it kept and how do I get to it? Is it / can it be encrypted? Why is there a list of those tokens in Sphinx, how can the same user have more than one?

With 2), I'm at a loss, because I cannot see any network interfaces from where the user creating happens and no way to redirect the browser anywhere. Again, as the user will actually be disabled at this moment, it should not be able to legally proceed anywhere further, but to the TOTP registration.

And with 3), again, how do I pass that back to Sphinx from outside? And would Sphinx do if the registration did not succeed and the user tries to login back? - my thinking is that any so far created artefacts should be cleared, so the user can repeat all this on the next round, without facing a situation where they cannot re-register, because the name already exists, but also cannot sign in, because of missing TOTP registration. And of course I do not want to flash this secret QR code over and over, I want the user to use it immediately, in a controlled fashion, or the whole thing should fail and self-destruct.

Does this make sense?

I'll be facing deep scrutiny from a pack of hungry Cerberuses for this solution, so I need to cover all my bases and know all details and answers...

The approach I am using is to return a flag in the JWT that says that the TOTP is not set up. I then will show a page in the webcore app that will have the QR Code displayed that will enable them to set it up in an authenticator app.

I'm still working my way through this as there are not really enough events on the Sphinx Config that I'd like. In other (non Sphinx apps) we offer the user the option to receive the validation code by Email or SMS rather that use an authenticator app (you'd be surprised how many companies don't use them). So a OnTOTPRequested event would be very useful. see TSphinxConfig event for OnSend2FACode - BIZ / BIZ Feature Requests - TMS Support Center

Ok, thanks, something to consider.

My target application for these JWT's is really something else entirely, so introducing this coupling unnecessarily does not feel right.

I also had a case today, where Sphinx hiccupped on something and left me on the signon page without the signon frame, just the empty background. I'd hate that to happen to any of my users.

What do you use for SMS? AWS? Are you sending and receiving SMS in the same single country, or cross-borders too?

And yes, there are certainly things I'd want handled entirely within the signon process and there's no way to plug anything like that in.

It's just UK on the SMS at the moment. I don't use AWS yet, but looking to move to it.

Re your Sphinx hiccup, see Sphinx: Auto restart of login session in certain circumstances - BIZ / BIZ Feature Requests - TMS Support Center

Yes, saw that, already voted ;-)

Wagner, can you suggest how I should go about implementing what I have described, if it's doable in Sphinx as-is? Or propose any enhancements for Sphinx to make it work?

I think the key thing is that when a user logs in using 2fa, Sphinx will generate the access token with the claim amr set to mfa.

With that in mind you can and should implement your application logic behavior, i.e., block features that you don't want to be accessed if user didn't use 2fa, and possibly provide a user interface that shows QR code and enable 2fa if that's the case (for now, that's up to your application to do that).

Ok, but that's a bit insufficient: when it creates a JWT, it's in the context of a specific client/application, with some redirect URL('s), designed to give access.

While in these cases, it would have to be a different URL. So Sphinx would need to be able to figure this out and redirect to a different page. But I do not think we can code that, right? I cannot see any way to redirect a user to the TOTP registration page, nor to any other page.

It would also timeout before the user can register. And potentially stop mid-flight, leaving the user stranded, with no way to re-register and no way to proceed forward.

Besides, regardless of 2FA, I cannot see how to nicely create a disabled account. Which is really a necessity, unless I have to define all users in advance, by the admins, manually, which is a more complicated path. It's much nicer to allow users to self-register. But then, if that's a bank account, you do not want to grant them access until after the account has been reviewed by the admins and explicitly enabled.

And the 2FA secrets: where is it stored, can I encrypt it, can I retrieve it later and can I set it initially?

There needs to be a standard way to do all those things...

What is the problem here? The JWT is generated in the context of a user login. Once the user is authenticated, JWTs are generated to access the APIs where the user should have access to. There, the amr claim is added indicating if that "JWT" has two-factor authentication "level".

I'm not sure what are you referring to here.

It's all in JWT and your business logic. You decided what claims to add to JWT based on what is in your user database. If the user has a flag "Reviewed", add that information to the JWT by the time of the login.

  1. So, every target needs to be aware of this and have a way to register the user for 2FA. This is not practical, it creates an unnecessary coupling, splits the registration into pieces and the client has no clear means of passing the result back to Sphinx. A normal way for the client that receives a token with undesirable claims is to refuse access, full stop. If it now needs to go through additional motions and talk back to the issuer, it complicates it dramatically.

  2. I was thinking about doing 2FA on a separate page, through a separate component. In light of your answer above, you are thinking it should be done by each target client. Not sure it's a good idea.

  3. This just complicated the already unnecessary coupling. It would not be too bad, if all of that was handled by the same server, but it's not a likely scenario.

So, basically, Sphinx would send the user to the target unconfigured and the target needs to know about that. But then how can it communicate the registration back? Another interface?

This picture is already getting bloated with extra hops to and from, which should not really be there, I think.

And you have left some questions unanswered: timeout, the secret encryption and getting the secret out of this separate Sphinx server and then passing it back to it.

The main thing is that if something can't easily do everything you want end to end, then you would either have to implement the missing parts yourself, while also learning all the quirks of the components involved, or instead write it yourself with another framework. And I'm still not sure which is easier. Say, writing a complete authentication solution in a generic fashion for publishing it as a solution in its own right is not a small task, but doing the same in a specific proprietary way for just the task you have at hand, it an order of magnitude easier, so I can roll it out in a day with IntraWeb, for instance. Adding these missing bits to Sphinx would make it a clear winner and not adding them may just make it a loser. As it stands, I'm implementing a few versions of what I want with multiple different frameworks, just to see which result would be cleaner/smaller/easier to support...

And what if this authentication is done for a 3rd-party system, which has no clue about any of that? - you cannot really send any JWT's there, because it would be 1) validating JWT signature, which would work and 2) extracting user's UPN, which would be there.

It's all part of user registration, it can't be spilled over to any clients, it must completely happen during user registration and with the new account actually created disabled, it must not be issuing any JWT's at all at this stage either, just giving the user a page of text to read in the end.

Wagner, can you see my point?

I'm not sure what you mean about a third party system doing the validation. If that was the case you wouldn't be using Sphinx, but would need to know the secret from the third party system so your server app could validate the JWT that it issued.

If you have a sphinx authentication system used by all your applications then the user would only have to set up 2FA on one of them, and then it would be valid for all. While each system could have the 2FA set up built into it, you could have another server app, even your Sphinx server app, handle that. You would just need to have it redirect there, if 2FA wasn't set up, first

I mean if Sphinx is the IdP for a 3rd-party system.

The the 3rd party system and Sphinx would need to share the secret

No, not at all. Sphinx will need to share the public certificate, as any other IdP.

I do not want Sphinx implementation details to become a secret they need to know ;-)

Yes. We will eventually implement a screen in login app which will ask (force) users to enable 2fa and provide the QR Code for them to register.

What I'm saying is that for now you can workaround it.

And, in some systems, users might not want to enforce 2fa on users. They just want to forbid access to some features if 2fa is not enabled. In this cases, the problem remains.

1 Like

That would be great, thanks!

No way to redirect the user to a URL from this process now, right? - that would have been the easiest solution.

I don't understand what you mean. Right now the login process is exactly through a redirect URL, which is called once the login process is finished.