25
25
package com .yubico .webauthn ;
26
26
27
27
import com .fasterxml .jackson .annotation .JsonCreator ;
28
+ import com .fasterxml .jackson .annotation .JsonIgnore ;
28
29
import com .fasterxml .jackson .annotation .JsonProperty ;
29
- import com .yubico .internal .util .ExceptionUtil ;
30
30
import com .yubico .webauthn .data .AuthenticatorAssertionExtensionOutputs ;
31
+ import com .yubico .webauthn .data .AuthenticatorAssertionResponse ;
31
32
import com .yubico .webauthn .data .AuthenticatorData ;
32
33
import com .yubico .webauthn .data .ByteArray ;
33
34
import com .yubico .webauthn .data .ClientAssertionExtensionOutputs ;
35
+ import com .yubico .webauthn .data .PublicKeyCredential ;
34
36
import com .yubico .webauthn .data .PublicKeyCredentialRequestOptions ;
35
37
import com .yubico .webauthn .data .UserIdentity ;
36
38
import java .util .Optional ;
37
- import lombok .Builder ;
39
+ import lombok .AccessLevel ;
40
+ import lombok .Getter ;
38
41
import lombok .NonNull ;
39
42
import lombok .Value ;
40
43
41
44
/** The result of a call to {@link RelyingParty#finishAssertion(FinishAssertionOptions)}. */
42
45
@ Value
43
- @ Builder (toBuilder = true )
44
46
public class AssertionResult {
45
47
46
48
/** <code>true</code> if the assertion was verified successfully. */
47
49
private final boolean success ;
48
50
51
+ @ JsonProperty
52
+ @ Getter (AccessLevel .NONE )
53
+ private final PublicKeyCredential <AuthenticatorAssertionResponse , ClientAssertionExtensionOutputs >
54
+ credentialResponse ;
55
+
49
56
/**
50
57
* The {@link RegisteredCredential} that was returned by {@link
51
58
* CredentialRepository#lookup(ByteArray, ByteArray)} and whose public key was used to
52
59
* successfully verify the assertion signature.
53
60
*
54
- * <p>NOTE: The {@link RegisteredCredential#getSignatureCount() signature count} in this object
55
- * will reflect the signature counter state <i>before</i> the assertion operation, not the new
56
- * counter value. When updating your database state, use the signature counter from {@link
57
- * #getSignatureCount()} instead.
61
+ * <p>NOTE: The {@link RegisteredCredential#getSignatureCount() signature count}, {@link
62
+ * RegisteredCredential#isBackupEligible() backup eligibility} and {@link
63
+ * RegisteredCredential#isBackedUp() backup state} properties in this object will reflect the
64
+ * state <i>before</i> the assertion operation, not the new state. When updating your database
65
+ * state, use the signature counter and backup state from {@link #getSignatureCount()}, {@link
66
+ * #isBackupEligible()} and {@link #isBackedUp()} instead.
58
67
*/
59
68
private final RegisteredCredential credential ;
60
69
@@ -65,16 +74,6 @@ public class AssertionResult {
65
74
*/
66
75
@ NonNull private final String username ;
67
76
68
- /**
69
- * The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature
70
- * count</a> of the credential used for the assertion.
71
- *
72
- * <p>You should update this value in your database.
73
- *
74
- * @see AuthenticatorData#getSignatureCounter()
75
- */
76
- private final long signatureCount ;
77
-
78
77
/**
79
78
* <code>true</code> if and only if at least one of the following is true:
80
79
*
@@ -96,65 +95,20 @@ public class AssertionResult {
96
95
*/
97
96
private final boolean signatureCounterValid ;
98
97
99
- private final ClientAssertionExtensionOutputs clientExtensionOutputs ;
100
-
101
- private final AuthenticatorAssertionExtensionOutputs authenticatorExtensionOutputs ;
102
-
103
- private AssertionResult (
104
- boolean success ,
105
- @ NonNull @ JsonProperty ("credential" ) RegisteredCredential credential ,
106
- @ NonNull String username ,
107
- long signatureCount ,
108
- boolean signatureCounterValid ,
109
- ClientAssertionExtensionOutputs clientExtensionOutputs ,
110
- AuthenticatorAssertionExtensionOutputs authenticatorExtensionOutputs ) {
111
- this (
112
- success ,
113
- credential ,
114
- username ,
115
- null ,
116
- null ,
117
- signatureCount ,
118
- signatureCounterValid ,
119
- clientExtensionOutputs ,
120
- authenticatorExtensionOutputs );
121
- }
122
-
123
98
@ JsonCreator
124
- private AssertionResult (
99
+ AssertionResult (
125
100
@ JsonProperty ("success" ) boolean success ,
101
+ @ NonNull @ JsonProperty ("credentialResponse" )
102
+ PublicKeyCredential <AuthenticatorAssertionResponse , ClientAssertionExtensionOutputs >
103
+ credentialResponse ,
126
104
@ NonNull @ JsonProperty ("credential" ) RegisteredCredential credential ,
127
105
@ NonNull @ JsonProperty ("username" ) String username ,
128
- @ JsonProperty ("credentialId" ) ByteArray credentialId , // TODO: Delete in next major release
129
- @ JsonProperty ("userHandle" ) ByteArray userHandle , // TODO: Delete in next major release
130
- @ JsonProperty ("signatureCount" ) long signatureCount ,
131
- @ JsonProperty ("signatureCounterValid" ) boolean signatureCounterValid ,
132
- @ JsonProperty ("clientExtensionOutputs" )
133
- ClientAssertionExtensionOutputs clientExtensionOutputs ,
134
- @ JsonProperty ("authenticatorExtensionOutputs" )
135
- AuthenticatorAssertionExtensionOutputs authenticatorExtensionOutputs ) {
106
+ @ JsonProperty ("signatureCounterValid" ) boolean signatureCounterValid ) {
136
107
this .success = success ;
108
+ this .credentialResponse = credentialResponse ;
137
109
this .credential = credential ;
138
110
this .username = username ;
139
-
140
- if (credentialId != null ) {
141
- ExceptionUtil .assure (
142
- credential .getCredentialId ().equals (credentialId ),
143
- "Legacy credentialId is present and does not equal credential.credentialId" );
144
- }
145
- if (userHandle != null ) {
146
- ExceptionUtil .assure (
147
- credential .getUserHandle ().equals (userHandle ),
148
- "Legacy userHandle is present and does not equal credential.userHandle" );
149
- }
150
-
151
- this .signatureCount = signatureCount ;
152
111
this .signatureCounterValid = signatureCounterValid ;
153
- this .clientExtensionOutputs =
154
- clientExtensionOutputs == null || clientExtensionOutputs .getExtensionIds ().isEmpty ()
155
- ? null
156
- : clientExtensionOutputs ;
157
- this .authenticatorExtensionOutputs = authenticatorExtensionOutputs ;
158
112
}
159
113
160
114
/**
@@ -168,6 +122,7 @@ private AssertionResult(
168
122
* getCredentialId()} instead.
169
123
*/
170
124
@ Deprecated
125
+ @ JsonIgnore
171
126
public ByteArray getCredentialId () {
172
127
return credential .getCredentialId ();
173
128
}
@@ -183,10 +138,76 @@ public ByteArray getCredentialId() {
183
138
* getUserHandle()} instead.
184
139
*/
185
140
@ Deprecated
141
+ @ JsonIgnore
186
142
public ByteArray getUserHandle () {
187
143
return credential .getUserHandle ();
188
144
}
189
145
146
+ /**
147
+ * Check whether the asserted credential is <a
148
+ * href="https://w3c.github.io/webauthn/#backup-eligible">backup eligible</a>, using the <a
149
+ * href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag</a> in the authenticator data.
150
+ *
151
+ * <p>You SHOULD store this value in your representation of the corresponding {@link
152
+ * RegisteredCredential} if no value is stored yet. {@link CredentialRepository} implementations
153
+ * SHOULD set this value as the {@link
154
+ * RegisteredCredential.RegisteredCredentialBuilder#backupEligible(Boolean)
155
+ * backupEligible(Boolean)} value when reconstructing that {@link RegisteredCredential}.
156
+ *
157
+ * @return <code>true</code> if and only if the created credential is backup eligible. NOTE that
158
+ * this is only a hint and not a guarantee, unless backed by a trusted authenticator
159
+ * attestation.
160
+ * @see <a href="https://w3c.github.io/webauthn/#backup-eligible">Backup Eligible in §4.
161
+ * Terminology</a>
162
+ * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-be">BE flag in §6.1. Authenticator
163
+ * Data</a>
164
+ * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
165
+ * the standard matures.
166
+ */
167
+ @ Deprecated
168
+ @ JsonIgnore
169
+ public boolean isBackupEligible () {
170
+ return credentialResponse .getResponse ().getParsedAuthenticatorData ().getFlags ().BE ;
171
+ }
172
+
173
+ /**
174
+ * Get the current <a href="https://w3c.github.io/webauthn/#backup-state">backup state</a> of the
175
+ * asserted credential, using the <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS
176
+ * flag</a> in the authenticator data.
177
+ *
178
+ * <p>You SHOULD update this value in your representation of a {@link RegisteredCredential}.
179
+ * {@link CredentialRepository} implementations SHOULD set this value as the {@link
180
+ * RegisteredCredential.RegisteredCredentialBuilder#backupState(Boolean) backupState(Boolean)}
181
+ * value when reconstructing that {@link RegisteredCredential}.
182
+ *
183
+ * @return <code>true</code> if and only if the created credential is believed to currently be
184
+ * backed up. NOTE that this is only a hint and not a guarantee, unless backed by a trusted
185
+ * authenticator attestation.
186
+ * @see <a href="https://w3c.github.io/webauthn/#backup-state">Backup State in §4. Terminology</a>
187
+ * @see <a href="https://w3c.github.io/webauthn/#authdata-flags-bs">BS flag in §6.1. Authenticator
188
+ * Data</a>
189
+ * @deprecated EXPERIMENTAL: This feature is from a not yet mature standard; it could change as
190
+ * the standard matures.
191
+ */
192
+ @ Deprecated
193
+ @ JsonIgnore
194
+ public boolean isBackedUp () {
195
+ return credentialResponse .getResponse ().getParsedAuthenticatorData ().getFlags ().BS ;
196
+ }
197
+
198
+ /**
199
+ * The new <a href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#signcount">signature
200
+ * count</a> of the credential used for the assertion.
201
+ *
202
+ * <p>You should update this value in your database.
203
+ *
204
+ * @see AuthenticatorData#getSignatureCounter()
205
+ */
206
+ @ JsonIgnore
207
+ public long getSignatureCount () {
208
+ return credentialResponse .getResponse ().getParsedAuthenticatorData ().getSignatureCounter ();
209
+ }
210
+
190
211
/**
191
212
* The <a
192
213
* href="https://www.w3.org/TR/2021/REC-webauthn-2-20210408/#client-extension-output">client
@@ -200,8 +221,10 @@ public ByteArray getUserHandle() {
200
221
* @see ClientAssertionExtensionOutputs
201
222
* @see #getAuthenticatorExtensionOutputs() ()
202
223
*/
224
+ @ JsonIgnore
203
225
public Optional <ClientAssertionExtensionOutputs > getClientExtensionOutputs () {
204
- return Optional .ofNullable (clientExtensionOutputs );
226
+ return Optional .of (credentialResponse .getClientExtensionResults ())
227
+ .filter (ceo -> !ceo .getExtensionIds ().isEmpty ());
205
228
}
206
229
207
230
/**
@@ -217,65 +240,9 @@ public Optional<ClientAssertionExtensionOutputs> getClientExtensionOutputs() {
217
240
* @see AuthenticatorAssertionExtensionOutputs
218
241
* @see #getClientExtensionOutputs()
219
242
*/
243
+ @ JsonIgnore
220
244
public Optional <AuthenticatorAssertionExtensionOutputs > getAuthenticatorExtensionOutputs () {
221
- return Optional .ofNullable (authenticatorExtensionOutputs );
222
- }
223
-
224
- static AssertionResultBuilder .MandatoryStages builder () {
225
- return new AssertionResultBuilder .MandatoryStages ();
226
- }
227
-
228
- static class AssertionResultBuilder {
229
- public static class MandatoryStages {
230
- private final AssertionResultBuilder builder = new AssertionResultBuilder ();
231
-
232
- public Step2 success (boolean success ) {
233
- builder .success (success );
234
- return new Step2 ();
235
- }
236
-
237
- public class Step2 {
238
- public Step3 credential (RegisteredCredential credential ) {
239
- builder .credential (credential );
240
- return new Step3 ();
241
- }
242
- }
243
-
244
- public class Step3 {
245
- public Step4 username (String username ) {
246
- builder .username (username );
247
- return new Step4 ();
248
- }
249
- }
250
-
251
- public class Step4 {
252
- public Step5 signatureCount (long signatureCount ) {
253
- builder .signatureCount (signatureCount );
254
- return new Step5 ();
255
- }
256
- }
257
-
258
- public class Step5 {
259
- public Step6 signatureCounterValid (boolean signatureCounterValid ) {
260
- builder .signatureCounterValid (signatureCounterValid );
261
- return new Step6 ();
262
- }
263
- }
264
-
265
- public class Step6 {
266
- public Step7 clientExtensionOutputs (
267
- ClientAssertionExtensionOutputs clientExtensionOutputs ) {
268
- builder .clientExtensionOutputs (clientExtensionOutputs );
269
- return new Step7 ();
270
- }
271
- }
272
-
273
- public class Step7 {
274
- public AssertionResultBuilder assertionExtensionOutputs (
275
- AuthenticatorAssertionExtensionOutputs authenticatorExtensionOutputs ) {
276
- return builder .authenticatorExtensionOutputs (authenticatorExtensionOutputs );
277
- }
278
- }
279
- }
245
+ return AuthenticatorAssertionExtensionOutputs .fromAuthenticatorData (
246
+ credentialResponse .getResponse ().getParsedAuthenticatorData ());
280
247
}
281
248
}
0 commit comments