From 326694e734cb5ca1ceaca342f3a72fb9c7a8b8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Fri, 21 Aug 2026 10:59:53 +0200 Subject: [PATCH 1/4] Add FIDO2 to MfaMethod --- common/client_types.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/common/client_types.proto b/common/client_types.proto index e758464..eaa5dc9 100644 --- a/common/client_types.proto +++ b/common/client_types.proto @@ -206,6 +206,7 @@ enum MfaMethod { OIDC = 2; BIOMETRIC = 3; MOBILE_APPROVE = 4; + FIDO2 = 5; } // Multi-step MFA (added for 2.2) From 8b4fc5495c4e80a096e0e03f0e82da07a333b83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 26 Aug 2026 10:26:39 +0200 Subject: [PATCH 2/4] Prepare for FIDO2 --- common/client_types.proto | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/client_types.proto b/common/client_types.proto index eaa5dc9..a0e455a 100644 --- a/common/client_types.proto +++ b/common/client_types.proto @@ -335,13 +335,17 @@ message ClientMfaStartResponse { message ClientMfaFinishRequest { string token = 1; + // [2.2] For FIDO2 this holds the hash of RP ID. optional string code = 2; + // [2.2] For FIDO2 this holds the signature. optional string auth_pub_key = 3; // [2.2] The attempt id minted by StepStart for the step being submitted. It binds // this proof to a specific attempt, so a stale or duplicate proof cannot advance // the step twice. Optional so pre-2.2 clients that omit it still parse; a None // value keeps the legacy single-step path working. optional string step_attempt_id = 4; + // [2.2] FIDO2 + optional bytes auth_data = 5; } message ClientMfaFinishResponse { From 2a005ecff5b00d58e1e640d8bf54ad2e858d0891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 26 Aug 2026 12:54:54 +0200 Subject: [PATCH 3/4] Expand FIDO2 --- common/client_types.proto | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/common/client_types.proto b/common/client_types.proto index a0e455a..7779847 100644 --- a/common/client_types.proto +++ b/common/client_types.proto @@ -296,7 +296,11 @@ message ClientMfaStepStartResponse { // callbacks can be matched against the attempt that is actually current. string step_attempt_id = 1; // Biometric or mobile-approve challenge, when the method needs one. + // [2.2] For FIDO2 this holds the challenge the key has to sign. optional string challenge = 2; + // [2.2] For FIDO2 this holds the base64 id of the credential the key must + // sign with, i.e. the one registered for this user. + optional string credential_id = 3; } message ClientMfaStartRequest { @@ -331,6 +335,9 @@ message ClientMfaStartResponse { // [2.2] Per-step rejections, sparse - only failing steps appear. Non-empty // means the plan was refused and no session was created. repeated MfaStepRejection rejections = 3; + // [2.2] For FIDO2 on the legacy fused path: the base64 id of the credential + // the key must sign with, alongside the challenge above. + optional string credential_id = 4; } message ClientMfaFinishRequest { From 3de628bde52e1a38e0b7752013d58bd72eaeb4f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 26 Aug 2026 14:50:29 +0200 Subject: [PATCH 4/4] credential_ids --- common/client_types.proto | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/common/client_types.proto b/common/client_types.proto index 7779847..a1ae8c5 100644 --- a/common/client_types.proto +++ b/common/client_types.proto @@ -298,9 +298,11 @@ message ClientMfaStepStartResponse { // Biometric or mobile-approve challenge, when the method needs one. // [2.2] For FIDO2 this holds the challenge the key has to sign. optional string challenge = 2; - // [2.2] For FIDO2 this holds the base64 id of the credential the key must - // sign with, i.e. the one registered for this user. - optional string credential_id = 3; + // [2.2] For FIDO2: the credentials registered for this user, base64url as + // webauthn-rs serializes them. The client offers the whole list to the key, + // which answers for the one it holds, and names it in the finish request so + // later attempts can be narrowed to that credential. + repeated string credential_ids = 3; } message ClientMfaStartRequest { @@ -335,9 +337,9 @@ message ClientMfaStartResponse { // [2.2] Per-step rejections, sparse - only failing steps appear. Non-empty // means the plan was refused and no session was created. repeated MfaStepRejection rejections = 3; - // [2.2] For FIDO2 on the legacy fused path: the base64 id of the credential - // the key must sign with, alongside the challenge above. - optional string credential_id = 4; + // [2.2] For FIDO2 on the legacy fused path: the credentials registered for + // this user, alongside the challenge above. + repeated string credential_ids = 4; } message ClientMfaFinishRequest { @@ -353,6 +355,10 @@ message ClientMfaFinishRequest { optional string step_attempt_id = 4; // [2.2] FIDO2 optional bytes auth_data = 5; + // [2.2] For FIDO2: which credential actually signed, base64url. Picked by the + // key out of the list it was offered, so Core knows which of the user's + // security keys is in use. + optional string credential_id = 6; } message ClientMfaFinishResponse {