[keycloak-dev] new credential SPI

Marek Posolda mposolda at redhat.com
Tue Aug 23 10:12:39 EDT 2016


Regarding SPNEGO, I remember we discussed it on ML few years ago and 
agreed on doing it at UserFederation level. However that was before we 
had Authentication SPI :-)

So yes, maybe we can refactor now?

What we can do is:
- Add keytab, kerberos principal and "debug" as properties of 
SPNEGOAuthenticator.
- If user is successfuly authenticated by SPNEGOAuthenticator, he will 
be lookup by UserFederationStorage. If found, then authentication 
finished with success (so the case when user is in LDAP is still 
supported). If he is not found, then he is lazily created (typically the 
usecase for SPNEGO/Kerberos not backed by LDAP)

This shouldn't be too hard to do though.

Regarding multiple handshakes, this is still valid requirement IMO? 
There are authentication mechanisms like SASL, which count with multiple 
handshakes. The Keycloak is currently around passwords and OTP, but 
people may want to add their own credential types or in the future we 
can add more mechanisms, which can require multiple handshakes?

Marek


On 23/08/16 14:47, Bill Burke wrote:
>
> I never understood why all this is done at the UserFederationSPI 
> level.  Why isn't it done at the authenticator level?  Its all 
> protocol stuff.  Would you do the same for bearer tokens if we had 
> to?  I don't think so.  UserStorageSPI is supposed to be an 
> abstraction for storage, not a protocol abstraction.
>
>
> On 8/23/16 3:23 AM, Marek Posolda wrote:
>> 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 at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/keycloak-dev
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/keycloak-dev/attachments/20160823/a9e62f43/attachment-0001.html 


More information about the keycloak-dev mailing list