{"record":{"id":"f777c2b2ab030364","repo":"goharbor/harbor","slug":"missing-oauth-token","errorCode":null,"errorMessage":"missing OAuth token","messagePattern":"missing OAuth token","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/pkg/p2p/preheat/provider/auth/token_handler.go","lineNumber":40,"sourceCode":"\n// TokenAuthHandler handles the OAuth auth mode.\ntype TokenAuthHandler struct {\n\t*BaseHandler\n}\n\n// Mode implements @Handler.Mode\nfunc (t *TokenAuthHandler) Mode() string {\n\treturn AuthModeOAuth\n}\n\n// Authorize implements @Handler.Authorize\nfunc (t *TokenAuthHandler) Authorize(req *http.Request, cred *Credential) error {\n\tif err := t.BaseHandler.Authorize(req, cred); err != nil {\n\t\treturn err\n\t}\n\n\tif _, ok := cred.Data[\"token\"]; !ok {\n\t\treturn errors.New(\"missing OAuth token\")\n\t}\n\n\tauthData := fmt.Sprintf(\"%s %s\", \"Bearer\", cred.Data[\"token\"])\n\treq.Header.Set(\"Authorization\", authData)\n\n\treturn nil\n}\n","sourceCodeStart":22,"sourceCodeEnd":48,"githubUrl":"https://github.com/goharbor/harbor/blob/7b2fd08cc568955cca339afeefab27372840d936/src/pkg/p2p/preheat/provider/auth/token_handler.go#L22-L48","documentation":"TokenAuthHandler.Authorize rejects a preheat request when the provider instance uses OAUTH auth mode but the credential data map has no \"token\" key — the Bearer header cannot be constructed. Unlike the BASIC/CUSTOM handlers, it looks up the specific key \"token\" rather than the first map entry, so any other key name yields this error.","triggerScenarios":"A preheat instance with auth_mode \"OAUTH\" whose auth_info lacks a \"token\" entry — e.g. {\"access_token\": \"...\"} or {\"Authorization\": \"...\"} or an empty map; any policy execution against the instance fails on its first outbound authorized request.","commonSituations":"Instance payloads authored with a different token key name; tokens rotated out and auth_info emptied during update; copy-paste from provider docs that use \"bearer\"/\"access_token\" field names; expired-token flows where automation rewrites auth_info minus the key.","solutions":["Re-save the instance with the token under exactly the \"token\" key: {\"auth_mode\": \"OAUTH\", \"auth_info\": {\"token\": \"<bearer-token>\"}}.","After rotating an expired token, PUT the instance again with the same key.","Re-run the policy execution once the instance credential is fixed."],"exampleFix":"# before\ncurl -X POST https://harbor/api/v2.0/p2p/preheat/instances -d '{\"name\": \"dfly\", \"provider\": \"dragonfly\", \"endpoint\": \"https://dfly.example.com\", \"auth_mode\": \"OAUTH\", \"auth_info\": {\"access_token\": \"eyJ...\"}}'\n# after\ncurl -X POST https://harbor/api/v2.0/p2p/preheat/instances -d '{\"name\": \"dfly\", \"provider\": \"dragonfly\", \"endpoint\": \"https://dfly.example.com\", \"auth_mode\": \"OAUTH\", \"auth_info\": {\"token\": \"eyJ...\"}}'","handlingStrategy":"validation","validationCode":"function validateOAuthAuthInfo(authInfo) {\n  if (!authInfo || typeof authInfo.token !== 'string' || authInfo.token.length === 0)\n    throw new Error(\"OAUTH auth requires a 'token' key\");\n}","typeGuard":"function isOAuthCredValid(cred) {\n  return cred != null && cred.Data != null && typeof cred.Data['token'] === 'string' && cred.Data['token'].length > 0;\n}","tryCatchPattern":"On 'missing OAuth token', PUT the instance with auth_info {\"token\": \"<bearer>\"} (exact key), then create a new policy execution; no point retrying the failed one.","preventionTips":["The OAUTH handler reads exactly the \"token\" key — not access_token/bearer/api_key.","Automate token rotation so auth_info is rewritten with the same key each cycle.","Validate instance payloads against a per-mode schema (NONE/BASIC/OAUTH/CUSTOM) before saving."],"tags":["p2p-preheat","oauth","bearer-token","credentials","harbor"],"backgroundTag":null,"analyzedSha":"7b2fd08cc568955cca339afeefab27372840d936","analyzedAt":"2026-08-16T00:00:10.961Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}