Files
sure/test/models/oidc_identity_test.rb
plind 6402f1dd08 fix(sso): preserve user-edited name across OIDC logins (#1777)
OidcIdentity#sync_user_attributes! runs on every SSO sign-in and
overwrote user.first_name / user.last_name with whatever the IdP sent,
because the precedence was `auth.info.* || user.*` — the IdP always
won when it supplied a value. A user who edited their first name to
"Adam" inside Sure had it reset to the IdP value "Ben" on the next
login, while the last name only "stuck" when the IdP happened not to
return a last_name (#1103).

Swap the precedence to `user.* || auth.info.*` so the IdP fills only
when Sure has nothing on file (first link or admin-blanked field).
Edits inside Sure are then authoritative for every subsequent login.
The audit copy on the OidcIdentity record itself is unchanged, so the
IdP-reported name is still available for debugging.

Closes #1103.

Co-authored-by: plind-junior <plind-junior@users.noreply.github.com>
2026-05-12 21:55:22 +02:00

118 lines
3.5 KiB
Ruby

require "test_helper"
class OidcIdentityTest < ActiveSupport::TestCase
setup do
@user = users(:family_admin)
@oidc_identity = oidc_identities(:bob_google)
end
test "belongs to user" do
assert_equal @user, @oidc_identity.user
end
test "validates presence of provider" do
@oidc_identity.provider = nil
assert_not @oidc_identity.valid?
assert_includes @oidc_identity.errors[:provider], "can't be blank"
end
test "validates presence of uid" do
@oidc_identity.uid = nil
assert_not @oidc_identity.valid?
assert_includes @oidc_identity.errors[:uid], "can't be blank"
end
test "validates presence of user_id" do
@oidc_identity.user_id = nil
assert_not @oidc_identity.valid?
assert_includes @oidc_identity.errors[:user_id], "can't be blank"
end
test "validates uniqueness of uid scoped to provider" do
duplicate = OidcIdentity.new(
user: users(:family_member),
provider: @oidc_identity.provider,
uid: @oidc_identity.uid
)
assert_not duplicate.valid?
assert_includes duplicate.errors[:uid], "has already been taken"
end
test "allows same uid for different providers" do
different_provider = OidcIdentity.new(
user: users(:family_member),
provider: "different_provider",
uid: @oidc_identity.uid
)
assert different_provider.valid?
end
test "records authentication timestamp" do
old_timestamp = @oidc_identity.last_authenticated_at
travel_to 1.hour.from_now do
@oidc_identity.record_authentication!
assert @oidc_identity.last_authenticated_at > old_timestamp
end
end
test "sync_user_attributes! preserves user-edited first and last name" do
# Regression for #1103: every SSO login used to overwrite user.first_name
# with the IdP value, clobbering edits the user made inside Sure.
@user.update!(first_name: "Adam", last_name: "Smith")
auth = OmniAuth::AuthHash.new(
provider: @oidc_identity.provider,
uid: @oidc_identity.uid,
info: { email: @user.email, first_name: "Ben", last_name: "Jones" }
)
@oidc_identity.sync_user_attributes!(auth)
@user.reload
assert_equal "Adam", @user.first_name
assert_equal "Smith", @user.last_name
# Identity record itself still mirrors the IdP for audit / debugging.
assert_equal "Ben", @oidc_identity.info["first_name"]
assert_equal "Jones", @oidc_identity.info["last_name"]
end
test "sync_user_attributes! fills blank user names from the IdP" do
@user.update!(first_name: nil, last_name: nil)
auth = OmniAuth::AuthHash.new(
provider: @oidc_identity.provider,
uid: @oidc_identity.uid,
info: { email: @user.email, first_name: "Ben", last_name: "Jones" }
)
@oidc_identity.sync_user_attributes!(auth)
@user.reload
assert_equal "Ben", @user.first_name
assert_equal "Jones", @user.last_name
end
test "creates from omniauth hash" do
auth = OmniAuth::AuthHash.new({
provider: "google_oauth2",
uid: "google-123456",
info: {
email: "test@example.com",
name: "Test User",
first_name: "Test",
last_name: "User"
}
})
identity = OidcIdentity.create_from_omniauth(auth, @user)
assert identity.persisted?
assert_equal "google_oauth2", identity.provider
assert_equal "google-123456", identity.uid
assert_equal "test@example.com", identity.info["email"]
assert_equal "Test User", identity.info["name"]
assert_equal @user, identity.user
assert_not_nil identity.last_authenticated_at
end
end