<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=utf-8">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    I'm currently working on a new credential SPI that will replace
    existing methods on UserProvider and UserModel, as well as replacing
    UserCredentialModel, etc.  This is a work in progress where we may
    see multiple iterations in master.  I hope to remain backward
    compatible, but can't guarentee I won't break existing User
    Federation Providers.  Here's an initial writeup to explain things. 
    Credentials revolve around these 4 events that are initiated by
    authentication flows, the admin console, and the account service.
    <p>* Is the user configured for a specific credential type</p>
    <p>* Is a credential valid</p>
    <p>* What required actions must be taken for an unconfigured
      credential type</p>
    <p>* update a credential</p>
    <p>How each of these events is resolved will depend on the
      configuration of the system and these interfaces:</p>
    <p>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </p>
    <pre style="background-color:#ffffff;color:#000000;font-family:'Menlo';font-size:9.0pt;"><span style="color:#000080;font-weight:bold;">public interface </span>CredentialInput {
    String getType();
}
</pre>
    <p>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </p>
    <p>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </p>
    <pre style="background-color:#ffffff;color:#000000;font-family:'Menlo';font-size:9.0pt;"><span style="color:#000080;font-weight:bold;">public interface </span>CredentialInputValidator {
    <span style="color:#000080;font-weight:bold;">boolean </span>supportsCredentialType(String credentialType);
    <span style="color:#000080;font-weight:bold;">boolean </span>isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
    <span style="color:#000080;font-weight:bold;">boolean </span>isValid(RealmModel realm, UserModel user, CredentialInput input);

}
</pre>
    <p>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </p>
    <pre style="background-color:#ffffff;color:#000000;font-family:'Menlo';font-size:9.0pt;"><span style="color:#000080;font-weight:bold;">public interface </span>CredentialInputUpdater {
    <span style="color:#000080;font-weight:bold;">boolean </span>supportsCredentialType(String credentialType);
    Set&lt;String&gt; requiredActionsFor(RealmModel realm, UserModel user, String credentialType);
    <span style="color:#000080;font-weight:bold;">void </span>updateCredential(RealmModel realm, UserModel user, CredentialInput input);
}

</pre>
    <p>Two different types of components will be able to implement these
      interfaces.  UserStorageProviders (user federation) and
      CredentialProviders.  CredentialProviders are components
      configured at the realm level.  CredentialProviders are
      responsible for managing one or more types of credential types and
      are the bridge between CredentialInput and where the credential is
      stored.  UserStorageProvider is always asked first whether it can
      complete the requested action, then CredentialProviders are
      queried in order of their priority.</p>
    <p>Each UserStorageProvider and/or CredentialProvider can implement
      the OnUserCache callback interface discussed in my previous custom
      caching email.  This allows each credential type to decide whether
      it will be cached or not along with the user.  For example, HOTP
      cannot be cached.</p>
    <p>So, for example, there will be a KeycloakMobileOTPProvider.  This
      deals with Google Authenticator and FreeOTP as well as storing
      these things within Keycloak storage, it also looks at the OTP
      policy of the realm to determine how to update and store the OTP
      secret and stuff.  There is also a KeycloakPasswordProvider which
      hooks into Keycloak storage and the PasswordPolicies set up by the
      realm.  When a user is cached, the KeycloakPasswordProvider will
      add the hashed password to the user cache, the
      KeycloakMobileOTPProvider will add the OTP secret to cache if its 
      not HOTP and needs to maintain a counter.</p>
    <p>Let's walk through an authentication flow, specificaly for OTP.</p>
    <p>1. Authenticator calls
      KeycloakSession.users().isConfiguredFor(realm, user, "OTP").  If
      the user was loaded by a UserStorageProvider and that provider
      implements the CredentialInputValidator interface,
      isConfiguredFor() is called on that.  If that returns false, each
      CredentialProvider is iterated on to call isConfiguredFor().</p>
    <p>2. If OTP is required and not configured for the user, the
      Authenticator then calls
      KeycloakSession.users().requiredActionsFor(...).  Again,
      UserStorageProvider is queried first, then the
      CredneitalProviders.  The first provider that returns a non-empty
      set will end the query and the set of required actions will be
      returned.</p>
    <p>3a. Let's say that in this particular example, the generic OTP
      Requried Action screen is invoked.  In that case, this required
      action provider callsKeycloakSession.users().updateCredential. 
      The first UserStorageProvider or CredentialProvider that can
      handle this credential type will save the credential.<br>
    </p>
    <p>3b. If OTP is configured for user, the OTP is obtained by the
      Authenticator and KeycloakSession.users().isValid() method is
      called.   Again, UserStorageProvider first, then each
      CredentialProvider.  Each provider is queried until one returns
      true or the list is exhausted.  FYI, This algorithm allows for
      multiple OTP authenticators per user.</p>
    <p>** Admin console and Account Service UIs **</p>
    <p>Like we do for other components, the UserStorageProvider or
      CredentialProvider can optionally provide a list of
      ProviderConfigProperties for the admin console and/or account
      serviceso that it can create a credential for a specific user. 
      There will be separate property lists for admin console and
      account service.  If a specific custom screen is desired, I'm
      pretty sure we can just allow the develoepr to plug in their own
      $routeProvider for the admin console.  We don't have a pluggable
      mechanism for the account service yet (or a way to generic render
      either).  This will need to be developed eventually.<br>
    </p>
    <p><br>
    </p>
  </body>
</html>