Class JdbcTotpRepository

java.lang.Object
com.codename1.backend.security.mfa.JdbcTotpRepository
All Implemented Interfaces:
TotpRepository

public final class JdbcTotpRepository extends Object implements TotpRepository

Secrets kept in the server's database, in the cn1_mfa_totp table of SecuritySchema.

A secret is what an attacker needs to produce a user's codes for good, so it is not stored as it is: each is sealed with AES-GCM under a key the database does not hold, with a nonce of its own stored beside it and the user's name bound in, so a row copied onto another user does not open. Give the key in the configuration, as 32 bytes in base64:

cn1.security.mfa.encryptionKey=...     # openssl rand -base64 32

and build the repository with fromConfig(DataSource, Config). Losing the key loses every enrolment; changing it does the same.

  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final String
    The setting that holds the key.
  • Constructor Summary

    Constructors
    Constructor
    Description
    JdbcTotpRepository(DataSource dataSource, byte[] encryptionKey)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    boolean
    advance(String username, byte[] expectedSecret, long step)
    Consumes a sign-in code only if the confirmed credential still has the verified secret and its last accepted step is lower.
    boolean
    advance(String username, long step)
    Records that a code of time step step was accepted, if no code of that step or a later one has been.
    boolean
    confirm(String username)
    Marks the credential confirmed, if it is there and was not.
    boolean
    confirm(String username, byte[] expectedSecret, long step)
    Confirms and consumes a code only if the current, unconfirmed credential still has the secret that was verified.
    boolean
    delete(String username)
    Forgets the credential.
    find(String username)
    The credential of username, or null.
    fromConfig(DataSource dataSource, Config config)
    A repository whose key is the configuration's ENCRYPTION_KEY.
    void
    save(String username, byte[] secret)
    Stores a new, unconfirmed secret for username, replacing any other.
    void
    setClock(Clock clock)
     

    Methods inherited from class Object

    clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

  • Constructor Details

    • JdbcTotpRepository

      public JdbcTotpRepository(DataSource dataSource, byte[] encryptionKey)
      Parameters:
      encryptionKey - 32 bytes
  • Method Details

    • fromConfig

      public static JdbcTotpRepository fromConfig(DataSource dataSource, Config config)

      A repository whose key is the configuration's ENCRYPTION_KEY.

      On a development profile a server without the setting gets a fixed key and says so, so that a laptop needs no secret; anywhere else the setting is required and the server does not start without it.

    • setClock

      public void setClock(Clock clock)
    • save

      public void save(String username, byte[] secret)
      Description copied from interface: TotpRepository
      Stores a new, unconfirmed secret for username, replacing any other.
      Specified by:
      save in interface TotpRepository
    • find

      public TotpCredential find(String username)
      Description copied from interface: TotpRepository
      The credential of username, or null.
      Specified by:
      find in interface TotpRepository
    • confirm

      public boolean confirm(String username)
      Description copied from interface: TotpRepository
      Marks the credential confirmed, if it is there and was not.
      Specified by:
      confirm in interface TotpRepository
      Returns:
      whether this call confirmed it
    • advance

      public boolean advance(String username, long step)
      Description copied from interface: TotpRepository

      Records that a code of time step step was accepted, if no code of that step or a later one has been.

      The test and the change are one step, which is what makes a code good once: of two requests presenting the same code at the same moment, on one server or two, exactly one is told true.

      Specified by:
      advance in interface TotpRepository
      Returns:
      whether this call recorded it
    • confirm

      public boolean confirm(String username, byte[] expectedSecret, long step)
      Description copied from interface: TotpRepository
      Confirms and consumes a code only if the current, unconfirmed credential still has the secret that was verified. The comparison and both changes must be atomic, including across servers sharing a database.
      Specified by:
      confirm in interface TotpRepository
    • advance

      public boolean advance(String username, byte[] expectedSecret, long step)
      Description copied from interface: TotpRepository
      Consumes a sign-in code only if the confirmed credential still has the verified secret and its last accepted step is lower. This is one atomic change.
      Specified by:
      advance in interface TotpRepository
    • delete

      public boolean delete(String username)
      Description copied from interface: TotpRepository
      Forgets the credential.
      Specified by:
      delete in interface TotpRepository
      Returns:
      whether there was one