<html>
  <head>
    <meta content="text/html; charset=ISO-8859-1"
      http-equiv="Content-Type">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <div class="moz-cite-prefix">On 8/19/2016 2:43 AM, Stian Thorgersen
      wrote:<br>
    </div>
    <blockquote
cite="mid:CAJgngAeD-9nKquNjEO_8CzARiAhJMVT494t6ta1OtZ4gdbaiAQ@mail.gmail.com"
      type="cite">
      <div dir="ltr"><br>
        <div class="gmail_extra"><br>
          <div class="gmail_quote">On 18 August 2016 at 21:00, Bill
            Burke <span dir="ltr">&lt;<a moz-do-not-send="true"
                href="mailto:bburke@redhat.com" target="_blank">bburke@redhat.com</a>&gt;</span>
            wrote:<br>
            <blockquote class="gmail_quote" style="margin:0 0 0
              .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000">
                <div>
                  <div class="h5">
                    <p><br>
                    </p>
                    <br>
                    <div>On 8/18/16 5:12 AM, Stian Thorgersen wrote:<br>
                    </div>
                    <blockquote type="cite">
                      <div dir="ltr"><br>
                        <div class="gmail_extra"><br>
                          <div class="gmail_quote">On 16 August 2016 at
                            00:57, Bill Burke <span dir="ltr">&lt;<a
                                moz-do-not-send="true"
                                href="mailto:bburke@redhat.com"
                                target="_blank">bburke@redhat.com</a>&gt;</span>
                            wrote:<br>
                            <blockquote class="gmail_quote"
                              style="margin:0 0 0 .8ex;border-left:1px
                              #ccc solid;padding-left:1ex">
                              <div 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.&nbsp;
                                This is a work in progress where we may
                                see multiple iterations in master.&nbsp; I
                                hope to remain backward compatible, but
                                can't guarentee I won't break existing
                                User Federation Providers.&nbsp; Here's an
                                initial writeup to explain things.&nbsp;
                                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> </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> </p>
                                <p> </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> </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.&nbsp; UserStorageProviders
                                  (user federation) and
                                  CredentialProviders.&nbsp;
                                  CredentialProviders are components
                                  configured at the realm level.&nbsp;
                                  CredentialProviders are responsible
                                  for managing one or more types of
                                  credential types and are the bridge
                                  between CredentialInput and where the
                                  credential is stored.&nbsp;
                                  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.&nbsp; This allows each
                                  credential type to decide whether it
                                  will be cached or not along with the
                                  user.&nbsp; For example, HOTP cannot be
                                  cached.</p>
                                <p>So, for example, there will be a
                                  KeycloakMobileOTPProvider.&nbsp; 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.&nbsp; There is also a
                                  KeycloakPasswordProvider which hooks
                                  into Keycloak storage and the
                                  PasswordPolicies set up by the realm.&nbsp;
                                  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&nbsp; 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().isConf<wbr>iguredFor(realm,

                                  user, "OTP").&nbsp; If the user was loaded
                                  by a UserStorageProvider and that
                                  provider implements the
                                  CredentialInputValidator interface,
                                  isConfiguredFor() is called on that.&nbsp;
                                  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().requir<wbr>edActionsFor(...).&nbsp;

                                  Again, UserStorageProvider is queried
                                  first, then the CredneitalProviders.&nbsp;
                                  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.&nbsp; In that
                                  case, this required action provider
                                  callsKeycloakSession.users().u<wbr>pdateCredential.&nbsp;

                                  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().isVali<wbr>d()
                                  method is called.&nbsp;&nbsp; Again,
                                  UserStorageProvider first, then each
                                  CredentialProvider.&nbsp; Each provider is
                                  queried until one returns true or the
                                  list is exhausted.&nbsp; 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.&nbsp; There will be separate
                                  property lists for admin console and
                                  account service.&nbsp; 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.&nbsp; We don't have a
                                  pluggable mechanism for the account
                                  service yet (or a way to generic
                                  render either).&nbsp; This will need to be
                                  developed eventually.<br>
                                </p>
                              </div>
                            </blockquote>
                            <div>Extending admin console is fine in
                              community, but would be hard to support. </div>
                          </div>
                        </div>
                      </div>
                    </blockquote>
                  </div>
                </div>
                Generic rendering from a metamodel provided by a REST
                API is something we can and should support.&nbsp; It won't be
                completely pretty, but should work for most things.&nbsp; If
                we don't want to support Angular extensions, then the
                user can just completely punt and have a completely
                different web app to configure a specific component.</div>
            </blockquote>
            <div><br>
            </div>
            <div>I didn't say we shouldn't support extensions to the UI,
              but we need to consider how to do it. If we tie it to much
              to Angular 1 specifics than it becomes even harder to
              migrate to Angular 2.</div>
            <div>&nbsp;</div>
            <blockquote class="gmail_quote" style="margin:0 0 0
              .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
                  <br>
                  <blockquote type="cite">
                    <div dir="ltr">
                      <div class="gmail_extra">
                        <div class="gmail_quote">
                          <div>Especially as we would eventually have to
                            move to AngularJS 2.0, which drastically
                            changes things. </div>
                        </div>
                      </div>
                    </div>
                  </blockquote>
                  <br>
                </span> Moving to Angular 2 seems daunting.&nbsp; <br>
                <br>
                <a moz-do-not-send="true"
                  href="https://angular.io/docs/ts/latest/guide/upgrade.html#"
                  target="_blank">https://angular.io/docs/ts/<wbr>latest/guide/upgrade.html#</a></div>
            </blockquote>
            <div><br>
            </div>
            <div>Yep, it's a complete rewrite! Sucks.</div>
          </div>
        </div>
      </div>
    </blockquote>
    Bill, you may not be aware that I'm coming to Boston next month to
    attend the Angular conference.&nbsp; There are sessions on migrating, so
    at least I should be able to get on the right track and make an
    educated assessment of the suckiness.<br>
    <blockquote
cite="mid:CAJgngAeD-9nKquNjEO_8CzARiAhJMVT494t6ta1OtZ4gdbaiAQ@mail.gmail.com"
      type="cite">
      <div dir="ltr">
        <div class="gmail_extra">
          <div class="gmail_quote">
            <div>&nbsp;</div>
            <blockquote class="gmail_quote" style="margin:0 0 0
              .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
                  <br>
                  <br>
                  <blockquote type="cite">
                    <div dir="ltr">
                      <div class="gmail_extra">
                        <div class="gmail_quote">
                          <div>I also wonder if we should provide an
                            higher level extension to register
                            extensions than having to do $routeProviders
                            etc as that sounds like a fairly low level
                            approach. Do you have any example?</div>
                          <div><br>
                          </div>
                        </div>
                      </div>
                    </div>
                  </blockquote>
                  <br>
                  <br>
                </span> Look at the old ldap provider for an example of
                overriding a specific component UI over a fallback
                generic one.&nbsp; It just has a more specific route than the
                generic page.&nbsp;</div>
            </blockquote>
            <blockquote class="gmail_quote" style="margin:0 0 0
              .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000"><span class=""><br>
                  <br>
                  <blockquote type="cite">
                    <div dir="ltr">
                      <div class="gmail_extra">
                        <div class="gmail_quote">
                          <div>Account service should be completely
                            scrapped and replaced with something more
                            modern. The UI needs a complete revamp and
                            it should also be changed to AngularJS and
                            REST service to make it more customizable
                            and extensible.<br>
                          </div>
                        </div>
                      </div>
                    </div>
                  </blockquote>
                </span> Again, we can focus on generic rendering for
                Account Service.&nbsp; Then it will translate quite easily to
                whatever we do here.</div>
            </blockquote>
            <div><br>
            </div>
            <div>I don't think generic rendering works for account
              service as it wants to be more end user friendly. Generic
              rendering is probably OK for the admin console most of the
              time, but it's never going to be brilliant for usability.</div>
          </div>
        </div>
      </div>
    </blockquote>
    I've done quite a bit of work with generic rendering.&nbsp; The richness
    and usability of the resulting UI tends to be a function of the
    richness of the metadata that generates it.&nbsp; In other words, the
    more you describe what you are rendering the better you can render
    it.&nbsp; We'll just have to see how much work we want to put into it.<br>
    <blockquote
cite="mid:CAJgngAeD-9nKquNjEO_8CzARiAhJMVT494t6ta1OtZ4gdbaiAQ@mail.gmail.com"
      type="cite">
      <div dir="ltr">
        <div class="gmail_extra">
          <div class="gmail_quote">
            <div>&nbsp;</div>
            <blockquote class="gmail_quote" style="margin:0 0 0
              .8ex;border-left:1px #ccc solid;padding-left:1ex">
              <div bgcolor="#FFFFFF" text="#000000"><span class="HOEnZb"><font
                    color="#888888"><br>
                    <br>
                    Bill<br>
                  </font></span></div>
            </blockquote>
          </div>
          <br>
        </div>
      </div>
      <br>
      <fieldset class="mimeAttachmentHeader"></fieldset>
      <br>
      <pre wrap="">_______________________________________________
keycloak-dev mailing list
<a class="moz-txt-link-abbreviated" href="mailto:keycloak-dev@lists.jboss.org">keycloak-dev@lists.jboss.org</a>
<a class="moz-txt-link-freetext" href="https://lists.jboss.org/mailman/listinfo/keycloak-dev">https://lists.jboss.org/mailman/listinfo/keycloak-dev</a></pre>
    </blockquote>
    <br>
  </body>
</html>