{"record":{"id":"d109de480d18c19e","repo":"netbirdio/netbird","slug":"token-received-is-empty","errorCode":null,"errorMessage":"token received is empty","messagePattern":"token received is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/auth/util.go","lineNumber":34,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not generate %d random bytes: %v\", count, err)\n\t}\n\n\treturn hex.EncodeToString(buf), nil\n}\n\n// validateTokenAudience checks that the token is a well-formed JWT whose\n// audience claim matches the expected audience.\n//\n// It does NOT verify the token's cryptographic signature and therefore must not\n// be treated as an authenticity check. The token is obtained by the client\n// directly from the IdP token endpoint over TLS, and its signature is verified\n// server-side by the management server against the IdP's JWKS\n// (see shared/auth/jwt/validator.go). This function is only a client-side\n// sanity check that the returned token targets the expected audience.\nfunc validateTokenAudience(token string, audience string) error {\n\tif token == \"\" {\n\t\treturn fmt.Errorf(\"token received is empty\")\n\t}\n\n\tparts := strings.Split(token, \".\")\n\tif len(parts) != 3 {\n\t\treturn fmt.Errorf(\"token is not a well-formed JWT\")\n\t}\n\n\tclaimsString, err := base64.RawURLEncoding.DecodeString(parts[1])\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tclaims := Claims{}\n\terr = json.Unmarshal(claimsString, &claims)\n\tif err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/auth/util.go#L16-L52","documentation":"validateTokenAudience was handed an empty token string: the flow had nothing to check. The token to use is the id_token when UseIDToken is set, otherwise the access token, so the emptiness points at which piece the IdP failed to return.","triggerScenarios":"The token endpoint returned a response whose access token is empty, or UseIDToken is enabled and token.Extra(\"id_token\") was absent so IDToken stayed empty while GetTokenToUse() returned the id_token path.","commonSituations":"IdP application registered without the openid scope so no id_token is minted while UseIDToken is on; a misconfigured token endpoint returning an error body that parses as an empty token; provider returning tokens only in a nonstandard field.","solutions":["Check whether 'Use ID Token' is enabled in the IdP configuration while the flow's scopes lack openid - either add the openid scope or disable UseIDToken.","Capture (at debug level) what the token endpoint returned; an empty token in a 200 response is an IdP application configuration problem.","Re-run the login after fixing scopes in the NetBird IdP configuration.","If the provider cannot issue JWT access tokens at all, switch the configuration to use the id_token."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before starting the flow, confirm the configuration can yield a usable token\nfunc tokenSourcesConfigured(cfg PKCEAuthProviderConfig) error {\n    if cfg.UseIDToken && !strings.Contains(cfg.Scope, \"openid\") {\n        return fmt.Errorf(\"UseIDToken requires the openid scope\")\n    }\n    return nil\n}","typeGuard":"func hasToken(t string) bool { return t != \"\" }","tryCatchPattern":"tokenInfo, err := flow.WaitToken(ctx, info)\nif err != nil {\n    if strings.Contains(err.Error(), \"token received is empty\") {\n        // the IdP returned no usable token: check UseIDToken + openid scope,\n        // or the token endpoint response, before retrying\n    }\n}","preventionTips":["Include the openid scope whenever UseIDToken is enabled.","Validate that the IdP token endpoint returns tokens in the standard OAuth2 JSON fields.","Alert on empty-token responses: a 200 with an empty access_token is an IdP misconfiguration."],"tags":["oauth2","jwt","idp","validation"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}