Search in sources :

Example 1 with DefaultChallenge

use of com.webauthn4j.data.client.challenge.DefaultChallenge 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());
}
Also used : UserModel(org.keycloak.models.UserModel) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) LoginFormsProvider(org.keycloak.forms.login.LoginFormsProvider) WebAuthnPolicy(org.keycloak.models.WebAuthnPolicy) WebAuthnAuthenticatorsBean(org.keycloak.forms.login.freemarker.model.WebAuthnAuthenticatorsBean) Challenge(com.webauthn4j.data.client.challenge.Challenge) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge)

Example 2 with DefaultChallenge

use of com.webauthn4j.data.client.challenge.DefaultChallenge 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);
}
Also used : UserModel(org.keycloak.models.UserModel) Response(javax.ws.rs.core.Response) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) WebAuthnCredentialModel(org.keycloak.models.credential.WebAuthnCredentialModel) WebAuthnPolicy(org.keycloak.models.WebAuthnPolicy) Challenge(com.webauthn4j.data.client.challenge.Challenge) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge)

Example 3 with DefaultChallenge

use of com.webauthn4j.data.client.challenge.DefaultChallenge 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;
    }
}
Also used : Origin(com.webauthn4j.data.client.Origin) RegistrationData(com.webauthn4j.data.RegistrationData) ServerProperty(com.webauthn4j.server.ServerProperty) WebAuthnCredentialModelInput(org.keycloak.credential.WebAuthnCredentialModelInput) WebAuthnCredentialProvider(org.keycloak.credential.WebAuthnCredentialProvider) RegistrationRequest(com.webauthn4j.data.RegistrationRequest) WebAuthnException(com.webauthn4j.util.exception.WebAuthnException) Challenge(com.webauthn4j.data.client.challenge.Challenge) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) WebAuthnCredentialModel(org.keycloak.models.credential.WebAuthnCredentialModel) WebAuthnException(com.webauthn4j.util.exception.WebAuthnException) WebAuthnPolicy(org.keycloak.models.WebAuthnPolicy) WebAuthnRegistrationManager(com.webauthn4j.WebAuthnRegistrationManager) WebAuthnCredentialProvider(org.keycloak.credential.WebAuthnCredentialProvider) CredentialProvider(org.keycloak.credential.CredentialProvider) RegistrationParameters(com.webauthn4j.data.RegistrationParameters) HashSet(java.util.HashSet)

Example 4 with DefaultChallenge

use of com.webauthn4j.data.client.challenge.DefaultChallenge 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();
    }
}
Also used : Origin(com.webauthn4j.data.client.Origin) AuthenticationParameters(com.webauthn4j.data.AuthenticationParameters) ServerProperty(com.webauthn4j.server.ServerProperty) WebAuthnCredentialModelInput(org.keycloak.credential.WebAuthnCredentialModelInput) Challenge(com.webauthn4j.data.client.challenge.Challenge) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) UserModel(org.keycloak.models.UserModel) DefaultChallenge(com.webauthn4j.data.client.challenge.DefaultChallenge) WebAuthnException(com.webauthn4j.util.exception.WebAuthnException) AuthenticationRequest(com.webauthn4j.data.AuthenticationRequest)

Aggregations

Challenge (com.webauthn4j.data.client.challenge.Challenge)4 DefaultChallenge (com.webauthn4j.data.client.challenge.DefaultChallenge)4 UserModel (org.keycloak.models.UserModel)3 WebAuthnPolicy (org.keycloak.models.WebAuthnPolicy)3 Origin (com.webauthn4j.data.client.Origin)2 ServerProperty (com.webauthn4j.server.ServerProperty)2 WebAuthnException (com.webauthn4j.util.exception.WebAuthnException)2 WebAuthnCredentialModelInput (org.keycloak.credential.WebAuthnCredentialModelInput)2 WebAuthnCredentialModel (org.keycloak.models.credential.WebAuthnCredentialModel)2 WebAuthnRegistrationManager (com.webauthn4j.WebAuthnRegistrationManager)1 AuthenticationParameters (com.webauthn4j.data.AuthenticationParameters)1 AuthenticationRequest (com.webauthn4j.data.AuthenticationRequest)1 RegistrationData (com.webauthn4j.data.RegistrationData)1 RegistrationParameters (com.webauthn4j.data.RegistrationParameters)1 RegistrationRequest (com.webauthn4j.data.RegistrationRequest)1 HashSet (java.util.HashSet)1 Response (javax.ws.rs.core.Response)1 CredentialProvider (org.keycloak.credential.CredentialProvider)1 WebAuthnCredentialProvider (org.keycloak.credential.WebAuthnCredentialProvider)1 LoginFormsProvider (org.keycloak.forms.login.LoginFormsProvider)1