mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-13 17:09:07 +02:00
[client] Keep account email on Android logout, drop it on profile removal (#7200)
Align Android logout semantics with the desktop UI and CLI: logging out no longer deletes the stored account email, so the next login passes it as the OIDC login_hint and the IdP preselects the account. Removing the profile is now the operation that deletes the email; previously RemoveProfile left the account file behind, which the fixed-name default profile would have inherited on recreation.
This commit is contained in:
@@ -204,8 +204,9 @@ func (a *Auth) foregroundGetTokenInfo(authClient *auth.Auth, urlOpener URLOpener
|
|||||||
return nil, fmt.Errorf("failed to get OAuth flow: %v", err)
|
return nil, fmt.Errorf("failed to get OAuth flow: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// An empty hint is deliberate, not a fallback: a fresh or logged-out profile
|
// An empty hint is deliberate, not a fallback: a fresh profile leaves the
|
||||||
// leaves the choice to the IdP, which is how accounts get switched.
|
// choice to the IdP. Switching accounts is done by switching or removing
|
||||||
|
// profiles, not by logging out — logout keeps the email.
|
||||||
if a.cfgPath != "" {
|
if a.cfgPath != "" {
|
||||||
if hint := readProfileEmail(a.cfgPath); hint != "" {
|
if hint := readProfileEmail(a.cfgPath); hint != "" {
|
||||||
if setter, ok := oAuthFlow.(loginHintSetter); ok {
|
if setter, ok := oAuthFlow.(loginHintSetter); ok {
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ type Profile struct {
|
|||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
// Email is the account this profile last logged in with, "" if it never
|
// Email is the account this profile last logged in with, "" if it never
|
||||||
// completed an SSO login or was logged out. See profile_state.go.
|
// completed an SSO login. Kept across logouts; cleared when the profile is
|
||||||
|
// removed. See profile_state.go.
|
||||||
Email string
|
Email string
|
||||||
IsActive bool
|
IsActive bool
|
||||||
}
|
}
|
||||||
@@ -200,11 +201,9 @@ func (pm *ProfileManager) LogoutProfile(id string) error {
|
|||||||
return fmt.Errorf("failed to save config: %w", err)
|
return fmt.Errorf("failed to save config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not fatal: a stale hint costs an account switch, not the logout itself.
|
// The stored account email is kept on purpose, matching the desktop and CLI
|
||||||
if err := removeProfileEmail(configPath); err != nil {
|
// logout semantics: the next login passes it as the login_hint so the IdP
|
||||||
log.Warnf("failed to clear stored account email for profile %s: %v", id, err)
|
// preselects the account. Removing the profile is what deletes it.
|
||||||
}
|
|
||||||
|
|
||||||
log.Infof("logged out from profile: %s", id)
|
log.Infof("logged out from profile: %s", id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -224,11 +223,24 @@ func (pm *ProfileManager) RenameProfile(id string, newName string) error {
|
|||||||
|
|
||||||
// RemoveProfile deletes a profile
|
// RemoveProfile deletes a profile
|
||||||
func (pm *ProfileManager) RemoveProfile(id string) error {
|
func (pm *ProfileManager) RemoveProfile(id string) error {
|
||||||
|
configPath, err := pm.getProfileConfigPath(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// Use ServiceManager (removes profile from profiles/ directory)
|
// Use ServiceManager (removes profile from profiles/ directory)
|
||||||
if err := pm.serviceMgr.RemoveProfile(profilemanager.ID(id), androidUsername); err != nil {
|
if err := pm.serviceMgr.RemoveProfile(profilemanager.ID(id), androidUsername); err != nil {
|
||||||
return fmt.Errorf("failed to remove profile: %w", err)
|
return fmt.Errorf("failed to remove profile: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The account file is this package's, not the ServiceManager's, so it must
|
||||||
|
// go here. The default profile has a fixed filename, so a recreated one
|
||||||
|
// would otherwise inherit the deleted profile's email as its login_hint.
|
||||||
|
// Not fatal: the profile itself is gone.
|
||||||
|
if err := removeProfileEmail(configPath); err != nil {
|
||||||
|
log.Warnf("failed to remove stored account email for profile %s: %v", id, err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Infof("removed profile: %s", id)
|
log.Infof("removed profile: %s", id)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ func writeProfileEmail(configPath string, email string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeProfileEmail drops the stored account email. Called on logout: while the
|
// removeProfileEmail drops the stored account email. Called on profile removal,
|
||||||
// email is on disk it goes out as a login_hint, which would steer the next login
|
// not on logout: a logged-out profile keeps its email so the next login passes
|
||||||
// straight back into the account just logged out of. Mirrors the desktop UI's
|
// it as the login_hint, matching the desktop and CLI semantics. Mirrors the
|
||||||
// RemoveProfileState call.
|
// desktop UI's RemoveProfileState call.
|
||||||
func removeProfileEmail(configPath string) error {
|
func removeProfileEmail(configPath string) error {
|
||||||
accountPath, err := profileAccountPathFor(configPath)
|
accountPath, err := profileAccountPathFor(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -127,10 +127,10 @@ func TestWriteThenReadProfileEmail(t *testing.T) {
|
|||||||
t.Fatalf("remove: %v", err)
|
t.Fatalf("remove: %v", err)
|
||||||
}
|
}
|
||||||
if got := readProfileEmail(configPath); got != "" {
|
if got := readProfileEmail(configPath); got != "" {
|
||||||
t.Errorf("expected no email after logout, got %q", got)
|
t.Errorf("expected no email after removal, got %q", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logout may run on a never-logged-in profile, so a second remove must pass.
|
// Removal may run on a never-logged-in profile, so a second remove must pass.
|
||||||
if err := removeProfileEmail(configPath); err != nil {
|
if err := removeProfileEmail(configPath); err != nil {
|
||||||
t.Fatalf("second remove should be a no-op: %v", err)
|
t.Fatalf("second remove should be a no-op: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user