brokers: use providerID for user identification - #1546
Conversation
There was a problem hiding this comment.
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
subin the authd users DB (schema v3) with a partial unique index, and expose lookup bysub. - Update authd user update logic to resolve existing users by
suband handle sub-matched renames (including local group membership cleanup). - Bump broker API to v3, passing
subinNewSession/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.
|
@copilot resolve the merge conflicts in this pull request |
Resolved and pushed in |
fa8c983 to
9a10353
Compare
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|
d352cfa to
4ef784c
Compare
3851b8f to
ca9c64c
Compare
a199ee4 to
52fb313
Compare
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.
52fb313 to
c42feac
Compare
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>
c42feac to
cd574c9
Compare
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 Edit: A small manual fix was needed, to compile and run the tests, but I've stashed that fix as well |
+1 on enabling rerere. however, in this case, you could also |
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>
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>
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>
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>
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>
We used to rely on emails or
preferred_usernameto 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 thesub(oidfor 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