fix: proxy passkey bind through Management API
All checks were successful
CI / build (push) Successful in 2m17s
CI / docker (push) Successful in 1m29s

Logto's /api/my-account/ endpoints reject the opaque access token with
401 even though /api/verifications/ accepts it. The bind step now goes
through the SaaS backend which calls the Management API instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-27 18:23:41 +02:00
parent 2aa5100530
commit 4df6fc9e03
4 changed files with 35 additions and 15 deletions

View File

@@ -103,6 +103,17 @@ public class AccountController {
return ResponseEntity.noContent().build();
}
// --- WebAuthn bind (proxied via Management API since Account API /my-account/ returns 401) ---
record WebAuthnBindRequest(String verificationRecordId, String identityVerificationId) {}
@PostMapping("/mfa/webauthn/bind")
public ResponseEntity<Void> bindWebAuthn(@AuthenticationPrincipal Jwt jwt,
@RequestBody WebAuthnBindRequest request) {
accountService.bindWebAuthnPasskey(jwt.getSubject(), request.verificationRecordId(), request.identityVerificationId());
return ResponseEntity.noContent().build();
}
// --- MFA Preference ---
@PostMapping("/mfa/method-preference")

View File

@@ -150,6 +150,10 @@ public class AccountService {
}
}
public void bindWebAuthnPasskey(String userId, String verificationRecordId, String identityVerificationId) {
logtoClient.bindWebAuthnMfa(userId, verificationRecordId, identityVerificationId);
}
// --- Passkeys ---
public List<PasskeyCredential> listPasskeys(String userId) {

View File

@@ -653,6 +653,18 @@ public class LogtoManagementClient {
}
}
/** Bind a WebAuthn passkey to a user via the Management API. */
public void bindWebAuthnMfa(String userId, String verificationRecordId, String identityVerificationId) {
if (!isAvailable()) throw new IllegalStateException("Logto not configured");
restClient.post()
.uri(config.getLogtoEndpoint() + "/api/users/" + userId + "/mfa-verifications")
.header("Authorization", "Bearer " + getAccessToken())
.contentType(MediaType.APPLICATION_JSON)
.body(Map.of("type", "WebAuthn", "verificationRecordId", verificationRecordId))
.retrieve()
.toBodilessEntity();
}
/** Enable the Account Center API for MFA management (WebAuthn passkey registration). */
public void enableAccountCenter() {
if (!isAvailable()) return;