{"record":{"id":"db7ef819bb58888d","repo":"gravitational/teleport","slug":"touch-id-not-available","errorCode":null,"errorMessage":"touch ID not available","messagePattern":"touch ID not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"lib/auth/touchid/api.go","lineNumber":52,"sourceCode":"\t\"sort\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/fxamacker/cbor/v2\"\n\t\"github.com/go-webauthn/webauthn/protocol\"\n\t\"github.com/go-webauthn/webauthn/protocol/webauthncose\"\n\t\"github.com/gravitational/trace\"\n\n\t\"github.com/gravitational/teleport\"\n\twantypes \"github.com/gravitational/teleport/lib/auth/webauthntypes\"\n\t\"github.com/gravitational/teleport/lib/darwin\"\n\tlogutils \"github.com/gravitational/teleport/lib/utils/log\"\n)\n\nvar (\n\tErrCredentialNotFound = errors.New(\"credential not found\")\n\tErrNotAvailable       = errors.New(\"touch ID not available\")\n\n\t// PromptPlatformMessage is the message shown before Touch ID prompts.\n\tPromptPlatformMessage = \"Using platform authenticator, follow the OS prompt\"\n\t// PromptWriter is the writer used for prompt messages.\n\tPromptWriter io.Writer = os.Stderr\n\n\tlogger = logutils.NewPackageLogger(teleport.ComponentKey, \"TouchID\")\n)\n\nfunc promptPlatform() {\n\tif PromptPlatformMessage != \"\" {\n\t\tfmt.Fprintln(PromptWriter, PromptPlatformMessage)\n\t}\n}\n\n// AuthContext is an optional, shared authentication context.\n// Allows reusing a single authentication prompt/gesture between different\n// functions, provided the functions are invoked in a short time interval.","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/gravitational/teleport/blob/1283425b60ec5f60d509ba4c791183d452923ff7/lib/auth/touchid/api.go#L34-L70","documentation":"ErrNotAvailable is the sentinel error of the touchid package, indicating the platform authenticator (Touch ID) is not usable on the current machine. Every public entry point (Register, Login, ListCredentials, DeleteCredential, Guard, Authenticate) checks IsAvailable() first and returns this error when the check fails. It lets callers degrade gracefully to other MFA methods instead of failing unpredictably deeper in the stack.","triggerScenarios":"Calling any touchid API (Register at api.go:231, Login at api.go:451, ListCredentials, DeleteCredential, Guard, Authenticate) when IsAvailable() returns false: no Touch ID hardware, unsupported macOS version, or the underlying darwin/CTK API reports the authenticator unavailable.","commonSituations":"Running Teleport client on Linux/Windows where lib/auth/touchid is compiled but no Touch ID exists; macOS without Touch ID (desktop Macs, VMs); biometrics not enrolled or disabled by MDM/policy; Secure Enclave unavailable.","solutions":["Check touchid.IsAvailable() before calling any touchid API and fall back to another authenticator (e.g. webauthncli/FIDO2) when false.","Enroll fingerprints in System Settings > Touch ID & Password on macOS.","Verify the app/binary is running on real macOS hardware with a Secure Enclave, not a VM or container.","If MDM blocks biometrics, ask IT to allow Touch ID or use an alternate MFA device."],"exampleFix":"// before\nreg, err := touchid.Register(origin, cc)\n// after\nif !touchid.IsAvailable() {\n\treturn mfaFallbackRegister(origin, cc) // e.g. FIDO2 flow\n}\nreg, err := touchid.Register(origin, cc)","handlingStrategy":"fallback","validationCode":"if !touchid.IsAvailable() {\n\t// use FIDO2 or another MFA method instead\n}","typeGuard":"func available() bool { return touchid.IsAvailable() }","tryCatchPattern":"reg, err := touchid.Register(origin, cc)\nif errors.Is(err, touchid.ErrNotAvailable) {\n\treg, err = fidoFallbackRegister(origin, cc)\n}","preventionTips":["Always gate touchid API calls behind touchid.IsAvailable().","Design MFA UX with a non-platform fallback (FIDO2/OTP).","Test on machines without Touch ID (desktops, VMs) to exercise the fallback path."],"tags":["touchid","macos","webauthn","availability"],"backgroundTag":"platform-authenticator-unavailable","analyzedSha":"1283425b60ec5f60d509ba4c791183d452923ff7","analyzedAt":"2026-09-02T04:06:41.601Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}