Since there was no answer in this topic, I can leave an answer for other people who may
look for something similar.
I created a new registration flow definition with additional form action at beginning of
it. My custom form action contains validation rules working on top of form data. Based on
received attributes validation fails registration attempt if external system client do not
contain matching user record. Thanks to that no database write attempt is made and there
is no need for implementing user storage provider. I attach pseudo code for reference.
There are lots of missing pieces, but it should give general idea how problem can be
approached.
public class InternalUserFormAction implements FormAction {
public static final String PROVIDER_ID = “internal-user-form-action";
@Override
public void validate(ValidationContext context) {
AuthenticatorConfigModel config = context.getAuthenticatorConfig();
// form action have one option which is external system uri, which should be called
in order to check if user who is attempted to made account exists.
String externalSystemURI = Constants.DEFAULT_SYSTEM_URI;
if (config != null && config.getConfig() != null) {
externalSystemURI =
config.getConfig().getOrDefault(Constants.SYSTEM_URI_OPTION_NAME,
Constants.DEFAULT_SYSTEM_URI);
}
MultivaluedMap<String, String> formData =
context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
String eventError = Errors.INVALID_REGISTRATION;
Optional<String> firstName = validateAndGet(formData, errors,
RegistrationPage.FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME);
Optional<String> lastName = validateAndGet(formData, errors,
RegistrationPage.FIELD_LAST_NAME, Messages.MISSING_LAST_NAME);
Optional<String> email = validateAndGet(formData, errors,
RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL);
Optional<String> phone = validateAndGet(formData, errors,
Constants.MOBILE_NUMBER_ATTRIBUTE, "missing_phone_number");
if (errors.size() > 0) {
fail(context, formData, eventError, errors);
return;
} else {
ExternalSystemClient client = new ExternalSystemClient(externalSystemURI);
Optional<InternalUser> internalUser = ex.find(
firstName.orElse(null),
lastName.orElse(null),
email.orElse(null),
phone.orElse(null)
));
if (!internalUser.isPresent()) {
// final check if all above data is valid
errors.add(new FormMessage(“generic", "unknown_member"));
fail(context, formData, eventError, errors);
return;
}
context.success();
}
}
}
Cheers,
Łukasz
—
Twitter: ldywicki
Blog:
http://dywicki.pl
Code-House -
http://code-house.org
On 29 Mar 2018, at 00:44, Łukasz Dywicki <luke(a)code-house.org>
wrote:
Hi all,
I have a case which is quite simple in terms of logic - I have existing
database of users with attributes such first and last name, as well as
email. I miss username and password or just password if I decide to use
email as login. I would like to use attributes I know for validation of
new user registrations.
Any registration attempt with uknown email, first and last should be denied.
Sadly due to necessity to host user self registration in mobile app I
had to move it outside of keycloak. This means I use a small utility to
create accounts using admin api.
I've tried to use UserStorageProvider, but this SPI is not permited to
"deny" user registration. When I try to add new user, it goes in even if
there is no matching combination of attributes. Which SPI is valid for
my use case?
Kind regards,
Lukasz