{"record":{"id":"7cf45f49662c3534","repo":"tailscale/tailscale","slug":"auth-required","errorCode":null,"errorMessage":"auth required","messagePattern":"auth required","errorType":"http","errorClass":null,"httpStatus":401,"severity":"error","filePath":"ipn/localapi/localapi.go","lineNumber":263,"sourceCode":"\tif h.b == nil {\n\t\thttp.Error(w, \"server has no local backend\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tif r.Referer() != \"\" || r.Header.Get(\"Origin\") != \"\" || !h.validHost(r.Host) {\n\t\tmetricInvalidRequests.Add(1)\n\t\thttp.Error(w, \"invalid localapi request\", http.StatusForbidden)\n\t\treturn\n\t}\n\tw.Header().Set(\"Tailscale-Version\", version.Long())\n\tw.Header().Set(\"Tailscale-Cap\", strconv.Itoa(int(tailcfg.CurrentCapabilityVersion)))\n\tw.Header().Set(\"Content-Security-Policy\", `default-src 'none'; frame-ancestors 'none'; script-src 'none'; script-src-elem 'none'; script-src-attr 'none'`)\n\tw.Header().Set(\"X-Frame-Options\", \"DENY\")\n\tw.Header().Set(\"X-Content-Type-Options\", \"nosniff\")\n\tif h.RequiredPassword != \"\" {\n\t\t_, pass, ok := r.BasicAuth()\n\t\tif !ok {\n\t\t\tmetricInvalidRequests.Add(1)\n\t\t\thttp.Error(w, \"auth required\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\t\tif subtle.ConstantTimeCompare([]byte(pass), []byte(h.RequiredPassword)) == 0 {\n\t\t\tmetricInvalidRequests.Add(1)\n\t\t\thttp.Error(w, \"bad password\", http.StatusForbidden)\n\t\t\treturn\n\t\t}\n\t}\n\tdefer h.b.CheckDeadlocks()()\n\tif fn, route, ok := handlerForPath(r.URL.Path); ok {\n\t\th.logRequest(r.Method, route)\n\t\tfn(h, w, r)\n\t} else {\n\t\thttp.NotFound(w, r)\n\t}\n}\n\n// validLocalHostForTesting allows loopback handlers without RequiredPassword for testing.","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/tailscale/tailscale/blob/6e0912f97994f927632b34ae9e63b53d6516a6ac/ipn/localapi/localapi.go#L245-L281","documentation":"Returned (HTTP 401 Unauthorized) when the localapi Handler has RequiredPassword set but the request carries no HTTP basic-auth credentials. RequiredPassword is used by the sandboxed macOS GUI auth mechanism (sameuserproof) and test harnesses; in that mode every request must include basic auth. The WWW-Authenticate context is that BasicAuth() parsing found no Authorization header or a non-basic scheme.","triggerScenarios":"Sending any request to the localapi socket without an Authorization: Basic ... header while RequiredPassword is non-empty — typical on macOS where the GUI connects through the sameuserproof socket.","commonSituations":"Custom scripts hitting the macOS tailscaled socket without the password; running localapi in test mode with a password and forgetting credentials in one client.","solutions":["Send basic auth with the expected password: req.SetBasicAuth(\"user\", password) (username is ignored, only the password is compared).","On macOS, use the official client libraries/CLI which handle the auth handshake.","If you control the server side and don't need it, leave RequiredPassword empty for socket-based auth."],"exampleFix":"// before\nreq, _ := http.NewRequest(\"GET\", \"http://local-tailscaled.sock/localapi/v0/status\", nil)\n// after\nreq, _ := http.NewRequest(\"GET\", \"http://local-tailscaled.sock/localapi/v0/status\", nil)\nreq.SetBasicAuth(\"tailscale\", localAPIPassword)","handlingStrategy":"validation","validationCode":"if serverRequiresPassword { // macOS sameuserproof mode\n\treq.SetBasicAuth(\"tailscale\", password) // only the password is checked\n}","typeGuard":null,"tryCatchPattern":"if resp.StatusCode == http.StatusUnauthorized {\n\treturn errors.New(\"localapi password required: add basic auth\")\n}","preventionTips":["Obtain the password through the platform helper that issued it; never hard-code.","One shared HTTP client with auth set covers all endpoints."],"tags":["localapi","authentication","unauthorized","basic-auth","tailscale"],"backgroundTag":"authentication-required","analyzedSha":"6e0912f97994f927632b34ae9e63b53d6516a6ac","analyzedAt":"2026-08-18T08:17:25.280Z","contentChangedAt":"2026-08-18T08:17:25.280Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}