{"record":{"id":"11449bccad199432","repo":"netbirdio/netbird","slug":"interactive-sso-login-failed-v","errorCode":null,"errorMessage":"interactive sso login failed: %v","messagePattern":"interactive sso login failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/android/login.go","lineNumber":170,"sourceCode":"func (a *Auth) login(urlOpener URLOpener, isAndroidTV bool) error {\n\tauthClient, err := auth.NewAuth(a.ctx, a.config.PrivateKey, a.config.ManagementURL, a.config)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create auth client: %v\", err)\n\t}\n\tdefer authClient.Close()\n\n\t// check if we need to generate JWT token\n\tneedsLogin, err := authClient.IsLoginRequired(a.ctx)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check login requirement: %v\", err)\n\t}\n\n\tjwtToken := \"\"\n\temail := \"\"\n\tif needsLogin {\n\t\ttokenInfo, err := a.foregroundGetTokenInfo(authClient, urlOpener, isAndroidTV)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"interactive sso login failed: %v\", err)\n\t\t}\n\t\tjwtToken = tokenInfo.GetTokenToUse()\n\t\temail = tokenInfo.Email\n\t}\n\n\terr, _ = authClient.Login(a.ctx, \"\", jwtToken)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"login failed: %v\", err)\n\t}\n\n\t// Stored after Login, not before: a rejected token must not leave a hint\n\t// pointing at an account that cannot be used.\n\tif email != \"\" && a.cfgPath != \"\" {\n\t\tif err := writeProfileEmail(a.cfgPath, email); err != nil {\n\t\t\tlog.Warnf(\"failed to store profile account email: %v\", err)\n\t\t}\n\t}\n","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/netbirdio/netbird/blob/93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c/client/android/login.go#L152-L188","documentation":"In Android interactive login, when IsLoginRequired returns true, foregroundGetTokenInfo runs the SSO flow: it obtains a session/code request from management, opens the browser via the injected URLOpener, and exchanges the result for a user JWT. Any failure in that chain (user cancels, opener error, exchange timeout, IdP unavailable, Android TV flow issues) is wrapped here with %v.","triggerScenarios":"User closing the browser or canceling before completing SSO; URLOpener implementation failing (especially on Android TV); token exchange request expiring; IdP outage or misconfigured redirect.","commonSituations":"Deep-link/custom-scheme redirect not registered for the app; TV devices where no browser is available; clock skew or expired flow state in the token exchange.","solutions":["Retry the flow: many failures are user cancellation or transient IdP issues.","Verify the URLOpener works on the target device (register the redirect scheme/intent filter on Android).","Check management and IdP configuration (redirect URI, client ID) if the failure is repeatable at the same step."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"func isInteractiveSSOFailure(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"interactive sso login failed:\")\n}","tryCatchPattern":"err := a.login(urlOpener, isAndroidTV)\nif err != nil && isInteractiveSSOFailure(err) {\n    // covers user cancellation, opener failure, token exchange timeout:\n    // offer a retry; if it always fails at the same step, check redirect/\n    // intent-filter registration and IdP configuration\n}","preventionTips":["Register the SSO redirect scheme (intent filter/deep link) in the app manifest.","Test the URLOpener on every form factor, especially Android TV where browsers are scarce.","Complete the browser flow promptly; long delays can expire the token exchange request."],"tags":["android","auth","sso","oauth","browser","login"],"backgroundTag":null,"analyzedSha":"93e97f4bf1ad715072dcb3fb6cdb1763431b5a9c","analyzedAt":"2026-08-16T03:09:19.136Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}