{"record":{"id":"f89a9b38befdd05f","repo":"gravitational/teleport","slug":"cannot-fulfill-authenticator-attachment-q","errorCode":null,"errorMessage":"cannot fulfill authenticator attachment %q","messagePattern":"cannot fulfill authenticator attachment %q","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/auth/touchid/api.go","lineNumber":251,"sourceCode":"\n\tif origin == \"\" {\n\t\treturn nil, trace.BadParameter(\"origin required\")\n\t}\n\tif err := cc.Validate(); err != nil {\n\t\treturn nil, trace.Wrap(err)\n\t}\n\n\t// Ignored cc fields:\n\t// - Timeout - we don't control touch ID timeouts (also the server is free to\n\t//   enforce it)\n\t// - CredentialExcludeList - we always allow re-registering (for various\n\t//   reasons).\n\t// - Extensions - none supported\n\t// - Attestation - we always to our best (packed/self-attestation).\n\t//   The server is free to ignore/reject.\n\n\tif cc.Response.AuthenticatorSelection.AuthenticatorAttachment == protocol.CrossPlatform {\n\t\treturn nil, fmt.Errorf(\"cannot fulfill authenticator attachment %q\", cc.Response.AuthenticatorSelection.AuthenticatorAttachment)\n\t}\n\tok := false\n\tfor _, param := range cc.Response.Parameters {\n\t\t// ES256 is all we can do.\n\t\tif param.Type == protocol.PublicKeyCredentialType && param.Algorithm == webauthncose.AlgES256 {\n\t\t\tok = true\n\t\t\tbreak\n\t\t}\n\t}\n\tif !ok {\n\t\treturn nil, errors.New(\"cannot fulfill credential parameters, only ES256 are supported\")\n\t}\n\n\trpID := cc.Response.RelyingParty.ID\n\tuser := cc.Response.User.Name\n\tuserHandle := cc.Response.User.ID\n\n\t// TODO(codingllama): Handle double registrations and failures after key","sourceCodeStart":233,"sourceCodeEnd":269,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/auth/touchid/api.go#L233-L269","documentation":"The Touch ID authenticator implementation in Teleport only supports the platform attachment (Touch ID / Secure Enclave). When a WebAuthn credential creation request (PublicKeyCredentialCreationOptions) specifies AuthenticatorAttachment of CrossPlatform (e.g. roaming security keys), it cannot be fulfilled and Register fails with this error. It is an early sanity check before building the platform authenticator request.","triggerScenarios":"Calling touchid Register (directly or via promptTouchIDRegisterChallenge) with a creation options object whose AuthenticatorSelection.AuthenticatorAttachment is set to protocol.CrossPlatform.","commonSituations":"Server-side WebAuthn configuration that requests cross-platform (USB/NFC) authenticators while the client is the macOS Touch ID path; test code (TestRegister_rollback, TestLogin_*) passing crafted options; shared WebAuthn settings not differentiated per platform.","solutions":["Set AuthenticatorSelection.AuthenticatorAttachment to protocol.Platform (or leave it unset/auto) when the request may be served by Touch ID.","Configure the Teleport server's WebAuthn settings so attachment is not pinned to cross-platform for macOS clients.","If cross-platform keys are genuinely required, use a different client path (e.g. webauthncli/FIDO2) instead of the Touch ID API.","Update tests to use Platform attachment when exercising the Touch ID register flow."],"exampleFix":"// before\ncc.Response.AuthenticatorSelection.AuthenticatorAttachment = protocol.CrossPlatform\ncred, err := api.Register(cc)\n// after\ncc.Response.AuthenticatorSelection.AuthenticatorAttachment = protocol.Platform\ncred, err := api.Register(cc)","handlingStrategy":"validation","validationCode":"if cc != nil && cc.Response.AuthenticatorSelection.AuthenticatorAttachment == protocol.CrossPlatform {\n    return errors.New(\"touchid API cannot serve cross-platform authenticators; use platform attachment\")\n}","typeGuard":"func touchIDCanFulfill(cc *protocol.CredentialCreation) bool {\n    return cc == nil || cc.Response.AuthenticatorSelection.AuthenticatorAttachment != protocol.CrossPlatform\n}","tryCatchPattern":"cred, err := api.Register(cc)\nif err != nil {\n    if strings.Contains(err.Error(), \"cannot fulfill authenticator attachment\") {\n        // fall back to the FIDO2/roaming-key path\n        return fallbackFIDO2Register(cc)\n    }\n    return trace.Wrap(err)\n}","preventionTips":["Leave AuthenticatorAttachment unset (auto) unless you know which client path will serve the request.","Gate cross-platform attachment requirements to non-macOS or FIDO2 client paths.","Add a pre-flight check on the client before invoking the Touch ID register API."],"tags":["webauthn","touchid","macos"],"backgroundTag":"authenticator-attachment-unsupported","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}