use of com.webauthn4j.data.AuthenticationParameters in project keycloak by keycloak.
the class WebAuthnCredentialProvider method isValid.
@Override
public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) {
if (!WebAuthnCredentialModelInput.class.isInstance(input))
return false;
WebAuthnCredentialModelInput context = WebAuthnCredentialModelInput.class.cast(input);
List<WebAuthnCredentialModelInput> auths = getWebAuthnCredentialModelList(realm, user);
WebAuthnAuthenticationManager webAuthnAuthenticationManager = new WebAuthnAuthenticationManager();
AuthenticationData authenticationData = null;
try {
for (WebAuthnCredentialModelInput auth : auths) {
byte[] credentialId = auth.getAttestedCredentialData().getCredentialId();
if (Arrays.equals(credentialId, context.getAuthenticationRequest().getCredentialId())) {
Authenticator authenticator = new AuthenticatorImpl(auth.getAttestedCredentialData(), auth.getAttestationStatement(), auth.getCount());
// parse
authenticationData = webAuthnAuthenticationManager.parse(context.getAuthenticationRequest());
// validate
AuthenticationParameters authenticationParameters = new AuthenticationParameters(context.getAuthenticationParameters().getServerProperty(), authenticator, context.getAuthenticationParameters().isUserVerificationRequired());
webAuthnAuthenticationManager.validate(authenticationData, authenticationParameters);
logger.debugv("response.getAuthenticatorData().getFlags() = {0}", authenticationData.getAuthenticatorData().getFlags());
CredentialModel credModel = getCredentialStore().getStoredCredentialById(realm, user, auth.getCredentialDBId());
WebAuthnCredentialModel webAuthnCredModel = getCredentialFromModel(credModel);
// update authenticator counter
// counters are an optional feature of the spec - if an authenticator does not support them, it
// will always send zero. MacOS/iOS does this for keys stored in the secure enclave (TouchID/FaceID)
long count = auth.getCount();
if (count > 0) {
webAuthnCredModel.updateCounter(count + 1);
getCredentialStore().updateCredential(realm, user, webAuthnCredModel);
}
logger.debugf("Successfully validated WebAuthn credential for user %s", user.getUsername());
dumpCredentialModel(webAuthnCredModel, auth);
return true;
}
}
} catch (WebAuthnException wae) {
wae.printStackTrace();
throw (wae);
}
// no authenticator matched
return false;
}
use of com.webauthn4j.data.AuthenticationParameters in project keycloak by keycloak.
the class WebAuthnAuthenticator method action.
public void action(AuthenticationFlowContext context) {
MultivaluedMap<String, String> params = context.getHttpRequest().getDecodedFormParameters();
context.getEvent().detail(Details.CREDENTIAL_TYPE, getCredentialType());
// receive error from navigator.credentials.get()
String errorMsgFromWebAuthnApi = params.getFirst(WebAuthnConstants.ERROR);
if (errorMsgFromWebAuthnApi != null && !errorMsgFromWebAuthnApi.isEmpty()) {
setErrorResponse(context, WEBAUTHN_ERROR_API_GET, errorMsgFromWebAuthnApi);
return;
}
String baseUrl = UriUtils.getOrigin(context.getUriInfo().getBaseUri());
String rpId = getRpID(context);
Origin origin = new Origin(baseUrl);
Challenge challenge = new DefaultChallenge(context.getAuthenticationSession().getAuthNote(WebAuthnConstants.AUTH_CHALLENGE_NOTE));
ServerProperty server = new ServerProperty(origin, rpId, challenge, null);
byte[] credentialId = Base64Url.decode(params.getFirst(WebAuthnConstants.CREDENTIAL_ID));
byte[] clientDataJSON = Base64Url.decode(params.getFirst(WebAuthnConstants.CLIENT_DATA_JSON));
byte[] authenticatorData = Base64Url.decode(params.getFirst(WebAuthnConstants.AUTHENTICATOR_DATA));
byte[] signature = Base64Url.decode(params.getFirst(WebAuthnConstants.SIGNATURE));
final String userHandle = params.getFirst(WebAuthnConstants.USER_HANDLE);
final String userId;
// existing User Handle means that the authenticator used Resident Key supported public key credential
if (userHandle == null || userHandle.isEmpty()) {
// Resident Key not supported public key credential was used
// so rely on the user that has already been authenticated
userId = context.getUser().getId();
} else {
// decode using the same charset as it has been encoded (see: WebAuthnRegister.java)
userId = new String(Base64Url.decode(userHandle), StandardCharsets.UTF_8);
if (context.getUser() != null) {
// Resident Key supported public key credential was used,
// so need to confirm whether the already authenticated user is equals to one authenticated by the webauthn authenticator
String firstAuthenticatedUserId = context.getUser().getId();
if (firstAuthenticatedUserId != null && !firstAuthenticatedUserId.equals(userId)) {
context.getEvent().detail(WebAuthnConstants.FIRST_AUTHENTICATED_USER_ID, firstAuthenticatedUserId).detail(WebAuthnConstants.AUTHENTICATED_USER_ID, userId);
setErrorResponse(context, WEBAUTHN_ERROR_DIFFERENT_USER, null);
return;
}
} else {
// Resident Key supported public key credential was used,
// and the user has not yet been identified
// so rely on the user authenticated by the webauthn authenticator
// NOP
}
}
boolean isUVFlagChecked = false;
String userVerificationRequirement = getWebAuthnPolicy(context).getUserVerificationRequirement();
if (WebAuthnConstants.OPTION_REQUIRED.equals(userVerificationRequirement))
isUVFlagChecked = true;
UserModel user = session.users().getUserById(context.getRealm(), userId);
AuthenticationRequest authenticationRequest = new AuthenticationRequest(credentialId, authenticatorData, clientDataJSON, signature);
AuthenticationParameters authenticationParameters = new AuthenticationParameters(server, // here authenticator cannot be fetched, set it afterwards in WebAuthnCredentialProvider.isValid()
null, isUVFlagChecked);
WebAuthnCredentialModelInput cred = new WebAuthnCredentialModelInput(getCredentialType());
cred.setAuthenticationRequest(authenticationRequest);
cred.setAuthenticationParameters(authenticationParameters);
boolean result = false;
try {
result = session.userCredentialManager().isValid(context.getRealm(), user, cred);
} catch (WebAuthnException wae) {
setErrorResponse(context, WEBAUTHN_ERROR_AUTH_VERIFICATION, wae.getMessage());
return;
}
String encodedCredentialID = Base64Url.encode(credentialId);
if (result) {
String isUVChecked = Boolean.toString(isUVFlagChecked);
logger.debugv("WebAuthn Authentication successed. isUserVerificationChecked = {0}, PublicKeyCredentialID = {1}", isUVChecked, encodedCredentialID);
context.setUser(user);
context.getEvent().detail(WebAuthnConstants.USER_VERIFICATION_CHECKED, isUVChecked).detail(WebAuthnConstants.PUBKEY_CRED_ID_ATTR, encodedCredentialID);
context.success();
} else {
context.getEvent().detail(WebAuthnConstants.AUTHENTICATED_USER_ID, userId).detail(WebAuthnConstants.PUBKEY_CRED_ID_ATTR, encodedCredentialID);
setErrorResponse(context, WEBAUTHN_ERROR_USER_NOT_FOUND, null);
context.cancelLogin();
}
}
Aggregations