25
25
package com .yubico .webauthn ;
26
26
27
27
import com .yubico .webauthn .data .AssertionExtensionInputs ;
28
+ import com .yubico .webauthn .data .ByteArray ;
28
29
import com .yubico .webauthn .data .PublicKeyCredentialRequestOptions ;
29
30
import com .yubico .webauthn .data .UserVerificationRequirement ;
30
31
import java .util .Optional ;
37
38
@ Builder (toBuilder = true )
38
39
public class StartAssertionOptions {
39
40
40
- /**
41
- * The username of the user to authenticate, if the user has already been identified.
42
- *
43
- * <p>If this is absent, that implies a first-factor authentication operation - meaning
44
- * identification of the user is deferred until after receiving the response from the client.
45
- *
46
- * <p>The default is empty (absent).
47
- *
48
- * @see <a
49
- * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
50
- * credential</a>
51
- */
52
41
private final String username ;
53
42
43
+ private final ByteArray userHandle ;
44
+
54
45
/**
55
46
* Extension inputs for this authentication operation.
56
47
*
@@ -91,8 +82,16 @@ public class StartAssertionOptions {
91
82
/**
92
83
* The username of the user to authenticate, if the user has already been identified.
93
84
*
94
- * <p>If this is absent, that implies a first-factor authentication operation - meaning
95
- * identification of the user is deferred until after receiving the response from the client.
85
+ * <p>Mutually exclusive with {@link #getUserHandle()}.
86
+ *
87
+ * <p>If this or {@link #getUserHandle()} is present, then {@link
88
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
89
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
90
+ * credentials.
91
+ *
92
+ * <p>If this and {@link #getUserHandle()} are both absent, that implies a first-factor
93
+ * authentication operation - meaning identification of the user is deferred until after receiving
94
+ * the response from the client.
96
95
*
97
96
* <p>The default is empty (absent).
98
97
*
@@ -104,6 +103,32 @@ public Optional<String> getUsername() {
104
103
return Optional .ofNullable (username );
105
104
}
106
105
106
+ /**
107
+ * The user handle of the user to authenticate, if the user has already been identified.
108
+ *
109
+ * <p>Mutually exclusive with {@link #getUsername()}.
110
+ *
111
+ * <p>If this or {@link #getUsername()} is present, then {@link
112
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
113
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
114
+ * credentials.
115
+ *
116
+ * <p>If this and {@link #getUsername()} are both absent, that implies a first-factor
117
+ * authentication operation - meaning identification of the user is deferred until after receiving
118
+ * the response from the client.
119
+ *
120
+ * <p>The default is empty (absent).
121
+ *
122
+ * @see #getUsername()
123
+ * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User Handle</a>
124
+ * @see <a
125
+ * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
126
+ * credential</a>
127
+ */
128
+ public Optional <ByteArray > getUserHandle () {
129
+ return Optional .ofNullable (userHandle );
130
+ }
131
+
107
132
/**
108
133
* The value for {@link PublicKeyCredentialRequestOptions#getUserVerification()} for this
109
134
* authentication operation.
@@ -135,40 +160,130 @@ public Optional<Long> getTimeout() {
135
160
136
161
public static class StartAssertionOptionsBuilder {
137
162
private String username = null ;
163
+ private ByteArray userHandle = null ;
138
164
private UserVerificationRequirement userVerification = null ;
139
165
private Long timeout = null ;
140
166
141
167
/**
142
168
* The username of the user to authenticate, if the user has already been identified.
143
169
*
144
- * <p>If this is absent, that implies a first-factor authentication operation - meaning
145
- * identification of the user is deferred until after receiving the response from the client.
170
+ * <p>Mutually exclusive with {@link #userHandle(Optional)}. Setting this to a present value
171
+ * will set {@link #userHandle(Optional)} to empty.
172
+ *
173
+ * <p>If this or {@link #userHandle(Optional)} is present, then {@link
174
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
175
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
176
+ * credentials.
177
+ *
178
+ * <p>If this and {@link #getUserHandle()} are both absent, that implies a first-factor
179
+ * authentication operation - meaning identification of the user is deferred until after
180
+ * receiving the response from the client.
146
181
*
147
182
* <p>The default is empty (absent).
148
183
*
184
+ * @see #username(String)
185
+ * @see #userHandle(Optional)
186
+ * @see #userHandle(ByteArray)
149
187
* @see <a
150
188
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
151
189
* credential</a>
152
190
*/
153
191
public StartAssertionOptionsBuilder username (@ NonNull Optional <String > username ) {
154
192
this .username = username .orElse (null );
193
+ if (username .isPresent ()) {
194
+ this .userHandle = null ;
195
+ }
155
196
return this ;
156
197
}
157
198
158
199
/**
159
200
* The username of the user to authenticate, if the user has already been identified.
160
201
*
161
- * <p>If this is absent, that implies a first-factor authentication operation - meaning
162
- * identification of the user is deferred until after receiving the response from the client.
202
+ * <p>Mutually exclusive with {@link #userHandle(Optional)}. Setting this to a non-null value
203
+ * will set {@link #userHandle(Optional)} to empty.
204
+ *
205
+ * <p>If this or {@link #userHandle(Optional)} is present, then {@link
206
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
207
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
208
+ * credentials.
209
+ *
210
+ * <p>If this and {@link #getUserHandle()} are both absent, that implies a first-factor
211
+ * authentication operation - meaning identification of the user is deferred until after
212
+ * receiving the response from the client.
163
213
*
164
214
* <p>The default is empty (absent).
165
215
*
216
+ * @see #username(Optional)
217
+ * @see #userHandle(Optional)
218
+ * @see #userHandle(ByteArray)
166
219
* @see <a
167
220
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
168
221
* credential</a>
169
222
*/
170
- public StartAssertionOptionsBuilder username (@ NonNull String username ) {
171
- return this .username (Optional .of (username ));
223
+ public StartAssertionOptionsBuilder username (String username ) {
224
+ return this .username (Optional .ofNullable (username ));
225
+ }
226
+
227
+ /**
228
+ * The user handle of the user to authenticate, if the user has already been identified.
229
+ *
230
+ * <p>Mutually exclusive with {@link #username(Optional)}. Setting this to a present value will
231
+ * set {@link #username(Optional)} to empty.
232
+ *
233
+ * <p>If this or {@link #username(Optional)} is present, then {@link
234
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
235
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
236
+ * credentials.
237
+ *
238
+ * <p>If this and {@link #getUsername()} are both absent, that implies a first-factor
239
+ * authentication operation - meaning identification of the user is deferred until after
240
+ * receiving the response from the client.
241
+ *
242
+ * <p>The default is empty (absent).
243
+ *
244
+ * @see #username(String)
245
+ * @see #username(Optional)
246
+ * @see #userHandle(ByteArray)
247
+ * @see <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#user-handle">User
248
+ * Handle</a>
249
+ * @see <a
250
+ * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
251
+ * credential</a>
252
+ */
253
+ public StartAssertionOptionsBuilder userHandle (@ NonNull Optional <ByteArray > userHandle ) {
254
+ this .userHandle = userHandle .orElse (null );
255
+ if (userHandle .isPresent ()) {
256
+ this .username = null ;
257
+ }
258
+ return this ;
259
+ }
260
+
261
+ /**
262
+ * The user handle of the user to authenticate, if the user has already been identified.
263
+ *
264
+ * <p>Mutually exclusive with {@link #username(Optional)}. Setting this to a non-null value will
265
+ * set {@link #username(Optional)} to empty.
266
+ *
267
+ * <p>If this or {@link #username(Optional)} is present, then {@link
268
+ * RelyingParty#startAssertion(StartAssertionOptions)} will set {@link
269
+ * PublicKeyCredentialRequestOptions#getAllowCredentials()} to the list of that user's
270
+ * credentials.
271
+ *
272
+ * <p>If this and {@link #getUsername()} are both absent, that implies a first-factor
273
+ * authentication operation - meaning identification of the user is deferred until after
274
+ * receiving the response from the client.
275
+ *
276
+ * <p>The default is empty (absent).
277
+ *
278
+ * @see #username(String)
279
+ * @see #username(Optional)
280
+ * @see #userHandle(Optional)
281
+ * @see <a
282
+ * href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-side-discoverable-public-key-credential-source">Client-side-resident
283
+ * credential</a>
284
+ */
285
+ public StartAssertionOptionsBuilder userHandle (ByteArray userHandle ) {
286
+ return this .userHandle (Optional .ofNullable (userHandle ));
172
287
}
173
288
174
289
/**
@@ -200,8 +315,8 @@ public StartAssertionOptionsBuilder userVerification(
200
315
* <p>The default is {@link UserVerificationRequirement#PREFERRED}.
201
316
*/
202
317
public StartAssertionOptionsBuilder userVerification (
203
- @ NonNull UserVerificationRequirement userVerification ) {
204
- return this .userVerification (Optional .of (userVerification ));
318
+ UserVerificationRequirement userVerification ) {
319
+ return this .userVerification (Optional .ofNullable (userVerification ));
205
320
}
206
321
207
322
/**
@@ -235,5 +350,13 @@ public StartAssertionOptionsBuilder timeout(@NonNull Optional<Long> timeout) {
235
350
public StartAssertionOptionsBuilder timeout (long timeout ) {
236
351
return this .timeout (Optional .of (timeout ));
237
352
}
353
+
354
+ /*
355
+ * Workaround, see: https://github.com/rzwitserloot/lombok/issues/2623#issuecomment-714816001
356
+ * Consider reverting this workaround if Lombok fixes that issue.
357
+ */
358
+ private StartAssertionOptionsBuilder timeout (Long timeout ) {
359
+ return this .timeout (Optional .ofNullable (timeout ));
360
+ }
238
361
}
239
362
}
0 commit comments