Skip to content

brokers: use providerID for user identification - #1546

Merged
denisonbarbosa merged 11 commits into
mainfrom
use-sub-for-identification
Jun 30, 2026
Merged

brokers: use providerID for user identification#1546
denisonbarbosa merged 11 commits into
mainfrom
use-sub-for-identification

Conversation

@denisonbarbosa

@denisonbarbosa denisonbarbosa commented May 28, 2026

Copy link
Copy Markdown
Member

We used to rely on emails or preferred_username to identify remote users. These are not guaranteed to be stable and can create difficulties when trying to identify users who have changed their email/username. In order to better identify the users and properly match them to their respective local accounts, we need to switch to using the sub (oid for EntraID) claim, which is a unique ID assigned by the provider to its users, to identify the remote users. These will be generically named providerID in the code, since we use different values for the brokers.

This also helps us to implement new features, such as allowing login with shortened usernames.

UDENG-9399

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates remote user identification from unstable attributes (email / preferred_username) to a stable provider-assigned identifier (sub, or oid for MS Entra ID), enabling reliable matching across username/email changes and supporting upcoming username-shortening features.

Changes:

  • Persist sub in the authd users DB (schema v3) with a partial unique index, and expose lookup by sub.
  • Update authd user update logic to resolve existing users by sub and handle sub-matched renames (including local group membership cleanup).
  • Bump broker API to v3, passing sub in NewSession / DeleteUser, and migrate OIDC-broker cache directories from username-keyed to sub-keyed storage.

Reviewed changes

Copilot reviewed 159 out of 203 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
internal/users/types/types.go Add Sub field to broker-returned UserInfo for stable identity matching.
internal/users/manager.go Use sub for user lookup/rename handling; persist sub into DB; add SubForUser.
internal/users/defs.go Populate types.UserInfo.Sub from DB rows.
internal/users/db/users.go Add sub column support to queries/inserts/updates; add UserBySub.
internal/users/db/update.go Allow UID/name change when sub matches; include sub in user scans.
internal/users/db/sql/create_schema.sql Extend schema with sub column and partial unique index.
internal/users/db/migration.go Add migration to introduce sub column + partial unique index (schema v3).
internal/testutils/broker.go Update broker mock API to v3 (sub parameter) and userinfo JSON to sub.
internal/services/user/user.go Pass stored sub to broker DeleteUser for sub-keyed cache cleanup.
internal/services/pam/pam.go Look up sub and pass it into broker session creation (API v3).
internal/brokers/manager.go Extend broker manager NewSession signature to include sub.
internal/brokers/manager_test.go Update tests for new NewSession(..., sub) signature.
internal/brokers/localbroker.go Update local broker interface implementation to accept sub.
internal/brokers/dbusbroker.go Implement v2/v3 dispatch for NewSession/DeleteUser based on interface version.
internal/brokers/dbusbroker_test.go Expect v3 interface selection and adjust version-related test cases.
internal/brokers/broker.go Thread sub through broker interface and document v3 behavior.
internal/brokers/broker_test.go Update tests for DeleteUser(..., sub) signature.
internal/brokers/testdata/golden/TestUserPreCheck/Successfully_pre-check_user Update golden userinfo JSON key from uuid to sub.
examplebroker/dbus.go Adapt example broker to new internal NewSession/DeleteUser signatures (passing empty sub).
examplebroker/broker.go Accept sub in example broker methods (unused) to satisfy new interface.
authd-oidc-brokers/internal/token/token_test.go Rename cached user identifier field from UUID to Sub in tests.
authd-oidc-brokers/internal/providers/msentraid/msentraid.go Use Entra oid as stable identifier; error if missing.
authd-oidc-brokers/internal/providers/info/info.go Rename provider user identifier field to Sub; update constructor.
authd-oidc-brokers/internal/providers/info/info_test.go Update tests for Sub field/parameter naming.
authd-oidc-brokers/internal/dbusservice/methods.go Add v3 DBus methods accepting sub (InterfaceV3 wrapper).
authd-oidc-brokers/internal/dbusservice/dbusservice.go Export com.ubuntu.authd.Broker3 interface and v3 introspection XML.
authd-oidc-brokers/internal/broker/broker.go Implement sub-keyed cache lookup/migration; accept sub in NewSession/DeleteUser.
authd-oidc-brokers/internal/broker/broker_test.go Update broker tests for new signatures and cache migration side-effects.
authd-oidc-brokers/internal/broker/helper_test.go Update helpers to call NewSession(..., sub) and cached user info field to Sub.
internal/users/testdata/golden/TestUpdateUser/User_private_group_GID_preserved_across_logins Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/UID_does_not_change_if_user_already_exists Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/Successfully_update_user_updating_local_groups_with_changes Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/Successfully_update_user_updating_local_groups Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/Successfully_update_user Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/Removing_last_user_from_a_group_keeps_the_group_record Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/GID_does_not_change_if_group_with_same_UGID_exists Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/GID_does_not_change_if_group_with_same_name_and_empty_UGID_exists Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateUser/Allow_login_with_existing_group_on_system Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUpdateBrokerForUser/Successfully_update_broker_for_user Bump golden DB schema version to v3.
internal/users/testdata/golden/TestUnlockUser/Successfully_enable_user Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Warns_creating_manager_with_partially_invalid_UID_ranges Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Warns_creating_manager_with_partially_invalid_GID_ranges Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Successfully_create_manager_with_UID_range_next_to_systemd_dynamic_users Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Successfully_create_manager_with_GID_range_next_to_systemd_dynamic_groups Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Successfully_create_manager_with_default_config Bump golden DB schema version to v3.
internal/users/testdata/golden/TestNewManager/Successfully_create_manager_with_custom_config Bump golden DB schema version to v3.
internal/users/testdata/golden/TestLockUser/Successfully_lock_user Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteUser/Successfully_delete_user_removes_them_from_local_groups Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteUser/Successfully_delete_user_keeps_primary_group_if_other_users_still_use_it Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteUser/Successfully_delete_user_keeps_other_users_in_shared_group Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteUser/Successfully_delete_user_and_remove_home Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteUser/Successfully_delete_user Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteGroup/Successfully_delete_shared_group_leaves_other_groups_intact Bump golden DB schema version to v3.
internal/users/testdata/golden/TestDeleteGroup/Successfully_delete_group_keeps_its_members_in_the_db Bump golden DB schema version to v3.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_valid_users/userwithoutbroker-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_valid_users/user3-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_valid_users/user2-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_valid_users/user1-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_not_matching_users/userwithoutbroker-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_not_matching_users/user3-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_not_matching_users/user2-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/testdata/golden/TestCompareNewUserInfoWithDB/Compare_all_not_matching_users/user1-from-getOldUserInfoFromDB Add sub: \"\" to golden userinfo output.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_does_not_change_shell_if_it_exists Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_does_not_change_homedir_if_it_exists Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_renaming_a_group Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_removing_optional_gecos_field_if_not_set Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_changing_attributes Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_adding_a_new_local_group Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_adding_a_new_group Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_user_by_adding_a_new_default_group Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Update_only_user_even_if_we_have_multiple_of_them Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Remove_user_from_a_group_still_part_from_another_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Remove_group_from_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Insert_new_user_without_optional_gecos_field Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Insert_new_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestUpdateUserEntry/Add_user_to_group_from_another_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestSetUserID/Set_user_id_for_existing_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestSetGroupID/Set_group_id_for_existing_group Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestNew/New_without_any_initialized_database Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestNew/New_with_already_existing_database Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesWithSymlinkedPreviousBackup/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesWithSymlinkedGroupFile/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesWithPreviousBackup/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesWithBackupFailure/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesEmptyDB/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNamesAlreadyUpdated/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationToLowercaseUserAndGroupNames/db Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestMigrationAddLockedColumnToUsersTable Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestDeleteUser/Deleting_last_user_from_a_group_keeps_the_group_record Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestDeleteUser/Deleting_existing_user_keeps_other_group_members_intact Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestDeleteGroup/Deleting_sole_group_of_a_user_removes_group_and_memberships_but_keeps_user Bump golden DB schema version to v3.
internal/users/db/testdata/golden/TestDeleteGroup/Deleting_shared_group_removes_only_that_group_and_its_memberships Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestUnlockUser/Successfully_unlock_user_with_uppercase Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestUnlockUser/Successfully_unlock_user Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestLockUser/Successfully_lock_user_with_uppercase Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestLockUser/Successfully_lock_user Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteUser/Warning_when_broker_not_found/database Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteUser/Warning_when_broker_fails_to_delete/database Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteUser/Successfully_delete_user/database Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteUser/Successfully_delete_user_with_uppercase/database Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteGroup/Successfully_delete_group_with_uppercase Bump golden DB schema version to v3.
internal/services/user/testdata/golden/TestDeleteGroup/Successfully_delete_group Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestSetDefaultBrokerForUser/Username_is_case_insensitive/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestSetDefaultBrokerForUser/Update_default_broker_for_existing_user_with_a_broker/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestSetDefaultBrokerForUser/Set_default_broker_for_existing_user_with_no_broker/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Update_local_groups/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Update_existing_DB_on_success/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Successfully_authenticate/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Successfully_authenticate_with_groups_with_uppercase/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_uppercase/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Successfully_authenticate_if_first_call_is_canceled/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Successfully_authenticate_after_calling_second_time_without_cancelling/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_user_is_locked/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_there_is_no_broker/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_sessionID_is_empty/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_not_root/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_broker_returns_invalid_userinfo/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_broker_returns_invalid_data/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_broker_returns_invalid_access/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_when_authenticating/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_on_updating_local_groups_with_unexisting_file/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Error_on_empty_data_even_if_granted/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIsAuthenticated/Denies_authentication_when_broker_times_out/cache.db Bump golden DB schema version to v3.
internal/services/pam/testdata/golden/TestIDGeneration/Generate_ID/cache.db Bump golden DB schema version to v3.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Successfully_allow_username_with_matching_allowed_suffix Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Successfully_allow_username_that_matches_at_least_one_allowed_suffix Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Successfully_allow_username_ignoring_empty_string_in_config Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Successfully_allow_username_if_suffix_is_allow_all Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Successfully_allow_username_if_suffix_has_asterisk Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestUserPreCheck/Return_userinfo_with_correct_homedir_after_precheck Update golden userinfo JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_password/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_password/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_password/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_with_device_auth_when_provider_uses_thin_id_token/second_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_with_device_auth_when_provider_uses_thin_id_token/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_with_device_auth_when_provider_uses_thin_id_token/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_device_auth_and_newpassword/second_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_device_auth_and_newpassword/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Successfully_authenticate_user_with_device_auth_and_newpassword/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured_but_user_does_not_become_owner/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured_but_user_does_not_become_owner/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Owner_extra_groups_configured_but_user_does_not_become_owner/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Extra_groups_configured/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Extra_groups_configured/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Extra_groups_configured/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Extra_and_owner_extra_groups_configured_with_existing_extra_group_in_cached_user_info/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Extra_and_owner_extra_groups_configured_but_already_in_cached_user_info/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_qrcode_reacquires_token/second_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_qrcode_reacquires_token/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_qrcode_reacquires_token/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_supports_device_registration/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_supports_device_registration/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_supports_device_registration/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_authentication_is_forced/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_authentication_is_forced/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_when_provider_authentication_is_forced/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_still_allowed_if_token_is_expired_and_server_is_unreachable/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_still_allowed_if_server_is_unreachable/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_still_allowed_if_no_refresh_token_and_server_is_unreachable/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_skips_token_refresh_network_error/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_groups/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_groups/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_groups/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_expired_token/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_expired_token/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_refreshes_expired_token/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_keeps_old_groups_if_session_is_offline/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_keeps_old_groups_if_fetching_groups_fails/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_keeps_old_groups_if_fetching_groups_fails/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_password_keeps_old_groups_if_fetching_groups_fails/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_device_auth_when_provider_supports_device_registration/second_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_device_auth_when_provider_supports_device_registration/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_with_device_auth_when_provider_supports_device_registration/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_when_the_auth_data_secret_field_uses_the_old_name/first_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_when_the_auth_data_secret_field_uses_the_old_name/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_when_the_auth_data_secret_field_uses_the_old_name/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_still_allowed_if_token_is_missing_scopes/second_call Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_still_allowed_if_token_is_missing_scopes/data/provider_url/test-user-id/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestIsAuthenticated/Authenticating_still_allowed_if_token_is_missing_scopes/data/provider_url/test-user-id/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/first_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/second_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/data/provider_url/user1/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/data/provider_url/user1/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/data/provider_url/user2/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_then_second_starts_and_first_finishes/data/provider_url/user2/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/first_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/second_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/data/provider_url/user1/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/data/provider_url/user1/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/data/provider_url/user2/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first/data/provider_url/user2/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first_and_is_registered_as_the_owner/second_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first_and_is_registered_as_the_owner/data/provider_url/user2/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_first_but_second_finishes_first_and_is_registered_as_the_owner/data/provider_url/user2/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/first_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/second_auth Update golden auth response JSON key from uuid to sub.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/data/provider_url/user1/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/data/provider_url/user1/password Add/update golden cached password file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/data/provider_url/user2/token.json Add/update golden cached token file content.
authd-oidc-brokers/internal/broker/testdata/golden/TestConcurrentIsAuthenticated/First_auth_starts_and_finishes_before_second/data/provider_url/user2/password Add/update golden cached password file content.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/users/manager.go
Comment thread internal/users/manager.go Outdated
Comment thread internal/brokers/dbusbroker.go Outdated
Comment thread internal/brokers/dbusbroker.go Outdated
Comment thread authd-oidc-brokers/internal/broker/broker.go Outdated
Comment thread authd-oidc-brokers/internal/broker/broker.go Outdated
Comment thread authd-oidc-brokers/internal/broker/broker.go Outdated
Comment thread internal/users/types/types.go Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 165 out of 209 changed files in this pull request and generated 1 comment.

Comment thread authd-oidc-brokers/internal/broker/broker_test.go Outdated
@denisonbarbosa

Copy link
Copy Markdown
Member Author

@copilot resolve the merge conflicts in this pull request

Copilot AI commented May 29, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved and pushed in a1a98e29.

@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch 2 times, most recently from fa8c983 to 9a10353 Compare June 1, 2026 12:22
@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.57895% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.25%. Comparing base (ea62880) to head (cd574c9).
⚠️ Report is 15 commits behind head on main.

Files with missing lines Patch % Lines
authd-oidc-brokers/internal/broker/broker.go 79.54% 45 Missing ⚠️
...d-oidc-brokers/internal/dbusservice/dbusservice.go 0.00% 10 Missing ⚠️
internal/services/pam/pam.go 75.75% 8 Missing ⚠️
internal/users/db/migration.go 70.00% 6 Missing ⚠️
internal/users/manager.go 88.37% 5 Missing ⚠️
internal/users/db/update.go 94.11% 1 Missing ⚠️
internal/users/db/users.go 94.11% 1 Missing ⚠️
pam/internal/adapter/brokerselection.go 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1546      +/-   ##
==========================================
- Coverage   87.98%   87.25%   -0.74%     
==========================================
  Files          99      120      +21     
  Lines        6832     8315    +1483     
  Branches      111      111              
==========================================
+ Hits         6011     7255    +1244     
- Misses        765     1004     +239     
  Partials       56       56              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch from d352cfa to 4ef784c Compare June 1, 2026 14:14
@denisonbarbosa
denisonbarbosa marked this pull request as ready for review June 1, 2026 21:14
@denisonbarbosa
denisonbarbosa requested a review from adombeck June 1, 2026 21:14
Comment thread authd-oidc-brokers/internal/providers/info/info.go Outdated
Comment thread authd-oidc-brokers/internal/broker/broker.go Outdated
Comment thread internal/users/db/users.go Outdated
@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch 3 times, most recently from 3851b8f to ca9c64c Compare June 3, 2026 12:40
@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch from a199ee4 to 52fb313 Compare June 29, 2026 12:21
@denisonbarbosa
denisonbarbosa requested a review from adombeck June 29, 2026 13:12

@adombeck adombeck left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

The identifier returned by providers is the OIDC "sub" claim (or an
equivalent stable per-tenant identifier), not a UUID. Rename the
info.User field and its JSON tag to ProviderID/"provider_id" so the
meaning is explicit and matches the wire format exchanged with authd.

This is a pure rename with no behavior change.
Entra ID's "sub" is pairwise per application and changes if the app
registration changes, whereas "oid" is stable across all apps within a
tenant.

Use the "oid" claim as the provider ID and fail authentication when it
is missing.
…ider ID

Bump the broker API to v3, adding a "provider_id" argument to NewSession
and DeleteUser so authd can pass the stable provider identifier.

When a provider ID is supplied, the broker stores and looks up the
on-disk cache under a provider-ID keyed directory instead of the
username. Existing username-based caches are migrated on first use and a
compatibility symlink is left behind, to ensure compatibility with older
versions. V2 callers keep the previous username-based behavior.

A dangling compatibility symlink, left when its provider-ID keyed target
is removed (for example by a partial DeleteUser), would otherwise make
every later cache write resolve through the broken link and fail, locking
the user out with no way to recover. Detect that on session start and
drop the stale link so the username path can be rebuilt as a fresh cache
directory.
Add a "provider_id" column to the users table to hold the stable
provider identifier, with uniqueness scoped per broker via a partial
index, plus the schema migration and the UserRow/query plumbing.

Add UserByProviderID to look up a user by its broker-scoped provider ID.
No caller populates the column yet.
@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch from 52fb313 to c42feac Compare June 29, 2026 13:34
@adombeck

adombeck commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Beware that there are merge conflicts with #1520. It might make sense to check if it's easier to resolve them here, i.e. rebasing this branch on main after merging #1520, rather than the other way around.

denisonbarbosa and others added 7 commits June 29, 2026 12:13
Propagate the provider ID through UserInfo and the user manager. On
UpdateUser, resolve the user by its broker-scoped provider ID so that a
username/email change at the identity provider is handled as a rename of
the existing user (preserving its UID and cleaning up stale local group
entries) instead of failing with a UID conflict.

The provider-ID match authorises the rename and bypasses the "UID already
in use" guard, so an IdP-side email change landing on a username already
owned by a different user would otherwise hit the raw UNIQUE(name)
constraint and surface an opaque SQLite error. Reject that collision
explicitly with a clear message, leaving both users intact.

Expose ProviderIDForUser for callers that need a user's stable
identifier.
Derive the broker ID from its D-Bus name so it stays stable regardless
of the broker's display name, and detect and call the v3 broker
interface when available, passing the user's stable provider ID to
NewSession and DeleteUser.

The PAM and user services look the provider ID up from the database so
brokers can resolve the provider-ID keyed cache directly. The example
broker and test doubles are updated to the v3 signatures.
…to GetBroker

Now that the brokerID is a mandatory and unchangeable data for each
user, updating the API for cleaner names makes sense.
The previous code was accidentally ignoring the error that could be
returned by committing the transaction.

Adding a named return to the function and joining with the previous
error value should allow us to also consider any error that happen when
committing the transaction.
broker_id and provider_id scope and define a user's stable identity.
Marshaling them with omitempty hid them from YAML dumps when empty, which
implied they are optional extras and made it ambiguous in fixtures and
golden dumps whether a user genuinely has no broker/provider ID or the
field was merely omitted.

Drop omitempty from their YAML struct tags so the identity fields are
always explicit. This only affects the YAML serialization used by tests;
the golden files are regenerated to show the empty values.
…ogin

Add two tests covering provider ID cache directory resolution paths that
were not exercised end-to-end:

- TestDeviceAuthRedirectsToExistingProviderIDDir drives a full online
  device auth followed by the new password step when the provider
  ID-keyed directory already exists but the username path does not yet
  resolve to it (the broker is updated but authd is not, so NewSession
  receives no provider ID). It asserts the redirect to the existing
  directory happens during device auth, before the device registration
  data is read and the new token and password are written, so the cache
  is consolidated under the provider ID directory rather than a stray
  username-keyed one.

- TestOfflineLoginCacheDirectoryResolution covers which directory a
  session resolves to on the first offline login after the update: an
  already-migrated cache stays on the provider ID directory through its
  compatibility symlink; a legacy directory whose cached token carries a
  provider ID is migrated even offline; and a legacy directory whose
  token has no provider ID stays on the username directory until the next
  online login.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The comment claimed cache directory migration is performed only during
online authentication and that an offline login always stays on the
username-keyed cache. That is inaccurate: NewSession migrates an offline
session whenever the cached token already carries a provider ID. Only a
legacy cache whose token predates the provider ID is deferred, because
finishAuth learns the provider ID from the freshly authenticated user
info, which is only available online. Reword the comment to describe what
this branch actually skips and why.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@denisonbarbosa
denisonbarbosa force-pushed the use-sub-for-identification branch from c42feac to cd574c9 Compare June 29, 2026 16:14
@denisonbarbosa

Copy link
Copy Markdown
Member Author

Beware that there are merge conflicts with #1520. It might make sense to check if it's easier to resolve them here, i.e. rebasing this branch on main after merging #1520, rather than the other way around.

I think it makes more sense to merge this one first, since #1520 might have to update some of its logic based on these changes (i.e., how to get the OID value from the token provided by the Direct Password auth), wdyt @adombeck @nooreldeenmansour?

@denisonbarbosa denisonbarbosa added the e2e-tests This issue is related to end-to-end tests / Run end-to-end tests on this pull request label Jun 30, 2026
@nooreldeenmansour

nooreldeenmansour commented Jun 30, 2026

Copy link
Copy Markdown
Member

Beware that there are merge conflicts with #1520. It might make sense to check if it's easier to resolve them here, i.e. rebasing this branch on main after merging #1520, rather than the other way around.

I think it makes more sense to merge this one first, since #1520 might have to update some of its logic based on these changes (i.e., how to get the OID value from the token provided by the Direct Password auth), wdyt @adombeck @nooreldeenmansour?

I've merged your branch into main and rebased my branch over it. And there were two non-major conflicts, which I resolved, and they are recorded locally because I have rerere enabled. So let's merge yours first

Edit: A small manual fix was needed, to compile and run the tests, but I've stashed that fix as well

@adombeck

adombeck commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

they are recorded locally because I have rerere enabled

+1 on enabling rerere. however, in this case, you could also git fetch && git rebase --onto origin/main <id-of-your-first-commit>~1 after this PR was merged to cherry-pick the commits and avoid the merge conflicts occurring again.

@denisonbarbosa
denisonbarbosa merged commit f9b39a8 into main Jun 30, 2026
64 of 81 checks passed
@denisonbarbosa
denisonbarbosa deleted the use-sub-for-identification branch June 30, 2026 21:26
adombeck added a commit that referenced this pull request Jul 8, 2026
Since #1546, users are bound to the broker (identity provider) they
first authenticated with. Selecting a different broker would be
rejected by authd via SelectBroker. Yet the UI still offered to
"go back to choose the provider" whenever multiple brokers were
available — including for users who already have a binding.

GetBroker only ever returns a non-local broker when the user has a
persistent binding in the database (or the in-memory cache populated
from the DB). Add a brokerBoundForUser event that AutoSelectForUser
emits in that case to suppress broker re-selection in the UI.

When brokerBoundForUser is received, both uiModel (interactive
terminal) and nativeModel (SSH/native) set userIsBoundToBroker, which
causes previousStage() to skip Stage_brokerSelection and return
Stage_userSelection instead. The GDM model also handles the event to
emit the expected BrokerSelected GDM event.

Result: existing users see "go back to user selection" instead of
"go back to choose the provider". New users who select a broker
manually are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
adombeck added a commit that referenced this pull request Jul 8, 2026
Since #1546, users are bound to the broker (identity provider) they
first authenticated with. Selecting a different broker would be
rejected by authd via SelectBroker. Yet the UI still offered to
"go back to choose the provider" whenever multiple brokers were
available — including for users who already have a binding.

GetBroker only ever returns a non-local broker when the user has a
persistent binding in the database (or the in-memory cache populated
from the DB). Add a brokerBoundForUser event that AutoSelectForUser
emits in that case to suppress broker re-selection in the UI.

When brokerBoundForUser is received, both uiModel (interactive
terminal) and nativeModel (SSH/native) set userIsBoundToBroker, which
causes previousStage() to skip Stage_brokerSelection and return
Stage_userSelection instead. The GDM model also handles the event to
emit the expected BrokerSelected GDM event.

Result: existing users see "go back to user selection" instead of
"go back to choose the provider". New users who select a broker
manually are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
adombeck added a commit that referenced this pull request Jul 13, 2026
Since #1546, users are bound to the broker (identity provider) they
first authenticated with. Selecting a different broker would be
rejected by authd via SelectBroker. Yet the UI still offered to
"go back to choose the provider" whenever multiple brokers were
available — including for users who already have a binding.

GetBroker only ever returns a non-local broker when the user has a
persistent binding in the database (or the in-memory cache populated
from the DB). Add a brokerBoundForUser event that AutoSelectForUser
emits in that case to suppress broker re-selection in the UI.

When brokerBoundForUser is received, both uiModel (interactive
terminal) and nativeModel (SSH/native) set userIsBoundToBroker, which
causes previousStage() to skip Stage_brokerSelection and return
Stage_userSelection instead. The GDM model also handles the event to
emit the expected BrokerSelected GDM event.

Result: existing users see "go back to user selection" instead of
"go back to choose the provider". New users who select a broker
manually are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
adombeck added a commit that referenced this pull request Jul 14, 2026
Since #1546, users are bound to the broker (identity provider) they
first authenticated with. Selecting a different broker would be
rejected by authd via SelectBroker. Yet the UI still offered to
"go back to choose the provider" whenever multiple brokers were
available — including for users who already have a binding.

GetBroker only ever returns a non-local broker when the user has a
persistent binding in the database (or the in-memory cache populated
from the DB). Add a brokerBoundToUser event that AutoSelectForUser
emits in that case to suppress broker re-selection in the UI.

When brokerBoundToUser is received, both uiModel (interactive
terminal) and nativeModel (SSH/native) set userIsBoundToBroker, which
causes previousStage() to skip Stage_brokerSelection and return
Stage_userSelection instead. The GDM model also handles the event to
emit the expected BrokerSelected GDM event.

Result: existing users see "go back to user selection" instead of
"go back to choose the provider". New users who select a broker
manually are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
adombeck added a commit that referenced this pull request Aug 10, 2026
Since #1546, users are bound to the broker (identity provider) they
first authenticated with. Selecting a different broker would be
rejected by authd via SelectBroker. Yet the UI still offered to
"go back to choose the provider" whenever multiple brokers were
available — including for users who already have a binding.

GetBroker only ever returns a non-local broker when the user has a
persistent binding in the database (or the in-memory cache populated
from the DB). Add a brokerBoundToUser event that AutoSelectForUser
emits in that case to suppress broker re-selection in the UI.

When brokerBoundToUser is received, both uiModel (interactive
terminal) and nativeModel (SSH/native) set userIsBoundToBroker, which
causes previousStage() to skip Stage_brokerSelection and return
Stage_userSelection instead. The GDM model also handles the event to
emit the expected BrokerSelected GDM event.

Result: existing users see "go back to user selection" instead of
"go back to choose the provider". New users who select a broker
manually are unaffected.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

e2e-tests This issue is related to end-to-end tests / Run end-to-end tests on this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants