Describe the bug
The transports saved with the credential during the registration request are not returned in the transports property of same credential within the Verification Options response provided by /webauthn/authenticate/options.
Note that I'm using the RC version of Spring Security 6.4.0.
To Reproduce 1. Add a Security Configuration using the following implementation:
@Configuration
class SecurityConfig {
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.webAuthn{ it
.rpName("Example")
.rpId("example.localhost")
.allowedOrigins("https://example.localhost")
}
.authorizeRequests { it
.anyRequest()
.permitAll()
}
.csrf { it.disable() }
return http.build()
}
val userDetails = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build()
@Bean
fun userDetailsService(): UserDetailsService {
return InMemoryUserDetailsManager(userDetails)
}
}
- Register a credential like the example in the docs but with an internal transport. Chrome virtual authenticator can be used to do this fairly easily.
{
"publicKey": {
"credential": {
"id": "dYF7EGnRFFIXkpXi9XU2wg",
"rawId": "dYF7EGnRFFIXkpXi9XU2wg",
"response": {
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUy9GqwTRaMpzVDbXq1dyEAXVOxrou08k22ggRC45MKNhdAAAAALraVWanqkAfvZZFYZpVEg0AEHWBexBp0RRSF5KV4vV1NsKlAQIDJiABIVggQjmrekPGzyqtoKK9HPUH-8Z2FLpoqkklFpFPQVICQ3IiWCD6I9Jvmor685fOZOyGXqUd87tXfvJk8rxj9OhuZvUALA",
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiSl9RTi10SFJYRWVKYjlNcUNrWmFPLUdOVmlibXpGVGVWMk43Z0ptQUdrQSIsIm9yaWdpbiI6Imh0dHBzOi8vZXhhbXBsZS5sb2NhbGhvc3Q6ODQ0MyIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
"transports": [
"internal"
]
},
"type": "public-key",
"clientExtensionResults": {},
"authenticatorAttachment": "platform"
},
"label": "1password"
}
}
- POST to /webauthn/authenticate/options
- The resulting response will have the registered credential in the
allowCredentials, but the transports array will be empty.
Expected behavior
The request would return the credential in the allowCredentials with the same transport as was registered.
Comment From: krnbr
@rwinch
The function registerCredential in the file org.springframework.security.web.webauthn.management.Webauthn4JRelyingPartyOperations is not storing the secureTransports correctly
It should have been like below:-
presently it is like below
ImmutableCredentialRecord userCredential = ImmutableCredentialRecord.builder()
.....
.transports(convertTransports(registrationData.getTransports()))
....
.build();
instead of registrationData.getTransports() it should have been -
.transports(new HashSet<>(response.getTransports()))
Tried debugging the flow, registrationData.getTransports() comes blank/null
while response.getTransports() is getting populated with values.
Comment From: rwinch
Thanks for the report @Jyosua the fix is now merged into main.
@krnbr Thanks for the pointer. The underlying issue was that the transports weren't being passed to webauthn4j which meant they were null. By passing it to webauthn4j, the transports are now returned. Note that this is preferred because we want to ensure that it aligns with what the webauthn4j library has verified vs what is being passed in as (untrusted) input.