{"record":{"id":"b60d2475288d3fd7","repo":"rancher/rancher","slug":"empty-access-token-secret-for-user-s","errorCode":null,"errorMessage":"empty access token secret for user %s","messagePattern":"empty access token secret for user (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/providers/keycloakoidc/keycloak_provider.go","lineNumber":209,"sourceCode":"\tif err != nil {\n\t\t// If the secret lookup failed for a reason other than NotFound, surface the error.\n\t\tif !apierrors.IsNotFound(err) {\n\t\t\treturn nil, fmt.Errorf(\"getting access token for user: %w\", err)\n\t\t}\n\n\t\t// Secret not found: fall back to the access token stored in ProviderInfo.\n\t\taccessToken, ok := token.GetProviderInfo()[\"access_token\"]\n\t\tif !ok || strings.TrimSpace(accessToken) == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"no stored access token found for user %s\", token.GetUserID())\n\t\t}\n\t\toauthToken = &oauth2.Token{\n\t\t\tAccessToken: strings.TrimSpace(accessToken),\n\t\t}\n\t} else {\n\t\t// Secret retrieved successfully. First, try to interpret it as JSON-encoded oauth2.Token.\n\t\tstored := strings.TrimSpace(storedOauthToken)\n\t\tif stored == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"empty access token secret for user %s\", token.GetUserID())\n\t\t}\n\t\tif unmarshalErr := json.Unmarshal([]byte(stored), &oauthToken); unmarshalErr != nil || oauthToken == nil {\n\t\t\t// If unmarshalling fails or yields nil, fall back to treating the secret as a raw access token string.\n\t\t\toauthToken = &oauth2.Token{\n\t\t\t\tAccessToken: stored,\n\t\t\t}\n\t\t}\n\t}\n\n\t// Valid will return false if access token is expired\n\tif !oauthToken.Valid() {\n\t\t// since token is not valid, the TokenSource func used in the Client func will attempt to refresh the access token\n\t\t// if the refresh token has not expired\n\t\tlogrus.Debugf(\"[generic oidc] RefreshAndUpdateToken: attempting to refresh access token\")\n\t}\n\n\treusedToken, err := oauth2.ReuseTokenSource(oauthToken, oauthConfig.TokenSource(ctx, oauthToken)).Token()\n\tif err != nil {","sourceCodeStart":191,"sourceCodeEnd":227,"githubUrl":"https://github.com/rancher/rancher/blob/932558d4e68565aff2d2f36e89ec4a391b06e7c5/pkg/auth/providers/keycloakoidc/keycloak_provider.go#L191-L227","documentation":"Thrown when the stored token secret for the user was found, but its trimmed string content is empty. The secret exists yet contains no credential, so it cannot be unmarshalled or used as a raw access token. This indicates a corrupt or half-written cache entry rather than a missing one.","triggerScenarios":"TokenMgr.GetSecret succeeds but returns \"\" (or only whitespace) for the user/authProvider key: a secret created with an empty string value, an UpdateToken call that persisted an empty token, or a manually edited/blanked secret.","commonSituations":"A previous token-update failed midway and wrote an empty value; someone manually recreated the secret without data; secret restored from backup with redacted content.","solutions":["Delete the empty token secret so the code falls back to ProviderInfo['access_token'] or the user re-logs in","Audit the writer path (UpdateToken/TokenMgr.SetSecret) that produced the empty value and guard it against persisting empty tokens","Re-login the affected user to repopulate the cache"],"exampleFix":"// before\nif stored == \"\" {\n    return nil, fmt.Errorf(\"empty access token secret for user %s\", token.GetUserID())\n}\n// after: treat empty cache as a cache miss and fall through to the ProviderInfo fallback\nif stored == \"\" {\n    accessToken, ok := token.GetProviderInfo()[\"access_token\"]\n    if !ok || strings.TrimSpace(accessToken) == \"\" {\n        return nil, fmt.Errorf(\"no stored access token found for user %s\", token.GetUserID())\n    }\n    oauthToken = &oauth2.Token{AccessToken: strings.TrimSpace(accessToken)}\n}","handlingStrategy":"validation","validationCode":"// Before triggering refresh, check the cached secret is usable\nstored, err := tokenMgr.GetSecret(userID, providerName, nil)\nif err == nil && strings.TrimSpace(stored) == \"\" {\n    // Corrupt cache entry: drop it so the fallback path can run\n    _ = tokenMgr.DeleteSecret(userID, providerName)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never write a token secret without asserting the marshalled JSON is non-empty","Treat an empty cached secret as a cache miss, not a hard failure, in wrapper code","Alert on token secrets whose data length is zero"],"tags":["keycloak","oidc","oauth2","token-cache","kubernetes-secrets"],"backgroundTag":null,"analyzedSha":"932558d4e68565aff2d2f36e89ec4a391b06e7c5","analyzedAt":"2026-08-16T04:37:02.125Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}