1 | // Copyright (c) 2018, Yubico AB | |
2 | // All rights reserved. | |
3 | // | |
4 | // Redistribution and use in source and binary forms, with or without | |
5 | // modification, are permitted provided that the following conditions are met: | |
6 | // | |
7 | // 1. Redistributions of source code must retain the above copyright notice, this | |
8 | // list of conditions and the following disclaimer. | |
9 | // | |
10 | // 2. Redistributions in binary form must reproduce the above copyright notice, | |
11 | // this list of conditions and the following disclaimer in the documentation | |
12 | // and/or other materials provided with the distribution. | |
13 | // | |
14 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
15 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
16 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
17 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
18 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
19 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
20 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
21 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
22 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | ||
25 | package com.yubico.webauthn; | |
26 | ||
27 | import com.fasterxml.jackson.annotation.JsonCreator; | |
28 | import com.fasterxml.jackson.annotation.JsonProperty; | |
29 | import com.fasterxml.jackson.core.JsonProcessingException; | |
30 | import com.yubico.internal.util.JacksonCodecs; | |
31 | import com.yubico.webauthn.data.ByteArray; | |
32 | import com.yubico.webauthn.data.PublicKeyCredentialRequestOptions; | |
33 | import java.util.Optional; | |
34 | import lombok.Builder; | |
35 | import lombok.NonNull; | |
36 | import lombok.Value; | |
37 | ||
38 | /** | |
39 | * A combination of a {@link PublicKeyCredentialRequestOptions} and, optionally, a {@link | |
40 | * #getUsername() username} or {@link #getUserHandle() user handle}. | |
41 | */ | |
42 | @Value | |
43 | @Builder(toBuilder = true) | |
44 | public class AssertionRequest { | |
45 | ||
46 | /** | |
47 | * An object that can be serialized to JSON and passed as the <code>publicKey</code> argument to | |
48 | * <code>navigator.credentials.get()</code>. | |
49 | */ | |
50 | @NonNull private final PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions; | |
51 | ||
52 | /** | |
53 | * The username of the user to authenticate, if the user has already been identified. | |
54 | * | |
55 | * <p>This is mutually exclusive with {@link #getUserHandle() userHandle}; setting this will unset | |
56 | * {@link #getUserHandle() userHandle}. When parsing from JSON, {@link #getUserHandle() | |
57 | * userHandle} takes precedence over this. | |
58 | * | |
59 | * <p>If both this and {@link #getUserHandle() userHandle} are empty, this indicates that this is | |
60 | * a request for an assertion by a <a | |
61 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
62 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
63 | * is received. | |
64 | * | |
65 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
66 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
67 | */ | |
68 | private final String username; | |
69 | ||
70 | /** | |
71 | * The user handle of the user to authenticate, if the user has already been identified. | |
72 | * | |
73 | * <p>This is mutually exclusive with {@link #getUsername() username}; setting this will unset | |
74 | * {@link #getUsername() username}. When parsing from JSON, this takes precedence over {@link | |
75 | * #getUsername() username}. | |
76 | * | |
77 | * <p>If both this and {@link #getUsername() username} are empty, this indicates that this is a | |
78 | * request for an assertion by a <a | |
79 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
80 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
81 | * is received. | |
82 | * | |
83 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
84 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
85 | */ | |
86 | private final ByteArray userHandle; | |
87 | ||
88 | @JsonCreator | |
89 | private AssertionRequest( | |
90 |
1
1. <init> : negated conditional → KILLED |
@NonNull @JsonProperty("publicKeyCredentialRequestOptions") |
91 | PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions, | |
92 | @JsonProperty("username") String username, | |
93 | @JsonProperty("userHandle") ByteArray userHandle) { | |
94 | this.publicKeyCredentialRequestOptions = publicKeyCredentialRequestOptions; | |
95 | ||
96 |
1
1. <init> : negated conditional → KILLED |
if (userHandle != null) { |
97 | this.username = null; | |
98 | this.userHandle = userHandle; | |
99 | } else { | |
100 | this.username = username; | |
101 | this.userHandle = null; | |
102 | } | |
103 | } | |
104 | ||
105 | /** | |
106 | * The username of the user to authenticate, if the user has already been identified. | |
107 | * | |
108 | * <p>This is mutually exclusive with {@link #getUserHandle()}; if this is present, then {@link | |
109 | * #getUserHandle()} will be empty. | |
110 | * | |
111 | * <p>If both this and {@link #getUserHandle()} are empty, this indicates that this is a request | |
112 | * for an assertion by a <a | |
113 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
114 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
115 | * is received. | |
116 | * | |
117 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
118 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
119 | */ | |
120 | public Optional<String> getUsername() { | |
121 |
1
1. getUsername : replaced return value with Optional.empty for com/yubico/webauthn/AssertionRequest::getUsername → KILLED |
return Optional.ofNullable(username); |
122 | } | |
123 | ||
124 | /** | |
125 | * The user handle of the user to authenticate, if the user has already been identified. | |
126 | * | |
127 | * <p>This is mutually exclusive with {@link #getUsername()}; if this is present, then {@link | |
128 | * #getUsername()} will be empty. | |
129 | * | |
130 | * <p>If both this and {@link #getUsername()} are empty, this indicates that this is a request for | |
131 | * an assertion by a <a | |
132 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
133 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
134 | * is received. | |
135 | * | |
136 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
137 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
138 | */ | |
139 | public Optional<ByteArray> getUserHandle() { | |
140 |
1
1. getUserHandle : replaced return value with Optional.empty for com/yubico/webauthn/AssertionRequest::getUserHandle → KILLED |
return Optional.ofNullable(userHandle); |
141 | } | |
142 | ||
143 | /** | |
144 | * Serialize this {@link AssertionRequest} value to JSON suitable for sending to the client. | |
145 | * | |
146 | * <p>This is an alias of <code>getPublicKeyCredentialRequestOptions().toCredentialsGetJson() | |
147 | * </code>. | |
148 | * | |
149 | * <p>Any {@link ByteArray} values in this data structure will be {@link ByteArray#getBase64Url() | |
150 | * Base64Url} encoded. Those values MUST be decoded into <code>BufferSource</code> values (such as | |
151 | * <code>Uint8Array</code>) on the client side before calling <code>navigator.credentials.get() | |
152 | * </code>. | |
153 | * | |
154 | * <p>After decoding binary values, the resulting JavaScript object is suitable for passing as an | |
155 | * argument to <code>navigator.credentials.get()</code>. | |
156 | * | |
157 | * @return a JSON value suitable for sending to the client and passing as an argument to <code> | |
158 | * navigator.credentials.get()</code>, after decoding binary options from Base64Url strings. | |
159 | * @throws JsonProcessingException if JSON serialization fails. | |
160 | */ | |
161 | public String toCredentialsGetJson() throws JsonProcessingException { | |
162 |
1
1. toCredentialsGetJson : replaced return value with "" for com/yubico/webauthn/AssertionRequest::toCredentialsGetJson → KILLED |
return publicKeyCredentialRequestOptions.toCredentialsGetJson(); |
163 | } | |
164 | ||
165 | /** | |
166 | * Encode this {@link AssertionRequest} to JSON. The inverse of {@link #fromJson(String)}. | |
167 | * | |
168 | * <p>This method is suitable for encoding the {@link AssertionRequest} for temporary storage so | |
169 | * that it can later be passed as an argument to {@link | |
170 | * RelyingParty#finishAssertion(FinishAssertionOptions)}. The {@link #fromJson(String)} factory | |
171 | * function is guaranteed to restore an identical {@link AssertionRequest} instance. | |
172 | * | |
173 | * <p>Note that encoding might not be needed if you can simply keep the {@link AssertionRequest} | |
174 | * instance in server memory. | |
175 | * | |
176 | * @return this {@link AssertionRequest} encoded to JSON. | |
177 | * @throws JsonProcessingException | |
178 | */ | |
179 | public String toJson() throws JsonProcessingException { | |
180 |
1
1. toJson : replaced return value with "" for com/yubico/webauthn/AssertionRequest::toJson → KILLED |
return JacksonCodecs.json().writeValueAsString(this); |
181 | } | |
182 | ||
183 | /** | |
184 | * Decode an {@link AssertionRequest} from JSON. The inverse of {@link #toJson()}. | |
185 | * | |
186 | * <p>If the JSON was generated by the {@link #toJson()} method, then {@link #fromJson(String)} in | |
187 | * the same library version guarantees to restore an identical {@link AssertionRequest} instance. | |
188 | * This is not guaranteed between different library versions. | |
189 | * | |
190 | * @return a {@link AssertionRequest} decoded from the input JSON. | |
191 | * @throws JsonProcessingException | |
192 | */ | |
193 | public static AssertionRequest fromJson(String json) throws JsonProcessingException { | |
194 |
1
1. fromJson : replaced return value with null for com/yubico/webauthn/AssertionRequest::fromJson → KILLED |
return JacksonCodecs.json().readValue(json, AssertionRequest.class); |
195 | } | |
196 | ||
197 | public static AssertionRequestBuilder.MandatoryStages builder() { | |
198 |
1
1. builder : replaced return value with null for com/yubico/webauthn/AssertionRequest::builder → KILLED |
return new AssertionRequestBuilder.MandatoryStages(); |
199 | } | |
200 | ||
201 | public static class AssertionRequestBuilder { | |
202 | private String username = null; | |
203 | private ByteArray userHandle = null; | |
204 | ||
205 | public static class MandatoryStages { | |
206 | private final AssertionRequestBuilder builder = new AssertionRequestBuilder(); | |
207 | ||
208 | /** | |
209 | * {@link | |
210 | * AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions) | |
211 | * publicKeyCredentialRequestOptions} is a required parameter. | |
212 | * | |
213 | * @see | |
214 | * AssertionRequestBuilder#publicKeyCredentialRequestOptions(PublicKeyCredentialRequestOptions) | |
215 | */ | |
216 | public AssertionRequestBuilder publicKeyCredentialRequestOptions( | |
217 | PublicKeyCredentialRequestOptions publicKeyCredentialRequestOptions) { | |
218 |
1
1. publicKeyCredentialRequestOptions : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder$MandatoryStages::publicKeyCredentialRequestOptions → KILLED |
return builder.publicKeyCredentialRequestOptions(publicKeyCredentialRequestOptions); |
219 | } | |
220 | } | |
221 | ||
222 | /** | |
223 | * The username of the user to authenticate, if the user has already been identified. | |
224 | * | |
225 | * <p>This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-empty | |
226 | * will unset {@link #userHandle(ByteArray)}. | |
227 | * | |
228 | * <p>If this is empty, this indicates that this is a request for an assertion by a <a | |
229 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
230 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
231 | * is received. | |
232 | * | |
233 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
234 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
235 | */ | |
236 |
1
1. username : negated conditional → KILLED |
public AssertionRequestBuilder username(@NonNull Optional<String> username) { |
237 |
1
1. username : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::username → KILLED |
return this.username(username.orElse(null)); |
238 | } | |
239 | ||
240 | /** | |
241 | * The username of the user to authenticate, if the user has already been identified. | |
242 | * | |
243 | * <p>This is mutually exclusive with {@link #userHandle(ByteArray)}; setting this to non-<code> | |
244 | * null</code> will unset {@link #userHandle(ByteArray)}. | |
245 | * | |
246 | * <p>If this is empty, this indicates that this is a request for an assertion by a <a | |
247 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
248 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
249 | * is received. | |
250 | * | |
251 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
252 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
253 | */ | |
254 | public AssertionRequestBuilder username(String username) { | |
255 | this.username = username; | |
256 |
1
1. username : negated conditional → KILLED |
if (username != null) { |
257 | this.userHandle = null; | |
258 | } | |
259 |
1
1. username : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::username → KILLED |
return this; |
260 | } | |
261 | ||
262 | /** | |
263 | * The user handle of the user to authenticate, if the user has already been identified. | |
264 | * | |
265 | * <p>This is mutually exclusive with {@link #username(String)}; setting this to non-empty will | |
266 | * unset {@link #username(String)}. | |
267 | * | |
268 | * <p>If both this and {@link #username(String)} are empty, this indicates that this is a | |
269 | * request for an assertion by a <a | |
270 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
271 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
272 | * is received. | |
273 | * | |
274 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
275 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
276 | */ | |
277 |
1
1. userHandle : negated conditional → KILLED |
public AssertionRequestBuilder userHandle(@NonNull Optional<ByteArray> userHandle) { |
278 |
1
1. userHandle : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::userHandle → KILLED |
return this.userHandle(userHandle.orElse(null)); |
279 | } | |
280 | ||
281 | /** | |
282 | * The user handle of the user to authenticate, if the user has already been identified. | |
283 | * | |
284 | * <p>This is mutually exclusive with {@link #username(String)}; setting this to non-<code>null | |
285 | * </code> will unset {@link #username(String)}. | |
286 | * | |
287 | * <p>If both this and {@link #username(String)} are empty, this indicates that this is a | |
288 | * request for an assertion by a <a | |
289 | * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">client-side-discoverable | |
290 | * credential</a> (passkey). Identification of the user is therefore deferred until the response | |
291 | * is received. | |
292 | * | |
293 | * @see <a href="https://passkeys.dev/docs/reference/terms/#passkey">Passkey</a> in <a | |
294 | * href="https://passkeys.dev">passkeys.dev</a> reference | |
295 | */ | |
296 | public AssertionRequestBuilder userHandle(ByteArray userHandle) { | |
297 |
1
1. userHandle : negated conditional → KILLED |
if (userHandle != null) { |
298 | this.username = null; | |
299 | } | |
300 | this.userHandle = userHandle; | |
301 |
1
1. userHandle : replaced return value with null for com/yubico/webauthn/AssertionRequest$AssertionRequestBuilder::userHandle → KILLED |
return this; |
302 | } | |
303 | } | |
304 | } | |
Mutations | ||
90 |
1.1 |
|
96 |
1.1 |
|
121 |
1.1 |
|
140 |
1.1 |
|
162 |
1.1 |
|
180 |
1.1 |
|
194 |
1.1 |
|
198 |
1.1 |
|
218 |
1.1 |
|
236 |
1.1 |
|
237 |
1.1 |
|
256 |
1.1 |
|
259 |
1.1 |
|
277 |
1.1 |
|
278 |
1.1 |
|
297 |
1.1 |
|
301 |
1.1 |