Skip to content

Commit

Permalink
Fix #1502: FIDO2: Return excludeCredentials in RegistrationChallenge (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
jnpsk authored May 3, 2024
1 parent a8c56d2 commit 238fc80
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import java.util.List;

/**
* Representation of an allowed authenticator instance.
* Representation of a FIDO2 Credential.
*
* @author Jan Pesek, [email protected]
*/
Expand All @@ -34,7 +34,7 @@
@ToString
@Builder
@Jacksonized
public class AllowCredentials {
public class Credential {

private final byte[] credentialId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package com.wultra.security.powerauth.fido2.model.response;

import com.wultra.security.powerauth.fido2.model.entity.AllowCredentials;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import lombok.Data;
import lombok.ToString;

Expand All @@ -38,6 +38,6 @@ public class AssertionChallengeResponse {
private String userId;
private Long failedAttempts;
private Long maxFailedAttempts;
private List<AllowCredentials> allowCredentials;
private List<Credential> allowCredentials;

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@

package com.wultra.security.powerauth.fido2.model.response;

import com.wultra.security.powerauth.fido2.model.entity.Credential;
import lombok.Data;
import lombok.ToString;

import java.util.List;

/**
* @author Roman Strobl, [email protected]
*/
Expand All @@ -32,5 +35,6 @@ public class RegistrationChallengeResponse {
@ToString.Exclude
private String challenge;
private String userId;
private List<Credential> excludeCredentials;

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import com.wultra.powerauth.fido2.service.model.Fido2DefaultAuthenticators;
import com.wultra.security.powerauth.client.model.request.OperationCreateRequest;
import com.wultra.security.powerauth.client.model.response.OperationDetailResponse;
import com.wultra.security.powerauth.fido2.model.entity.AllowCredentials;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.security.powerauth.fido2.model.entity.AuthenticatorDetail;
import com.wultra.security.powerauth.fido2.model.request.AssertionChallengeRequest;
Expand Down Expand Up @@ -108,7 +108,7 @@ public static AssertionChallenge convertAssertionChallengeFromOperationDetail(Op
destination.setMaxFailedAttempts(source.getMaxFailureCount());

if (authenticatorDetails != null && !authenticatorDetails.isEmpty()) {
final List<AllowCredentials> allowCredentials = new ArrayList<>();
final List<Credential> allowCredentials = new ArrayList<>();
for (AuthenticatorDetail ad: authenticatorDetails) {

@SuppressWarnings("unchecked")
Expand All @@ -121,11 +121,11 @@ public static AssertionChallenge convertAssertionChallengeFromOperationDetail(Op
credentialId = ByteUtils.concat(credentialId, operationDataBytes);
}

final AllowCredentials ac = AllowCredentials.builder()
final Credential credential = Credential.builder()
.credentialId(credentialId)
.transports(transports)
.build();
allowCredentials.add(ac);
allowCredentials.add(credential);
}
destination.setAllowCredentials(allowCredentials);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,15 @@
package com.wultra.powerauth.fido2.rest.model.converter;

import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.security.powerauth.fido2.model.entity.AuthenticatorDetail;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import com.wultra.security.powerauth.fido2.model.response.RegistrationChallengeResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.Base64;
import java.util.List;

/**
* @author Petr Dvorak, [email protected]
*/
Expand All @@ -45,7 +50,19 @@ public RegistrationChallengeResponse fromChallenge(RegistrationChallenge source)
destination.setActivationId(source.getActivationId());
destination.setApplicationId(source.getApplicationId());
destination.setChallenge(source.getChallenge());
destination.setExcludeCredentials(source.getExcludeCredentials());
return destination;
}

public static Credential toCredentialDescriptor(final AuthenticatorDetail authenticatorDetail) {
@SuppressWarnings("unchecked")
final List<String> transports = (List<String>) authenticatorDetail.getExtras().get("transports");
final byte[] credentialId = Base64.getDecoder().decode(authenticatorDetail.getCredentialId());

return Credential.builder()
.credentialId(credentialId)
.transports(transports)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

package com.wultra.powerauth.fido2.rest.model.entity;

import com.wultra.security.powerauth.fido2.model.entity.AllowCredentials;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import lombok.Data;

import java.util.List;
Expand All @@ -36,6 +36,6 @@ public class AssertionChallenge {
private String userId;
private Long failedAttempts;
private Long maxFailedAttempts;
private List<AllowCredentials> allowCredentials;
private List<Credential> allowCredentials;

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@

package com.wultra.powerauth.fido2.rest.model.entity;

import com.wultra.security.powerauth.fido2.model.entity.Credential;
import lombok.Data;

import java.util.List;

/**
* Model class representing registration challenge.
*
Expand All @@ -31,4 +34,5 @@ public class RegistrationChallenge {
private String applicationId;
private String challenge;
private String userId;
private List<Credential> excludeCredentials;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import com.wultra.security.powerauth.client.model.request.OperationCreateRequest;
import com.wultra.security.powerauth.client.model.response.OperationDetailResponse;
import com.wultra.security.powerauth.fido2.model.entity.AllowCredentials;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.security.powerauth.fido2.model.entity.AuthenticatorDetail;
import com.wultra.security.powerauth.fido2.model.request.AssertionChallengeRequest;
Expand Down Expand Up @@ -121,7 +121,7 @@ void testConvertAssertionChallengeFromOperationDetail_nonWultraAuthenticatorDeta
assertEquals(5L, assertionChallenge.getMaxFailedAttempts());

assertNotNull(assertionChallenge.getAllowCredentials());
final AllowCredentials allowCredential = assertionChallenge.getAllowCredentials().get(0);
final Credential allowCredential = assertionChallenge.getAllowCredentials().get(0);
assertArrayEquals("credential-1".getBytes(), allowCredential.getCredentialId());
assertEquals("hybrid", allowCredential.getTransports().get(0));
assertEquals("public-key", allowCredential.getType());
Expand Down Expand Up @@ -154,7 +154,7 @@ void testConvertAssertionChallengeFromOperationDetail_withWultraAuthenticatorDet

assertNotNull(assertionChallenge.getAllowCredentials());
assertEquals(1, assertionChallenge.getAllowCredentials().size());
final AllowCredentials allowCredential = assertionChallenge.getAllowCredentials().get(0);
final Credential allowCredential = assertionChallenge.getAllowCredentials().get(0);
assertArrayEquals("credential-1A1*A100CZK".getBytes(), allowCredential.getCredentialId());
assertEquals("usb", allowCredential.getTransports().get(0));
assertEquals("public-key", allowCredential.getType());
Expand Down Expand Up @@ -194,12 +194,12 @@ void testConvertAssertionChallengeFromOperationDetail_multipleWultraAuthenticato

assertNotNull(assertionChallenge.getAllowCredentials());
assertEquals(2, assertionChallenge.getAllowCredentials().size());
final AllowCredentials allowCredential1 = assertionChallenge.getAllowCredentials().get(0);
final Credential allowCredential1 = assertionChallenge.getAllowCredentials().get(0);
assertArrayEquals("credential-1A1*A100CZK".getBytes(), allowCredential1.getCredentialId());
assertEquals("usb", allowCredential1.getTransports().get(0));
assertEquals("public-key", allowCredential1.getType());

final AllowCredentials allowCredential2 = assertionChallenge.getAllowCredentials().get(1);
final Credential allowCredential2 = assertionChallenge.getAllowCredentials().get(1);
assertArrayEquals("credential-2A1*A100CZK".getBytes(), allowCredential2.getCredentialId());
assertEquals("usb", allowCredential2.getTransports().get(0));
assertEquals("public-key", allowCredential2.getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package io.getlime.security.powerauth.app.server.service.fido2;

import com.wultra.powerauth.fido2.errorhandling.Fido2AuthenticationFailedException;
import com.wultra.powerauth.fido2.rest.model.converter.RegistrationChallengeConverter;
import com.wultra.powerauth.fido2.rest.model.entity.RegistrationChallenge;
import com.wultra.powerauth.fido2.service.provider.RegistrationProvider;
import com.wultra.security.powerauth.client.model.entity.ApplicationConfigurationItem;
Expand All @@ -27,6 +28,7 @@
import com.wultra.security.powerauth.client.model.request.GetApplicationConfigRequest;
import com.wultra.security.powerauth.client.model.response.GetApplicationConfigResponse;
import com.wultra.security.powerauth.client.model.response.InitActivationResponse;
import com.wultra.security.powerauth.fido2.model.entity.Credential;
import io.getlime.security.powerauth.app.server.database.RepositoryCatalogue;
import io.getlime.security.powerauth.app.server.database.model.entity.ActivationRecordEntity;
import io.getlime.security.powerauth.app.server.database.model.entity.ApplicationEntity;
Expand All @@ -42,10 +44,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;

import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_AAGUIDS;
import static com.wultra.powerauth.fido2.rest.model.enumeration.Fido2ConfigKeys.CONFIG_KEY_ALLOWED_ATTESTATION_FMT;
Expand All @@ -61,25 +60,33 @@ public class PowerAuthRegistrationProvider implements RegistrationProvider {

private final RepositoryCatalogue repositoryCatalogue;
private final ServiceBehaviorCatalogue serviceBehaviorCatalogue;

private final PowerAuthAuthenticatorProvider authenticatorProvider;
private final KeyConvertor keyConvertor = new KeyConvertor();

@Autowired
public PowerAuthRegistrationProvider(RepositoryCatalogue repositoryCatalogue, ServiceBehaviorCatalogue serviceBehaviorCatalogue) {
public PowerAuthRegistrationProvider(final RepositoryCatalogue repositoryCatalogue, final ServiceBehaviorCatalogue serviceBehaviorCatalogue, final PowerAuthAuthenticatorProvider authenticatorProvider) {
this.repositoryCatalogue = repositoryCatalogue;
this.serviceBehaviorCatalogue = serviceBehaviorCatalogue;
this.authenticatorProvider = authenticatorProvider;
}

@Override
@Transactional
public RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws GenericServiceException {
public RegistrationChallenge provideChallengeForRegistration(String userId, String applicationId) throws GenericServiceException, Fido2AuthenticationFailedException {
final InitActivationResponse initActivationResponse = serviceBehaviorCatalogue.getActivationServiceBehavior()
.initActivation(ActivationProtocol.FIDO2, applicationId, userId, null, null, ActivationOtpValidation.NONE, null, null, keyConvertor);

final List<Credential> excludeCredentials = authenticatorProvider.findByUserId(userId, applicationId)
.stream()
.map(RegistrationChallengeConverter::toCredentialDescriptor)
.toList();

final RegistrationChallenge registrationChallenge = new RegistrationChallenge();
registrationChallenge.setUserId(initActivationResponse.getUserId());
registrationChallenge.setApplicationId(initActivationResponse.getApplicationId());
registrationChallenge.setActivationId(initActivationResponse.getActivationId());
registrationChallenge.setChallenge(initActivationResponse.getActivationCode());
registrationChallenge.setExcludeCredentials(excludeCredentials);
return registrationChallenge;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import com.wultra.security.powerauth.fido2.model.response.RegistrationResponse;
import io.getlime.security.powerauth.app.server.Application;
import io.getlime.security.powerauth.app.server.service.PowerAuthService;
import jakarta.transaction.Transactional;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -66,6 +67,7 @@
*/
@SpringBootTest(classes = Application.class)
@ActiveProfiles("test")
@Transactional
class Fido2AuthenticatorTest {

private static final String RP_ID = "powerauth.com";
Expand Down

0 comments on commit 238fc80

Please sign in to comment.