Skip to content

Commit

Permalink
Add timeout parameter to start ceremony options
Browse files Browse the repository at this point in the history
Fixes issue #23
See #23
  • Loading branch information
emlun committed May 21, 2019
2 parents 3b92984 + 0290ee5 commit 8979e0d
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 0 deletions.
8 changes: 8 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
== Version 1.3.0 (unreleased) ==

New features:

* New optional parameter `timeout` added to `StartRegistrationOptions` and
`StartAssertionOptions`


== Version 1.2.0 ==

New features:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ public PublicKeyCredentialCreationOptions startRegistration(StartRegistrationOpt
)
.authenticatorSelection(startRegistrationOptions.getAuthenticatorSelection())
.extensions(startRegistrationOptions.getExtensions())
.timeout(startRegistrationOptions.getTimeout())
;
attestationConveyancePreference.ifPresent(builder::attestation);
return builder.build();
Expand Down Expand Up @@ -344,6 +345,7 @@ public AssertionRequest startAssertion(StartAssertionOptions startAssertionOptio
.appid(appId)
.build()
)
.timeout(startAssertionOptions.getTimeout())
;

startAssertionOptions.getUserVerification().ifPresent(pkcro::userVerification);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,19 @@ public class StartAssertionOptions {
@NonNull
private final Optional<UserVerificationRequirement> userVerification;

/**
* The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication operation.
* <p>
* The default is empty.
* </p>
*/
@NonNull
private final Optional<Long> timeout;

public static class StartAssertionOptionsBuilder {
private @NonNull Optional<String> username = Optional.empty();
private @NonNull Optional<UserVerificationRequirement> userVerification = Optional.empty();
private @NonNull Optional<Long> timeout = Optional.empty();

/**
* The username of the user to authenticate, if the user has already been identified.
Expand Down Expand Up @@ -141,5 +151,29 @@ public StartAssertionOptionsBuilder userVerification(@NonNull Optional<UserVerif
public StartAssertionOptionsBuilder userVerification(@NonNull UserVerificationRequirement userVerification) {
return this.userVerification(Optional.of(userVerification));
}

/**
* The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication operation.
* <p>
* The default is empty.
* </p>
*/
public StartAssertionOptionsBuilder timeout(@NonNull Optional<Long> timeout) {
if (timeout.isPresent() && timeout.get() <= 0) {
throw new IllegalArgumentException("timeout must be positive, was: " + timeout.get());
}
this.timeout = timeout;
return this;
}

/**
* The value for {@link PublicKeyCredentialRequestOptions#getTimeout()} for this authentication operation.
* <p>
* The default is empty.
* </p>
*/
public StartAssertionOptionsBuilder timeout(long timeout) {
return this.timeout(Optional.of(timeout));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.yubico.webauthn;

import com.yubico.webauthn.data.AuthenticatorSelectionCriteria;
import com.yubico.webauthn.data.PublicKeyCredentialCreationOptions;
import com.yubico.webauthn.data.RegistrationExtensionInputs;
import com.yubico.webauthn.data.UserIdentity;
import java.util.Optional;
Expand Down Expand Up @@ -58,12 +59,22 @@ public class StartRegistrationOptions {
@Builder.Default
private final RegistrationExtensionInputs extensions = RegistrationExtensionInputs.builder().build();

/**
* The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration operation.
* <p>
* The default is empty.
* </p>
*/
@NonNull
private final Optional<Long> timeout;

public static StartRegistrationOptionsBuilder.MandatoryStages builder() {
return new StartRegistrationOptionsBuilder.MandatoryStages();
}

public static class StartRegistrationOptionsBuilder {
private @NonNull Optional<AuthenticatorSelectionCriteria> authenticatorSelection = Optional.empty();
private @NonNull Optional<Long> timeout = Optional.empty();

public static class MandatoryStages {
private final StartRegistrationOptionsBuilder builder = new StartRegistrationOptionsBuilder();
Expand All @@ -87,6 +98,30 @@ public StartRegistrationOptionsBuilder authenticatorSelection(@NonNull Optional<
public StartRegistrationOptionsBuilder authenticatorSelection(@NonNull AuthenticatorSelectionCriteria authenticatorSelection) {
return this.authenticatorSelection(Optional.of(authenticatorSelection));
}

/**
* The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration operation.
* <p>
* The default is empty.
* </p>
*/
public StartRegistrationOptionsBuilder timeout(@NonNull Optional<Long> timeout) {
if (timeout.isPresent() && timeout.get() <= 0) {
throw new IllegalArgumentException("timeout must be positive, was: " + timeout.get());
}
this.timeout = timeout;
return this;
}

/**
* The value for {@link PublicKeyCredentialCreationOptions#getTimeout()} for this registration operation.
* <p>
* The default is empty.
* </p>
*/
public StartRegistrationOptionsBuilder timeout(long timeout) {
return this.timeout(Optional.of(timeout));
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import java.util.Optional

import com.yubico.internal.util.scala.JavaConverters._
import com.yubico.scalacheck.gen.JavaGenerators._
import com.yubico.webauthn.data.AuthenticatorAttachment
import com.yubico.webauthn.data.AuthenticatorSelectionCriteria
import com.yubico.webauthn.data.PublicKeyCredentialDescriptor
import com.yubico.webauthn.data.ByteArray
import com.yubico.webauthn.data.UserIdentity
Expand All @@ -38,6 +40,7 @@ import com.yubico.webauthn.extension.appid.AppId
import com.yubico.webauthn.extension.appid.Generators._
import org.junit.runner.RunWith
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen
import org.scalatest.FunSpec
import org.scalatest.Matchers
import org.scalatest.junit.JUnitRunner
Expand Down Expand Up @@ -104,6 +107,56 @@ class RelyingPartyStartOperationSpec extends FunSpec with Matchers with Generato
request2.getChallenge.size should be >= 32
}

it("allows setting the timeout to empty.") {
val pkcco = relyingParty().startRegistration(
StartRegistrationOptions.builder()
.user(userId)
.timeout(Optional.empty[java.lang.Long])
.build())
pkcco.getTimeout.asScala shouldBe 'empty
}

it("allows setting the timeout to a positive value.") {
val rp = relyingParty()

forAll(Gen.posNum[Long]) { timeout: Long =>
val pkcco = rp.startRegistration(
StartRegistrationOptions.builder()
.user(userId)
.timeout(timeout)
.build())

pkcco.getTimeout.asScala should equal (Some(timeout))
}
}

it("does not allow setting the timeout to zero or negative.") {
an [IllegalArgumentException] should be thrownBy {
StartRegistrationOptions.builder()
.user(userId)
.timeout(0)
}

an [IllegalArgumentException] should be thrownBy {
StartRegistrationOptions.builder()
.user(userId)
.timeout(Optional.of[java.lang.Long](0L))
}

forAll(Gen.negNum[Long]) { timeout: Long =>
an [IllegalArgumentException] should be thrownBy {
StartRegistrationOptions.builder()
.user(userId)
.timeout(timeout)
}

an [IllegalArgumentException] should be thrownBy {
StartRegistrationOptions.builder()
.user(userId)
.timeout(Optional.of[java.lang.Long](timeout))
}
}
}
}

describe("RelyingParty.startAssertion") {
Expand Down Expand Up @@ -152,6 +205,50 @@ class RelyingPartyStartOperationSpec extends FunSpec with Matchers with Generato
}
}

it("allows setting the timeout to empty.") {
val req = relyingParty().startAssertion(
StartAssertionOptions.builder()
.timeout(Optional.empty[java.lang.Long])
.build())
req.getPublicKeyCredentialRequestOptions.getTimeout.asScala shouldBe 'empty
}

it("allows setting the timeout to a positive value.") {
val rp = relyingParty()

forAll(Gen.posNum[Long]) { timeout: Long =>
val req = rp.startAssertion(
StartAssertionOptions.builder()
.timeout(timeout)
.build())

req.getPublicKeyCredentialRequestOptions.getTimeout.asScala should equal (Some(timeout))
}
}

it("does not allow setting the timeout to zero or negative.") {
an [IllegalArgumentException] should be thrownBy {
StartAssertionOptions.builder()
.timeout(0)
}

an [IllegalArgumentException] should be thrownBy {
StartAssertionOptions.builder()
.timeout(Optional.of[java.lang.Long](0L))
}

forAll(Gen.negNum[Long]) { timeout: Long =>
an [IllegalArgumentException] should be thrownBy {
StartAssertionOptions.builder()
.timeout(timeout)
}

an [IllegalArgumentException] should be thrownBy {
StartAssertionOptions.builder()
.timeout(Optional.of[java.lang.Long](timeout))
}
}
}
}

}

0 comments on commit 8979e0d

Please sign in to comment.