@@ -129,7 +129,7 @@ public PIResponse parsePIResponse(String serverResponse)
129129 if (result != null )
130130 {
131131 String r = getString (result , AUTHENTICATION );
132- for (AuthenticationStatus as : AuthenticationStatus .values ())
132+ for (AuthenticationStatus as : AuthenticationStatus .values ())
133133 {
134134 if (as .toString ().equals (r ))
135135 {
@@ -168,14 +168,25 @@ else if ("interactive".equals(modeFromResponse))
168168 response .preferredClientMode = modeFromResponse ;
169169 }
170170 response .message = getString (detail , MESSAGE );
171+ response .username = getString (detail , USERNAME );
171172 response .image = getString (detail , IMAGE );
172173 response .serial = getString (detail , SERIAL );
173174 response .transactionID = getString (detail , TRANSACTION_ID );
174175 response .type = getString (detail , TYPE );
175176 response .otpLength = getInt (detail , OTPLEN );
176-
177+ JsonObject passkeyChallenge = detail .getAsJsonObject (PASSKEY );
178+ if (passkeyChallenge != null && !passkeyChallenge .isJsonNull ())
179+ {
180+ response .passkeyChallenge = passkeyChallenge .toString ();
181+ // The passkey challenge can contain a transaction id, use that if none was set prior
182+ // This will happen if the passkey challenge was requested via /validate/initialize
183+ if (response .transactionID == null || response .transactionID .isEmpty ())
184+ {
185+ response .transactionID = getString (passkeyChallenge , TRANSACTION_ID );
186+ }
187+ }
177188 String r = getString (detail , CHALLENGE_STATUS );
178- for (ChallengeStatus cs : ChallengeStatus .values ())
189+ for (ChallengeStatus cs : ChallengeStatus .values ())
179190 {
180191 if (cs .toString ().equals (r ))
181192 {
@@ -187,12 +198,12 @@ else if ("interactive".equals(modeFromResponse))
187198 if (arrMessages != null )
188199 {
189200 arrMessages .forEach (val ->
190- {
201+ {
191202 if (val != null )
192203 {
193204 response .messages .add (val .getAsString ());
194205 }
195- });
206+ });
196207 }
197208
198209 JsonArray arrChallenges = detail .getAsJsonArray (MULTI_CHALLENGE );
@@ -208,6 +219,17 @@ else if ("interactive".equals(modeFromResponse))
208219 String transactionID = getString (challenge , TRANSACTION_ID );
209220 String type = getString (challenge , TYPE );
210221
222+ if (challenge .has (PASSKEY_REGISTRATION ))
223+ {
224+ response .passkeyRegistration = challenge .get (PASSKEY_REGISTRATION ).toString ();
225+ // TODO for passkey registration with enroll_via_multichallenge, the txid is probably in the wrong place
226+ // as of 3.11.0
227+ if (response .transactionID == null || response .transactionID .isEmpty ())
228+ {
229+ response .transactionID = transactionID ;
230+ }
231+ }
232+
211233 if (TOKEN_TYPE_WEBAUTHN .equals (type ))
212234 {
213235 String webauthnSignRequest = getItemFromAttributes (WEBAUTHN_SIGN_REQUEST , challenge );
@@ -352,24 +374,24 @@ private TokenInfo parseSingleTokenInfo(String json)
352374 if (joInfo != null )
353375 {
354376 joInfo .entrySet ().forEach (entry ->
355- {
377+ {
356378 if (entry .getKey () != null && entry .getValue () != null )
357379 {
358380 info .info .put (entry .getKey (), entry .getValue ().getAsString ());
359381 }
360- });
382+ });
361383 }
362384
363385 JsonArray arrRealms = obj .getAsJsonArray (REALMS );
364386 if (arrRealms != null )
365387 {
366388 arrRealms .forEach (val ->
367- {
389+ {
368390 if (val != null )
369391 {
370392 info .realms .add (val .getAsString ());
371393 }
372- });
394+ });
373395 }
374396 return info ;
375397 }
@@ -465,7 +487,7 @@ Map<String, String> parseWebAuthnSignResponse(String json)
465487 }
466488 catch (JsonSyntaxException e )
467489 {
468- privacyIDEA .error ("WebAuthn sign response has the wrong format: " + e .getLocalizedMessage ());
490+ privacyIDEA .error ("FIDO2 sign response has the wrong format: " + e .getLocalizedMessage ());
469491 return null ;
470492 }
471493
@@ -488,6 +510,40 @@ Map<String, String> parseWebAuthnSignResponse(String json)
488510 return params ;
489511 }
490512
513+ Map <String , String > parseFIDO2AuthenticationResponse (String json )
514+ {
515+ Map <String , String > params = new LinkedHashMap <>();
516+ JsonObject obj ;
517+ try
518+ {
519+ obj = JsonParser .parseString (json ).getAsJsonObject ();
520+ }
521+ catch (JsonSyntaxException e )
522+ {
523+ privacyIDEA .error ("FIDO2 sign response has the wrong format: " + e .getLocalizedMessage ());
524+ return null ;
525+ }
526+
527+ params .put (CREDENTIAL_ID , getString (obj , CREDENTIAL_ID ));
528+ params .put (CLIENTDATAJSON , getString (obj , CLIENTDATAJSON ));
529+ params .put (SIGNATURE , getString (obj , SIGNATURE ));
530+ params .put (AUTHENTICATOR_DATA , getString (obj , AUTHENTICATOR_DATA ));
531+
532+ // The userhandle and assertionclientextension fields are optional
533+ String userhandle = getString (obj , USERHANDLE );
534+ if (!userhandle .isEmpty ())
535+ {
536+ params .put (USERHANDLE , userhandle );
537+ }
538+ String extensions = getString (obj , ASSERTIONCLIENTEXTENSIONS );
539+ if (!extensions .isEmpty ())
540+ {
541+ params .put (ASSERTIONCLIENTEXTENSIONS , extensions );
542+ }
543+ return params ;
544+ }
545+
546+
491547 private boolean getBoolean (JsonObject obj , String name )
492548 {
493549 JsonPrimitive primitive = getPrimitiveOrNull (obj , name );
@@ -521,4 +577,26 @@ private JsonPrimitive getPrimitiveOrNull(JsonObject obj, String name)
521577 }
522578 return primitive ;
523579 }
524- }
580+
581+ public Map <String , String > parseFIDO2RegistrationResponse (String registrationResponse )
582+ {
583+ Map <String , String > params = new LinkedHashMap <>();
584+ JsonObject obj ;
585+ try
586+ {
587+ obj = JsonParser .parseString (registrationResponse ).getAsJsonObject ();
588+ }
589+ catch (JsonSyntaxException e )
590+ {
591+ privacyIDEA .error ("Passkey registration response is not JSON: " + e .getLocalizedMessage ());
592+ return null ;
593+ }
594+
595+ params .put (CREDENTIAL_ID , getString (obj , CREDENTIAL_ID ));
596+ params .put (CLIENTDATAJSON , getString (obj , CLIENTDATAJSON ));
597+ params .put (ATTESTATION_OBJECT , getString (obj , ATTESTATION_OBJECT ));
598+ params .put (AUTHENTICATOR_ATTACHMENT , getString (obj , AUTHENTICATOR_ATTACHMENT ));
599+ params .put (RAW_ID , getString (obj , RAW_ID ));
600+ return params ;
601+ }
602+ }
0 commit comments