{"record":{"id":"186cf8a3e7c54af6","repo":"netbirdio/netbird","slug":"authentication-failed-invalid-access-token-w","errorCode":null,"errorMessage":"authentication failed: invalid access token - %w","messagePattern":"authentication failed: invalid access token - %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/internal/auth/pkce_flow.go","lineNumber":310,"sourceCode":"\ttokenInfo := TokenInfo{\n\t\tAccessToken:  token.AccessToken,\n\t\tRefreshToken: token.RefreshToken,\n\t\tTokenType:    token.TokenType,\n\t\tExpiresIn:    token.Expiry.Second(),\n\t\tUseIDToken:   p.providerConfig.UseIDToken,\n\t}\n\tif idToken, ok := token.Extra(\"id_token\").(string); ok {\n\t\ttokenInfo.IDToken = idToken\n\t}\n\n\t// if a provider doesn't support an audience, use the Client ID for token verification\n\taudience := p.providerConfig.Audience\n\tif audience == \"\" {\n\t\taudience = p.providerConfig.ClientID\n\t}\n\n\tif err := validateTokenAudience(tokenInfo.GetTokenToUse(), audience); err != nil {\n\t\treturn TokenInfo{}, fmt.Errorf(\"authentication failed: invalid access token - %w\", err)\n\t}\n\n\temail, err := parseEmailFromIDToken(tokenInfo.IDToken)\n\tif err != nil {\n\t\tlog.Warnf(\"failed to parse email from ID token: %v\", err)\n\t} else {\n\t\ttokenInfo.Email = email\n\t}\n\n\treturn tokenInfo, nil\n}\n\n// parseEmailFromIDToken extracts the email (or name) claim from an ID token\n// without verifying its signature. The value is best-effort and used only as a\n// UX convenience (login hint prefill and display); it never drives an\n// authorization decision. The authoritative identity is established server-side\n// from the signature-verified token.\nfunc parseEmailFromIDToken(token string) (string, error) {","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/internal/auth/pkce_flow.go#L292-L328","documentation":"After a successful token exchange, the client-side audience sanity check (validateTokenAudience) rejected the token that would be used for management login. The wrapped error says which check failed: token empty, not a three-part JWT, missing aud claim, or an aud matching neither the configured Audience nor - when Audience is empty - the ClientID fallback. This is a configuration sanity check, not a signature verification (that happens server-side against the IdP JWKS).","triggerScenarios":"parseOAuthToken calls validateTokenAudience(tokenInfo.GetTokenToUse(), audience): the IdP issued the token for a different API identifier than configured; the access token is opaque rather than a JWT; UseIDToken is set but no id_token came back so the checked string is empty; Audience is unset so ClientID is compared but the token's aud is a resource URI.","commonSituations":"Auth0/Azure AD API identifier (aud) differs from the Audience entered in NetBird's IdP settings; provider does not support the audience parameter so the ClientID fallback is used while the token still targets a resource scope; some IdP configurations issue opaque access tokens unless the app registration is adjusted; UseIDToken enabled on a flow where the scopes omit openid so no id_token is returned.","solutions":["Decode the failing JWT (base64url-decode the middle dot-separated part) and compare its aud claim with the Audience configured in NetBird's IdP settings; make them match.","If the provider has no audience concept, ensure the token's aud equals the ClientID - that is the fallback checked when Audience is empty.","If the access token is opaque (not a JWT), enable 'Use ID Token' in the IdP configuration so the always-JWT id_token is validated instead.","Re-login after the administrator fixes the IdP configuration; the check runs on every fresh token retrieval."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// decode the aud claim of a sample token and compare with what NetBird will check\nfunc tokenAudienceMatches(token, expected string) bool {\n    parts := strings.Split(token, \".\")\n    if len(parts) != 3 {\n        return false\n    }\n    payload, err := base64.RawURLEncoding.DecodeString(parts[1])\n    if err != nil {\n        return false\n    }\n    var claims struct {\n        Audience json.RawMessage `json:\"aud\"`\n    }\n    if json.Unmarshal(payload, &claims) != nil || len(claims.Audience) == 0 {\n        return false\n    }\n    var auds []string\n    if json.Unmarshal(claims.Audience, &auds) != nil {\n        var single string\n        if json.Unmarshal(claims.Audience, &single) == nil {\n            auds = []string{single}\n        }\n    }\n    for _, a := range auds {\n        if a == expected {\n            return true\n        }\n    }\n    return false\n}","typeGuard":"func isJWT(s string) bool { return len(strings.Split(s, \".\")) == 3 }","tryCatchPattern":"tokenInfo, err := flow.WaitToken(ctx, info)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid access token\") {\n        // configuration mismatch between IdP audience and NetBird settings;\n        // fix the Audience (or enable Use ID Token) before retrying\n    }\n}","preventionTips":["Set NetBird's Audience to the IdP's exact API identifier before the first login.","If the provider cannot issue JWT access tokens, enable Use ID Token in the IdP configuration.","When Audience is left empty, confirm the token's aud equals the ClientID - that is what gets checked.","After any IdP app registration change, decode a sample token and verify aud before rolling out."],"tags":["oauth2","jwt","audience","idp","configuration"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}