From bc73c376be2348b585e86e6ac5e7cdf22186716c Mon Sep 17 00:00:00 2001 From: mkovalua Date: Thu, 26 Mar 2026 15:41:38 +0200 Subject: [PATCH 1/2] If a user has an existing ORCID ID, and then associates a new ORCID ID, the existing ORCID ID should be removed/overwritten --- api/users/views.py | 6 ++++-- framework/auth/views.py | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/api/users/views.py b/api/users/views.py index b920798be86..4a34801e9e2 100644 --- a/api/users/views.py +++ b/api/users/views.py @@ -755,7 +755,10 @@ def post(self, request, *args, **kwargs): # 1. update user oauth, with pending status external_identity[external_id_provider][external_id] = 'LINK' if external_id_provider in user.external_identity: - user.external_identity[external_id_provider].update(external_identity[external_id_provider]) + if external_id_provider == 'orcid': + user.external_identity[external_id_provider] = external_identity[external_id_provider] + else: + user.external_identity[external_id_provider].update(external_identity[external_id_provider]) else: user.external_identity.update(external_identity) if not user.accepted_terms_of_service and accepted_terms_of_service: @@ -1153,7 +1156,6 @@ class ConfirmEmailView(generics.CreateAPIView): def _process_external_identity(self, user, external_identity, service_url): """Handle all external_identity logic, including task enqueueing and url updates.""" - provider = next(iter(external_identity)) if provider not in user.external_identity: raise ValidationError('External-ID provider mismatch.') diff --git a/framework/auth/views.py b/framework/auth/views.py index aefb1b4f0e8..0e72df882c7 100644 --- a/framework/auth/views.py +++ b/framework/auth/views.py @@ -1097,7 +1097,10 @@ def external_login_email_post(): # 1. update user oauth, with pending status external_identity[external_id_provider][external_id] = 'LINK' if external_id_provider in user.external_identity: - user.external_identity[external_id_provider].update(external_identity[external_id_provider]) + if external_id_provider == 'orcid': + user.external_identity[external_id_provider] = external_identity[external_id_provider] + else: + user.external_identity[external_id_provider].update(external_identity[external_id_provider]) else: user.external_identity.update(external_identity) if not user.accepted_terms_of_service and form.accepted_terms_of_service.data: From 41ad2daaec47fb15e2f1490e6adbb533392b1a36 Mon Sep 17 00:00:00 2001 From: mkovalua Date: Fri, 27 Mar 2026 15:00:09 +0200 Subject: [PATCH 2/2] add test to check if orcid is overwritten --- .../users/views/test_user_external_login.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/api_tests/users/views/test_user_external_login.py b/api_tests/users/views/test_user_external_login.py index 6f436f5d422..9278ac984ae 100644 --- a/api_tests/users/views/test_user_external_login.py +++ b/api_tests/users/views/test_user_external_login.py @@ -87,3 +87,21 @@ def test_existing_user(self, app, payload, url, user_one, session_data, csrf_tok assert res.json == {'external_id_provider': 'orcid', 'auth_user_fullname': 'external login'} user_one.reload() assert user_one.username in user_one.unconfirmed_emails + + def test_existing_user_orcid_overwrites(self, app, payload, url, user_one, session_data, csrf_token): + user_one.external_identity = { + 'orcid': { + '0000-0000-0000-0000': 'LINK', + } + } + user_one.save() + app.set_cookie(CSRF_COOKIE_NAME, csrf_token) + app.set_cookie(settings.COOKIE_NAME, str(session_data)) + assert '0000-0000-0000-0000' in user_one.external_identity['orcid'] + payload['data']['attributes']['email'] = user_one.username + with capture_notifications(): + res = app.post_json_api(url, payload, headers={'X-CSRFToken': csrf_token}) + assert res.status_code == 200 + user_one.reload() + assert '0000-0000-0000-0000' not in user_one.external_identity['orcid'] + assert user_one.external_identity['orcid'] == {'1234-1234-1234-1234': 'LINK'}