diff --git a/web/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java b/web/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java index 4dc7efc5a8d..38070587c89 100644 --- a/web/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java +++ b/web/src/main/java/org/springframework/security/web/webauthn/management/Webauthn4JRelyingPartyOperations.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ import com.webauthn4j.data.extension.authenticator.RegistrationExtensionAuthenticatorOutput; import com.webauthn4j.server.ServerProperty; + import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.core.Authentication; @@ -333,9 +334,7 @@ private static Set convertTransports( public PublicKeyCredentialRequestOptions createCredentialRequestOptions( PublicKeyCredentialRequestOptionsRequest request) { Authentication authentication = request.getAuthentication(); - // FIXME: do not load credentialRecords if anonymous - PublicKeyCredentialUserEntity userEntity = findUserEntityOrCreateAndSave(authentication.getName()); - List credentialRecords = this.userCredentials.findByUserId(userEntity.getId()); + List credentialRecords = findCredentialRecords(authentication); return PublicKeyCredentialRequestOptions.builder() .allowCredentials(credentialDescriptors(credentialRecords)) .challenge(Bytes.random()) @@ -346,6 +345,17 @@ public PublicKeyCredentialRequestOptions createCredentialRequestOptions( .build(); } + private List findCredentialRecords(Authentication authentication) { + if (authentication == null || this.trustResolver.isAnonymous(authentication)) { + return Collections.emptyList(); + } + PublicKeyCredentialUserEntity userEntity = this.userEntities.findByUsername(authentication.getName()); + if (userEntity == null) { + return Collections.emptyList(); + } + return this.userCredentials.findByUserId(userEntity.getId()); + } + @Override public PublicKeyCredentialUserEntity authenticate(RelyingPartyAuthenticationRequest request) { PublicKeyCredentialRequestOptions requestOptions = request.getRequestOptions(); diff --git a/web/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java b/web/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java index 4061746e884..db2477ce544 100644 --- a/web/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java +++ b/web/src/test/java/org/springframework/security/web/webauthn/management/Webauthn4jRelyingPartyOperationsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -536,6 +536,27 @@ void createCredentialRequestOptionsThenUserVerificationSameAsCreation() { .isEqualTo(creationOptions.getAuthenticatorSelection().getUserVerification()); } + @Test + void shouldReturnEmptyCredentialsWhenUserIsAnonymous() { + AnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken("key", "anonymousUser", + Set.of(() -> "ROLE_ANONYMOUS")); + PublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest( + authentication); + PublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations + .createCredentialRequestOptions(createRequest); + + assertThat(credentialRequestOptions.getAllowCredentials()).isEmpty(); + } + + @Test + void shouldReturnEmptyCredentialsWhenAnonymousUserIsDisabled() { + PublicKeyCredentialRequestOptionsRequest createRequest = new ImmutablePublicKeyCredentialRequestOptionsRequest(null); + PublicKeyCredentialRequestOptions credentialRequestOptions = this.rpOperations + .createCredentialRequestOptions(createRequest); + + assertThat(credentialRequestOptions.getAllowCredentials()).isEmpty(); + } + private static AuthenticatorAttestationResponse setFlag(byte... flags) throws Exception { AuthenticatorAttestationResponseBuilder authAttResponseBldr = TestAuthenticatorAttestationResponse .createAuthenticatorAttestationResponse();