use of com.webauthn4j.data.client.challenge.Challenge in project keycloak by keycloak.
the class WebAuthnAuthenticator method authenticate.
public void authenticate(AuthenticationFlowContext context) {
LoginFormsProvider form = context.form();
Challenge challenge = new DefaultChallenge();
String challengeValue = Base64Url.encode(challenge.getValue());
context.getAuthenticationSession().setAuthNote(WebAuthnConstants.AUTH_CHALLENGE_NOTE, challengeValue);
form.setAttribute(WebAuthnConstants.CHALLENGE, challengeValue);
WebAuthnPolicy policy = getWebAuthnPolicy(context);
String rpId = getRpID(context);
form.setAttribute(WebAuthnConstants.RP_ID, rpId);
form.setAttribute(WebAuthnConstants.CREATE_TIMEOUT, policy.getCreateTimeout());
UserModel user = context.getUser();
boolean isUserIdentified = false;
if (user != null) {
// in 2 Factor Scenario where the user has already been identified
WebAuthnAuthenticatorsBean authenticators = new WebAuthnAuthenticatorsBean(context.getSession(), context.getRealm(), user, getCredentialType());
if (authenticators.getAuthenticators().isEmpty()) {
// require the user to register webauthn authenticator
return;
}
isUserIdentified = true;
form.setAttribute(WebAuthnConstants.ALLOWED_AUTHENTICATORS, authenticators);
} else {
// in ID-less & Password-less Scenario
// NOP
}
form.setAttribute(WebAuthnConstants.IS_USER_IDENTIFIED, Boolean.toString(isUserIdentified));
// read options from policy
String userVerificationRequirement = policy.getUserVerificationRequirement();
form.setAttribute(WebAuthnConstants.USER_VERIFICATION, userVerificationRequirement);
form.setAttribute(WebAuthnConstants.SHOULD_DISPLAY_AUTHENTICATORS, shouldDisplayAuthenticators(context));
context.challenge(form.createLoginWebAuthn());
}
use of com.webauthn4j.data.client.challenge.Challenge in project keycloak by keycloak.
the class WebAuthnRegister method requiredActionChallenge.
@Override
public void requiredActionChallenge(RequiredActionContext context) {
UserModel userModel = context.getUser();
// Use standard UTF-8 charset to get bytes from string.
// Otherwise the platform's default charset is used and it might cause problems later when
// decoded on different system.
String userId = Base64Url.encode(userModel.getId().getBytes(StandardCharsets.UTF_8));
String username = userModel.getUsername();
Challenge challenge = new DefaultChallenge();
String challengeValue = Base64Url.encode(challenge.getValue());
context.getAuthenticationSession().setAuthNote(WebAuthnConstants.AUTH_CHALLENGE_NOTE, challengeValue);
// construct parameters for calling WebAuthn API navigator.credential.create()
// mandatory
WebAuthnPolicy policy = getWebAuthnPolicy(context);
List<String> signatureAlgorithmsList = policy.getSignatureAlgorithm();
String signatureAlgorithms = stringifySignatureAlgorithms(signatureAlgorithmsList);
String rpEntityName = policy.getRpEntityName();
// optional
String rpId = policy.getRpId();
if (rpId == null || rpId.isEmpty())
rpId = context.getUriInfo().getBaseUri().getHost();
String attestationConveyancePreference = policy.getAttestationConveyancePreference();
String authenticatorAttachment = policy.getAuthenticatorAttachment();
String requireResidentKey = policy.getRequireResidentKey();
String userVerificationRequirement = policy.getUserVerificationRequirement();
long createTimeout = policy.getCreateTimeout();
boolean avoidSameAuthenticatorRegister = policy.isAvoidSameAuthenticatorRegister();
String excludeCredentialIds = "";
if (avoidSameAuthenticatorRegister) {
excludeCredentialIds = session.userCredentialManager().getStoredCredentialsByTypeStream(context.getRealm(), userModel, getCredentialType()).map(credentialModel -> {
WebAuthnCredentialModel credModel = WebAuthnCredentialModel.createFromCredentialModel(credentialModel);
return Base64Url.encodeBase64ToBase64Url(credModel.getWebAuthnCredentialData().getCredentialId());
}).collect(Collectors.joining(","));
}
String isSetRetry = null;
if (isFormDataRequest(context.getHttpRequest())) {
isSetRetry = context.getHttpRequest().getDecodedFormParameters().getFirst(WebAuthnConstants.IS_SET_RETRY);
}
Response form = context.form().setAttribute(WebAuthnConstants.CHALLENGE, challengeValue).setAttribute(WebAuthnConstants.USER_ID, userId).setAttribute(WebAuthnConstants.USER_NAME, username).setAttribute(WebAuthnConstants.RP_ENTITY_NAME, rpEntityName).setAttribute(WebAuthnConstants.SIGNATURE_ALGORITHMS, signatureAlgorithms).setAttribute(WebAuthnConstants.RP_ID, rpId).setAttribute(WebAuthnConstants.ATTESTATION_CONVEYANCE_PREFERENCE, attestationConveyancePreference).setAttribute(WebAuthnConstants.AUTHENTICATOR_ATTACHMENT, authenticatorAttachment).setAttribute(WebAuthnConstants.REQUIRE_RESIDENT_KEY, requireResidentKey).setAttribute(WebAuthnConstants.USER_VERIFICATION_REQUIREMENT, userVerificationRequirement).setAttribute(WebAuthnConstants.CREATE_TIMEOUT, createTimeout).setAttribute(WebAuthnConstants.EXCLUDE_CREDENTIAL_IDS, excludeCredentialIds).setAttribute(WebAuthnConstants.IS_SET_RETRY, isSetRetry).createForm("webauthn-register.ftl");
context.challenge(form);
}
use of com.webauthn4j.data.client.challenge.Challenge in project keycloak by keycloak.
the class WebAuthnRegister method processAction.
@Override
public void processAction(RequiredActionContext context) {
MultivaluedMap<String, String> params = context.getHttpRequest().getDecodedFormParameters();
String isSetRetry = params.getFirst(WebAuthnConstants.IS_SET_RETRY);
if (isSetRetry != null && !isSetRetry.isEmpty()) {
requiredActionChallenge(context);
return;
}
context.getEvent().detail(Details.CREDENTIAL_TYPE, getCredentialType());
// receive error from navigator.credentials.create()
String errorMsgFromWebAuthnApi = params.getFirst(WebAuthnConstants.ERROR);
if (errorMsgFromWebAuthnApi != null && !errorMsgFromWebAuthnApi.isEmpty()) {
setErrorResponse(context, WEBAUTHN_ERROR_REGISTER_VERIFICATION, errorMsgFromWebAuthnApi);
return;
}
WebAuthnPolicy policy = getWebAuthnPolicy(context);
String rpId = policy.getRpId();
if (rpId == null || rpId.isEmpty())
rpId = context.getUriInfo().getBaseUri().getHost();
String label = params.getFirst(WebAuthnConstants.AUTHENTICATOR_LABEL);
byte[] clientDataJSON = Base64.getUrlDecoder().decode(params.getFirst(WebAuthnConstants.CLIENT_DATA_JSON));
byte[] attestationObject = Base64.getUrlDecoder().decode(params.getFirst(WebAuthnConstants.ATTESTATION_OBJECT));
String publicKeyCredentialId = params.getFirst(WebAuthnConstants.PUBLIC_KEY_CREDENTIAL_ID);
Origin origin = new Origin(UriUtils.getOrigin(context.getUriInfo().getBaseUri()));
Challenge challenge = new DefaultChallenge(context.getAuthenticationSession().getAuthNote(WebAuthnConstants.AUTH_CHALLENGE_NOTE));
ServerProperty serverProperty = new ServerProperty(origin, rpId, challenge, null);
// check User Verification by considering a malicious user might modify the result of calling WebAuthn API
boolean isUserVerificationRequired = policy.getUserVerificationRequirement().equals(WebAuthnConstants.OPTION_REQUIRED);
final String transportsParam = params.getFirst(WebAuthnConstants.TRANSPORTS);
RegistrationRequest registrationRequest;
if (StringUtil.isNotBlank(transportsParam)) {
final Set<String> transports = new HashSet<>(Arrays.asList(transportsParam.split(",")));
registrationRequest = new RegistrationRequest(attestationObject, clientDataJSON, transports);
} else {
registrationRequest = new RegistrationRequest(attestationObject, clientDataJSON);
}
RegistrationParameters registrationParameters = new RegistrationParameters(serverProperty, isUserVerificationRequired);
WebAuthnRegistrationManager webAuthnRegistrationManager = createWebAuthnRegistrationManager();
try {
// parse
RegistrationData registrationData = webAuthnRegistrationManager.parse(registrationRequest);
// validate
webAuthnRegistrationManager.validate(registrationData, registrationParameters);
showInfoAfterWebAuthnApiCreate(registrationData);
checkAcceptedAuthenticator(registrationData, policy);
WebAuthnCredentialModelInput credential = new WebAuthnCredentialModelInput(getCredentialType());
credential.setAttestedCredentialData(registrationData.getAttestationObject().getAuthenticatorData().getAttestedCredentialData());
credential.setCount(registrationData.getAttestationObject().getAuthenticatorData().getSignCount());
credential.setAttestationStatementFormat(registrationData.getAttestationObject().getFormat());
credential.setTransports(registrationData.getTransports());
// Save new webAuthn credential
WebAuthnCredentialProvider webAuthnCredProvider = (WebAuthnCredentialProvider) this.session.getProvider(CredentialProvider.class, getCredentialProviderId());
WebAuthnCredentialModel newCredentialModel = webAuthnCredProvider.getCredentialModelFromCredentialInput(credential, label);
webAuthnCredProvider.createCredential(context.getRealm(), context.getUser(), newCredentialModel);
String aaguid = newCredentialModel.getWebAuthnCredentialData().getAaguid();
logger.debugv("WebAuthn credential registration success for user {0}. credentialType = {1}, publicKeyCredentialId = {2}, publicKeyCredentialLabel = {3}, publicKeyCredentialAAGUID = {4}", context.getUser().getUsername(), getCredentialType(), publicKeyCredentialId, label, aaguid);
webAuthnCredProvider.dumpCredentialModel(newCredentialModel, credential);
context.getEvent().detail(WebAuthnConstants.PUBKEY_CRED_ID_ATTR, publicKeyCredentialId).detail(WebAuthnConstants.PUBKEY_CRED_LABEL_ATTR, label).detail(WebAuthnConstants.PUBKEY_CRED_AAGUID_ATTR, aaguid);
context.success();
} catch (WebAuthnException wae) {
if (logger.isDebugEnabled())
logger.debug(wae.getMessage(), wae);
setErrorResponse(context, WEBAUTHN_ERROR_REGISTRATION, wae.getMessage());
return;
} catch (Exception e) {
if (logger.isDebugEnabled())
logger.debug(e.getMessage(), e);
setErrorResponse(context, WEBAUTHN_ERROR_REGISTRATION, e.getMessage());
return;
}
}
use of com.webauthn4j.data.client.challenge.Challenge 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