Best-practice design for a multi-step auth/registration flow (phone & national-ID dedup + OTP) in Keycloak

I’m designing an authentication & registration experience for a fintech app (public portal + staff portal) and would appreciate best-practice guidance on how to implement it cleanly in Keycloak. The core needs are:

  • Multi-step registration with email/phone capture, SMS OTP verification, and profile completion.

  • Login with username/email or phone, optionally followed by OTP (step-up).

  • Deduplication: if a phone number or national ID already exists, detect it early (before creating the account) and branch the flow (block, recover, or escalate to manual review).

  • Auditability, rate-limits, and a path for staff to override/verify suspicious cases.

Environment

  • Keycloak: (planning latest 24/25.x, can adapt if a version matters)

  • Clients: Spring Boot (resource servers) + Angular (SPA)

  • SMS provider: pluggable (we can call an external SMS service)

  • Data residency/PII constraints apply (phone & national ID are sensitive)


Desired user journeys (simplified)

Registration (Public)

  1. User enters phone + email.

  2. Send SMS OTP → verify.

  3. Collect profile fields including nationalId.

  4. Dedup check (phone and nationalId):

    • If match → show “account exists / recovery” or escalate to manual verification.

    • If no match → create user, mark attributes, complete.

Login (Public)

  • Username/email/phone → password.

  • Optional step-up OTP based on risk signals or device trust.

Staff portal

  • Username/password

Couple of thoughts:

  • there is no SMS OTP option native to Keycloak. I’ve used this extension before with success GitHub - netzbegruenung/keycloak-mfa-plugins: Keycloak plugins for MFA (enforce MFA, SMS authentication step, native app integration)
  • for your “Dedup check”, by showing the user an “account exists / recovery” error, you’re opening yourself to enumeration attacks.
  • for login, there isn’t an option to look up an account by an attribute like phone number, so you may have to override the UsernamePasswordForm to accept a phone number and look up a user based on that attribute.
  • for “Optional step-up OTP based on risk signals or device trust”, you’ll need to implement a custom authenticator

I’d suggest installing that SMS OTP extension, building the flows, and then seeing if what you get out of the box is sufficient for your use case. If not, it’s definitely possible based on building additional custom extensions, but that opens you up to developing and maintaining your own authenticators and forms.