com.yubico.webauthn.AssertionRequest Maven / Gradle / Ivy
Show all versions of webauthn-server-core Show documentation
// Copyright (c) 2018, Yubico AB
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.yubico.webauthn;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.yubico.internal.util.JacksonCodecs;
import com.yubico.webauthn.data.ByteArray;
import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions;
import java.util.Optional;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;
/**
* A combination of a {@link PublicKeyCredentialRequestOptions} and, optionally, a {@link
* #getUsername() username} or {@link #getUserHandle() user handle}.
*/
@Value
@Builder(toBuilder = true)
public class AssertionRequest {
/**
* An object that can be serialized to JSON and passed as the publicKey
argument to
* navigator.credentials.get()
.
*/
@NonNull private final PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions;
/**
* The username of the user to authenticate, if the user has already been identified.
*
* This is mutually exclusive with {@link #getUserHandle() userHandle}; setting this will unset
* {@link #getUserHandle() userHandle}. When parsing from JSON, {@link #getUserHandle()
* userHandle} takes precedence over this.
*
*
If both this and {@link #getUserHandle() userHandle} are empty, this indicates that this is
* a request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
private final String username;
/**
* The user handle of the user to authenticate, if the user has already been identified.
*
*
This is mutually exclusive with {@link #getUsername() username}; setting this will unset
* {@link #getUsername() username}. When parsing from JSON, this takes precedence over {@link
* #getUsername() username}.
*
*
If both this and {@link #getUsername() username} are empty, this indicates that this is a
* request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
private final ByteArray userHandle;
@JsonCreator
private AssertionRequest(
@NonNull @JsonProperty("publicKeyCredentialRequestOptions")
PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions,
@JsonProperty("username") String username,
@JsonProperty("userHandle") ByteArray userHandle) {
this.publicKeyCredentialRequestOptions = publicKeyCredentialRequestOptions;
if (userHandle != null) {
this.username = null;
this.userHandle = userHandle;
} else {
this.username = username;
this.userHandle = null;
}
}
/**
* The username of the user to authenticate, if the user has already been identified.
*
*
This is mutually exclusive with {@link #getUserHandle()}; if this is present, then {@link
* #getUserHandle()} will be empty.
*
*
If both this and {@link #getUserHandle()} are empty, this indicates that this is a request
* for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public Optional getUsername() {
return Optional.ofNullable(username);
}
/**
* The user handle of the user to authenticate, if the user has already been identified.
*
* This is mutually exclusive with {@link #getUsername()}; if this is present, then {@link
* #getUsername()} will be empty.
*
*
If both this and {@link #getUsername()} are empty, this indicates that this is a request for
* an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public Optional getUserHandle() {
return Optional.ofNullable(userHandle);
}
/**
* Serialize this {@link AssertionRequest} value to JSON suitable for sending to the client.
*
* This is an alias of getPublicKeyCredentialRequestOptions().toCredentialsGetJson()
*
.
*
*
Any {@link ByteArray} values in this data structure will be {@link ByteArray#getBase64Url()
* Base64Url} encoded. Those values MUST be decoded into BufferSource
values (such as
* Uint8Array
) on the client side before calling navigator.credentials.get()
*
.
*
*
After decoding binary values, the resulting JavaScript object is suitable for passing as an
* argument to navigator.credentials.get()
.
*
* @return a JSON value suitable for sending to the client and passing as an argument to
* navigator.credentials.get()
, after decoding binary options from Base64Url strings.
* @throws JsonProcessingException if JSON serialization fails.
*/
public String toCredentialsGetJson() throws JsonProcessingException {
return publicKeyCredentialRequestOptions.toCredentialsGetJson();
}
/**
* Encode this {@link AssertionRequest} to JSON. The inverse of {@link #fromJson(String)}.
*
*
This method is suitable for encoding the {@link AssertionRequest} for temporary storage so
* that it can later be passed as an argument to {@link
* RelyingParty#finishAssertion(FinishAssertionOptions)}. The {@link #fromJson(String)} factory
* function is guaranteed to restore an identical {@link AssertionRequest} instance.
*
*
Note that encoding might not be needed if you can simply keep the {@link AssertionRequest}
* instance in server memory.
*
* @return this {@link AssertionRequest} encoded to JSON.
* @throws JsonProcessingException
*/
public String toJson() throws JsonProcessingException {
return JacksonCodecs.json().writeValueAsString(this);
}
/**
* Decode an {@link AssertionRequest} from JSON. The inverse of {@link #toJson()}.
*
*
If the JSON was generated by the {@link #toJson()} method, then {@link #fromJson(String)} in
* the same library version guarantees to restore an identical {@link AssertionRequest} instance.
* This is not guaranteed between different library versions.
*
* @return a {@link AssertionRequest} decoded from the input JSON.
* @throws JsonProcessingException
*/
public static AssertionRequest fromJson(String json) throws JsonProcessingException {
return JacksonCodecs.json().readValue(json, AssertionRequest.class);
}
public static AssertionRequestBuilder.MandatoryStages builder() {
return new AssertionRequestBuilder.MandatoryStages();
}
public static class AssertionRequestBuilder {
private String username = null;
private ByteArray userHandle = null;
public static class MandatoryStages {
private final AssertionRequestBuilder builder = new AssertionRequestBuilder();
/**
* {@link
* AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions)
* publicKeyCredentialRequestOptions} is a required parameter.
*
* @see
* AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions)
*/
public AssertionRequestBuilder publicKeyCredentialRequestOptions(
PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions) {
return builder.publicKeyCredentialRequestOptions(publicKeyCredentialRequestOptions);
}
}
/**
* The username of the user to authenticate, if the user has already been identified.
*
*
This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-empty
* will unset {@link #userHandle(ByteArray)}.
*
*
If this is empty, this indicates that this is a request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public AssertionRequestBuilder username(@NonNull Optional username) {
return this.username(username.orElse(null));
}
/**
* The username of the user to authenticate, if the user has already been identified.
*
* This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-
* null
will unset {@link #userHandle(ByteArray)}.
*
*
If this is empty, this indicates that this is a request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public AssertionRequestBuilder username(String username) {
this.username = username;
if (username != null) {
this.userHandle = null;
}
return this;
}
/**
* The user handle of the user to authenticate, if the user has already been identified.
*
*
This is mutually exclusive with {@link #username(String)}; setting this to non-empty will
* unset {@link #username(String)}.
*
*
If both this and {@link #username(String)} are empty, this indicates that this is a
* request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public AssertionRequestBuilder userHandle(@NonNull Optional userHandle) {
return this.userHandle(userHandle.orElse(null));
}
/**
* The user handle of the user to authenticate, if the user has already been identified.
*
* This is mutually exclusive with {@link #username(String)}; setting this to non-null
*
will unset {@link #username(String)}.
*
*
If both this and {@link #username(String)} are empty, this indicates that this is a
* request for an assertion by a client-side-discoverable
* credential (passkey). Identification of the user is therefore deferred until the response
* is received.
*
* @see Passkey in passkeys.dev reference
*/
public AssertionRequestBuilder userHandle(ByteArray userHandle) {
if (userHandle != null) {
this.username = null;
}
this.userHandle = userHandle;
return this;
}
}
}