Skip to content

Commit

Permalink
Fix #1502: FIDO2: Return excludeCredentials in RegistrationChallenge …
Browse files Browse the repository at this point in the history
…(backport) (#1507)
  • Loading branch information
jnpsk authored May 3, 2024
1 parent 4ebccb7 commit fa0d785
Show file tree
Hide file tree
Showing 13 changed files with 62 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
import java.util.List;

/**
* Representation of an allowed authenticator instance.
* Representation of a FIDO2 Credential.
*
* @author Jan Pesek, jan.pesek@wultra.com
*/
@Data
public class AllowCredentials {
public class Credential {

private 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.client.model.response.fido2;

import com.wultra.security.powerauth.client.model.entity.fido2.AllowCredentials;
import com.wultra.security.powerauth.client.model.entity.fido2.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.client.model.response.fido2;

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

import java.util.List;

/**
* @author Roman Strobl, roman.strobl@wultra.com
*/
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 @@ -18,7 +18,7 @@

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

import com.wultra.powerauth.fido2.rest.model.entity.AllowCredentials;
import com.wultra.powerauth.fido2.rest.model.entity.Credential;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.model.entity.Fido2DefaultAuthenticators;
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 @@ -18,11 +18,16 @@

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

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

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

/**
* @author Petr Dvorak, petr@wultra.com
*/
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 @@ -35,6 +35,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 @@ -27,15 +27,15 @@
import java.util.List;

/**
* Representation of an allowed authenticator instance.
* Representation of a FIDO2 Credential.
*
* @author Petr Dvorak, petr@wultra.com
*/
@Getter
@EqualsAndHashCode
@ToString
@Builder
public class AllowCredentials {
public class Credential {

private final byte[] credentialId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import lombok.Data;

import java.util.List;

/**
* Model class representing registration challenge.
*
Expand All @@ -31,4 +33,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 @@ -18,7 +18,7 @@

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

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

import java.util.List;
Expand All @@ -36,6 +36,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,8 +18,11 @@

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

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

import java.util.List;

/**
* Registration challenge response.
*
Expand All @@ -32,6 +35,6 @@ public class RegistrationChallengeResponse {
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 @@ -18,7 +18,7 @@

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

import com.wultra.powerauth.fido2.rest.model.entity.AllowCredentials;
import com.wultra.powerauth.fido2.rest.model.entity.Credential;
import com.wultra.powerauth.fido2.rest.model.entity.AssertionChallenge;
import com.wultra.powerauth.fido2.rest.model.entity.AuthenticatorDetail;
import com.wultra.powerauth.fido2.rest.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,8 @@
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.Credential;
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 @@ -42,11 +44,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
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 @@ -62,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(Protocols.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 @@ -51,6 +51,7 @@
import com.wultra.security.powerauth.client.model.response.OperationTemplateDetailResponse;
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 @@ -72,6 +73,7 @@
*/
@SpringBootTest(classes = Application.class)
@ActiveProfiles("test")
@Transactional
class Fido2AuthenticatorTest {

private final CBORMapper CBOR_MAPPER = new CBORMapper();
Expand Down

0 comments on commit fa0d785

Please sign in to comment.