Three (HOTP hardware token, TOTP, TAN list) auto assigned tokens per user?

I’ve succesfully configured PrivacyIDEA to be able to authenticate Outlook Web Access Users via Kemp Loadmaster frontend using Radius protocol with a Radius challenge asking for the 2nd factor. Works great.

So far I’ve been assigning tokens to a few test users manually, but I’d like to automate it using the auto assignment policy and also would like to have a plan B in case a token is lost, smartphone has no battery or is not allowed on-site, whatever. Also, I’d like to avoid discussions with users, some don’t like hardware tokens because their USB ports are all used up already, some have multiple machines and will likely sooner or later forget a token in the other machine, some don’t want to use their (private) smartphone for work-related stuff, some are just stubborn and don’t like tech or changes in general.

So I’ve tested using multiple tokens per user. Yubikey with HOTP as a primary means, smartphone via FreeOTP or similar app with TOTP QR Code, plus a TAN list on paper as a 3rd backup (paper has no bugs, doesn’t need power, cannot be hacked, doesn’t need firmware upgrades, …). This also works fine so far.

To make the above work, two policies had to be configured:

scope: authentication, miscellaneous: otppin: userstore
scope: authentication, miscellaneous: challenge_response: hotp,tan,totp

Now I’ve tried to configure the autoassignment policy additionally:
scope: enrollment, token: autoassignment: userstore

But after enabling that policy, authentication does not work anymore, after giving the username and password, authentication is denied with wrong credentials and there is no radius challenge for the 2nd factor being sent to Kemp Loadmaster.

After switching to DEBUG logging in pi.cfg, the log show this:

[2024-11-25 09:25:11,620][19706][139840438400704][DEBUG][privacyidea.lib.tokenclass:1415] Splitting the an OTP value of length 6 from the password.
[2024-11-25 09:25:11,621][19706][139840438400704][DEBUG][privacyidea.lib.config:174] Entering get_from_config with arguments () and keywords {'key': 'PrependPin', 'default': False, 'return_bool': True}
[2024-11-25 09:25:11,622][19706][139840438400704][DEBUG][privacyidea.lib.config:187] Exiting get_from_config with result True
[2024-11-25 09:25:11,622][19706][139840438400704][DEBUG][privacyidea.lib.utils:1332] PIN prepended. PIN length is 5, OTP length is 6.
[2024-11-25 09:25:11,622][19706][139840438400704][DEBUG][privacyidea.lib.user:177] Entering check_password with arguments HIDDEN and keywords HIDDEN
[2024-11-25 09:25:11,623][19706][139840438400704][INFO][privacyidea.lib.user:448] User XXXXXX from realm XXXXXXX tries to authenticate
[2024-11-25 09:25:11,623][19706][139840438400704][DEBUG][privacyidea.lib.resolver:174] Entering get_resolver_object with arguments ('AD',) and keywords {}
[2024-11-25 09:25:11,624][19706][139840438400704][DEBUG][privacyidea.lib.resolver:174] Entering get_resolver_list with arguments () and keywords {'filter_resolver_name': 'AD'}
[2024-11-25 09:25:11,624][19706][139840438400704][DEBUG][privacyidea.lib.resolver:189] Exiting get_resolver_list with result HIDDEN
[2024-11-25 09:25:11,625][19706][139840438400704][DEBUG][privacyidea.lib.resolver:187] Exiting get_resolver_object with result <privacyidea.lib.resolvers.LDAPIdResolver.IdResolver object at 0x7f2f11bdec10>
[2024-11-25 09:25:11,625][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:272] Reading 'XXXXXXX-7cf6-4b64-847b-XXXXXXXXXXX' from cache for '_getDN'
[2024-11-25 09:25:11,626][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:948] Added XXXXXXXXXXX, None, False to server pool.
[2024-11-25 09:25:11,627][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:948] Added XXXXXXXXXXX, None, False to server pool.
[2024-11-25 09:25:11,627][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:372] Authtype: 'Simple'
[2024-11-25 09:25:11,628][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:373] user    : 'CN=XXXXXXXXXX,OU=XXXXXXX,DC=XXXXXXX,DC=XXXXXXX'
[2024-11-25 09:25:11,641][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:386] bind result: False
[2024-11-25 09:25:11,641][19706][139840438400704][WARNING][privacyidea.lib.resolvers.LDAPIdResolver:393] failed to check password for 'XXXXXXXXX-7cf6-4b64-847b-XXXXXXX'/'CN=XXXXXX,OU=XXXXXXX,DC=XXXXXXX,DC=XXXXXXX': Exception('Wrong credentials')
[2024-11-25 09:25:11,643][19706][139840438400704][DEBUG][privacyidea.lib.resolvers.LDAPIdResolver:394] Traceback (most recent call last):
  File "/opt/privacyidea/lib/python3.11/site-packages/privacyidea/lib/resolvers/LDAPIdResolver.py", line 388, in checkPass
    raise Exception("Wrong credentials")
Exception: Wrong credentials

From the debug log it seems that privacyidea is trying to split the password into PIN and OTP (my password is 11 characters long). What am I missing here? Is there a way to configure privacyidea to not do that? There are no PINs configured for the tokens.

Do not use autoassigment. This is not, waht you want.

Autoassignment is ment for hardware tokens.
The user already has a token in possession. The token exists in privacyIDEA.

And, it only works with one token, if the user has not other token, yet.
You need to rethink your enrollment strategy.

Well, I’d like a simple and convenient rollout strategy. Both for users as well as admins.

If autoassignment only works with hardware tokens and only works if no other token is assigned, I cannot really think of a good strategy to achieve what I want: Hardware token as primary means, plus backup token in case hardware token is lost or forgotten etc.Plus a convenient and easy workflow, having to worry about what token is assigned to which user doesn’t make it convenient. How do you do that with Yubikey Nanos for example? They all look the same, they are very small, I’m not even sure if there is a serialnumber on the outside or something to identify them. Even if there is a serial, it’s too small to read. They are too small to stick a label with the username on them. And even if that was possible, labelling dozens of keys by hand would be an annoying and error-prone process.

If I configure the token enrollment wizard for self-service enrollment of QR Codes or TAN lists, I have to make sure that users don’t do that before they have the hardware token auto-assigned. Even if I make sure of that for the initial rollout, what happens in case a token is lost for a user? He already has other tokens assigned by then, so autoassignment won’t work for replacing a lost token.

Is there a technical or security reason autoassignment works only for hardware tokens and only when no other token is assigned? I can’t think of one, but as my username implies, I’m new to all this (not new to IT in general though …).

If autoassignment would work for all token types as well as when there are other token types already assigned, it would make things a lot easier:

I’d simply:

  • Import hardware tokens via CSV list
  • Generate TOTP and TAN tokens via API or scripts, save all the TOTP QRCode .PNGs and TAN list .PDFs somewhere, no need to worry about which one is assigned to which user
  • Give out random tokens and QR-Codes plus TAN lists to people.
  • People just start using them as they please. Done. Most simple.

privacyIDEA is very flexible. Scenarios are very different.
We can spent hours discussing rollout strategies. Unterstanding all you situation, discussing possiblites.

  • What processes already exist
  • What information about users is available
  • Where are users?

  • Depending on this there are different ways to hand HW tokens to users.

You could autoenroll HW tokens. Users could self assign HW tokens…

Autoassignment works for already existing tokens. THese are usually hardware tokens.
There is no need to and it is difficult to enroll a smartphone app like TOTP and autoassign this phone to the user?

The processing power need would exceed this of a question to ChatGPT.

Btw: you can autoenroll most of the tokentypes.