Few additional things, which applies for credentials like
SPNEGO/Kerberos tokens, however I think they might be required for other
credential types too.
- Authentication of unknown user : For example in case of SPNEGO, you
have just the token, but you don't know which user are you
authenticating. User is "recognized" later once the SPNEGO token is
successfully validated
- More handshakes for credential validation : This is again related to
SPNEGO, but I am sure it applies for some other credential types too.
Instead of true/false we may need something like : SUCCESS, FAILED,
CONTINUE.
Also the possibility to send some context info back to the client, so
client can continue with the handshake. For the old federationProvider
we had:
CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel
credential);
I guess we need something similar for the new SPI too? Also for
"isValid" method, I would rather return CredentialValidationOutput
instead of just true/false. True/false is good for passwords/OTP, which
are most widely used credential types in Keycloak, but may not be
sufficient for other custom credential types.
Marek
On 16/08/16 00:57, Bill Burke wrote:
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.
* Is the user configured for a specific credential type
* Is a credential valid
* What required actions must be taken for an unconfigured credential type
* update a credential
How each of these events is resolved will depend on the configuration
of the system and these interfaces:
public interface CredentialInput {
String getType();
}
public interface CredentialInputValidator {
boolean supportsCredentialType(String credentialType);
boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType);
boolean isValid(RealmModel realm, UserModel user, CredentialInput input);
}
public interface CredentialInputUpdater {
boolean supportsCredentialType(String credentialType);
Set<String> requiredActionsFor(RealmModel realm, UserModel user, String
credentialType);
void updateCredential(RealmModel realm, UserModel user, CredentialInput input);
}
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.
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.
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.
Let's walk through an authentication flow, specificaly for OTP.
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().
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.
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.
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.
** Admin console and Account Service UIs **
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.
_______________________________________________
keycloak-dev mailing list
keycloak-dev(a)lists.jboss.org
https://lists.jboss.org/mailman/listinfo/keycloak-dev