PublicKeyCredentialRequestOptions.java
/*
* Copyright 2004-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.web.webauthn.api;
import java.io.Serial;
import java.io.Serializable;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import org.jspecify.annotations.Nullable;
import org.springframework.util.Assert;
/**
* <a href=
* "https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions">PublicKeyCredentialRequestOptions</a>
* contains the information to create an assertion used for authentication.
*
* @author Rob Winch
* @since 6.4
*/
public final class PublicKeyCredentialRequestOptions implements Serializable {
@Serial
private static final long serialVersionUID = -2970057592835694354L;
private final Bytes challenge;
private final Duration timeout;
private final @Nullable String rpId;
private final List<PublicKeyCredentialDescriptor> allowCredentials;
private final @Nullable UserVerificationRequirement userVerification;
private final AuthenticationExtensionsClientInputs extensions;
private PublicKeyCredentialRequestOptions(Bytes challenge, Duration timeout, @Nullable String rpId,
List<PublicKeyCredentialDescriptor> allowCredentials,
@Nullable UserVerificationRequirement userVerification, AuthenticationExtensionsClientInputs extensions) {
Assert.notNull(challenge, "challenge cannot be null");
Assert.hasText(rpId, "rpId cannot be empty");
this.challenge = challenge;
this.timeout = timeout;
this.rpId = rpId;
this.allowCredentials = allowCredentials;
this.userVerification = userVerification;
this.extensions = extensions;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-challenge">challenge</a>
* property specifies a challenge that the authenticator signs, along with other data,
* when producing an authentication assertion.
* @return the challenge
*/
public Bytes getChallenge() {
return this.challenge;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-timeout">timeout</a>
* property is an OPTIONAL member specifies a time, in milliseconds, that the Relying
* Party is willing to wait for the call to complete.
* @return the timeout
*/
public Duration getTimeout() {
return this.timeout;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-rpid">rpId</a>
* is an OPTIONAL member specifies the RP ID claimed by the Relying Party. The client
* MUST verify that the Relying Party's origin matches the scope of this RP ID.
* @return the relying party id
*/
public @Nullable String getRpId() {
return this.rpId;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-allowcredentials">allowCredentials</a>
* property is an OPTIONAL member is used by the client to find authenticators
* eligible for this authentication ceremony.
* @return the allowCredentials property
*/
public List<PublicKeyCredentialDescriptor> getAllowCredentials() {
return this.allowCredentials;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-userverification">userVerification</a>
* property is an OPTIONAL member specifies the Relying Party's requirements regarding
* user verification for the get() operation.
* @return the user verification
*/
public @Nullable UserVerificationRequirement getUserVerification() {
return this.userVerification;
}
/**
* The <a href=
* "https://www.w3.org/TR/webauthn-3/#dom-publickeycredentialrequestoptions-extensions">extensions</a>
* is an OPTIONAL property used by the Relying Party to provide client extension
* inputs requesting additional processing by the client and authenticator.
* @return the extensions
*/
public AuthenticationExtensionsClientInputs getExtensions() {
return this.extensions;
}
/**
* Creates a {@link PublicKeyCredentialRequestOptionsBuilder}
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public static PublicKeyCredentialRequestOptionsBuilder builder() {
return new PublicKeyCredentialRequestOptionsBuilder();
}
/**
* Used to build a {@link PublicKeyCredentialCreationOptions}.
*
* @author Rob Winch
* @since 6.4
*/
public static final class PublicKeyCredentialRequestOptionsBuilder {
private @Nullable Bytes challenge;
private Duration timeout = Duration.ofMinutes(5);
private @Nullable String rpId;
private List<PublicKeyCredentialDescriptor> allowCredentials = Collections.emptyList();
private @Nullable UserVerificationRequirement userVerification;
private AuthenticationExtensionsClientInputs extensions = new ImmutableAuthenticationExtensionsClientInputs(
new ArrayList<>());
private PublicKeyCredentialRequestOptionsBuilder() {
}
/**
* Sets the {@link #getChallenge()} property.
* @param challenge the challenge
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder challenge(Bytes challenge) {
this.challenge = challenge;
return this;
}
/**
* Sets the {@link #getTimeout()} property.
* @param timeout the timeout
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder timeout(Duration timeout) {
Assert.notNull(timeout, "timeout cannot be null");
this.timeout = timeout;
return this;
}
/**
* Sets the {@link #getRpId()} property.
* @param rpId the rpId property
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder rpId(String rpId) {
this.rpId = rpId;
return this;
}
/**
* Sets the {@link #getAllowCredentials()} property
* @param allowCredentials the allowed credentials
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder allowCredentials(
List<PublicKeyCredentialDescriptor> allowCredentials) {
Assert.notNull(allowCredentials, "allowCredentials cannot be null");
this.allowCredentials = allowCredentials;
return this;
}
/**
* Sets the {@link #getUserVerification()} property.
* @param userVerification the user verification
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder userVerification(UserVerificationRequirement userVerification) {
this.userVerification = userVerification;
return this;
}
/**
* Sets the {@link #getExtensions()} property
* @param extensions the extensions
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder extensions(AuthenticationExtensionsClientInputs extensions) {
this.extensions = extensions;
return this;
}
/**
* Allows customizing the {@link PublicKeyCredentialRequestOptionsBuilder}
* @param customizer the {@link Consumer} used to customize the builder
* @return the {@link PublicKeyCredentialRequestOptionsBuilder}
*/
public PublicKeyCredentialRequestOptionsBuilder customize(
Consumer<PublicKeyCredentialRequestOptionsBuilder> customizer) {
customizer.accept(this);
return this;
}
/**
* Builds a new {@link PublicKeyCredentialRequestOptions}
* @return a new {@link PublicKeyCredentialRequestOptions}
*/
public PublicKeyCredentialRequestOptions build() {
if (this.challenge == null) {
this.challenge = Bytes.random();
}
return new PublicKeyCredentialRequestOptions(this.challenge, this.timeout, this.rpId, this.allowCredentials,
this.userVerification, this.extensions);
}
}
}