-
Notifications
You must be signed in to change notification settings - Fork 24
ORCID authentication #1184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
ORCID authentication #1184
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
8edca59
Distinguish authenticated/unauthenticated ORCID IDs. #783
fbacall cd566fd
Button to allow ORCID authentication
fbacall 690b3ca
Remove debug code
fbacall b74f36f
Fix condition not being checked
fbacall 41541fe
Handle blank orcid in `orcid_url`
fbacall 231e67d
Splat options properly
fbacall 009c406
Prefer `button_to` over `link_to ... method: :post`
fbacall d75e94d
Check ORCID prefix present in migration to avoid unnecessary updates
fbacall 90f765c
Move ORCID authentication into its own controller
fbacall 83f14b5
Handle missing ORCID from metadata
fbacall 359b780
Index profiles on ORCID
fbacall fdbcf98
Authenticating ORCID should de-authenticate existing ORCID users
fbacall File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| class OrcidController < ApplicationController | ||
| before_action :authenticate_user! | ||
| before_action :set_oauth_client, only: [:authenticate, :callback] | ||
|
|
||
| # Faraday::ParsingError occurs in rack-oauth2 if the response does not contain JSON | ||
| rescue_from(Rack::OAuth2::Client::Error, Faraday::ParsingError) do | ||
| handle_error(:unprocessable_entity, t('orcid.error')) | ||
| end | ||
|
|
||
| def authenticate | ||
| redirect_to @oauth2_client.authorization_uri(scope: '/authenticate'), allow_other_host: true | ||
| end | ||
|
|
||
| def callback | ||
| @oauth2_client.authorization_code = params[:code] | ||
| token = Rack::OAuth2::AccessToken::Bearer.new(access_token: @oauth2_client.access_token!) | ||
| orcid = token.access_token&.raw_attributes['orcid'] | ||
| respond_to do |format| | ||
| profile = current_user.profile | ||
| if orcid.present? | ||
| if profile.authenticate_orcid(orcid) | ||
| flash[:notice] = t('orcid.authentication_success') | ||
| else | ||
| flash[:error] = profile.errors.full_messages.join(', ') | ||
| end | ||
| else | ||
| flash[:error] = t('orcid.authentication_failure') | ||
| end | ||
| format.html { redirect_to current_user } | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def set_oauth_client | ||
| config = Rails.application.config.secrets.orcid | ||
| @oauth2_client ||= Rack::OAuth2::Client.new( | ||
| identifier: config[:client_id], | ||
| secret: config[:secret], | ||
| redirect_uri: config[:redirect_uri].presence || orcid_callback_url, | ||
| authorization_endpoint: '/oauth/authorize', | ||
| token_endpoint: '/oauth/token', | ||
| host: config[:host].presence || (Rails.env.production? ? 'orcid.org' : 'sandbox.orcid.org') | ||
| ) | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
db/migrate/20251013115523_add_orcid_authenticated_to_profiles.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| class AddOrcidAuthenticatedToProfiles < ActiveRecord::Migration[7.2] | ||
| def change | ||
| add_column :profiles, :orcid_authenticated, :boolean, default: false | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| class UpdateOrcidsInProfiles < ActiveRecord::Migration[7.2] | ||
| ORCID_PREFIX = 'https://orcid.org/'.freeze | ||
| def up | ||
| Profile.where('orcid IS NOT NULL').find_each do |profile| | ||
| profile.update_column(:orcid, profile.orcid.gsub(ORCID_PREFIX, '')) if profile.orcid.start_with?(ORCID_PREFIX) | ||
| end | ||
| end | ||
|
|
||
| def down | ||
| Profile.where('orcid IS NOT NULL').find_each do |profile| | ||
| profile.update_column(:orcid, "#{ORCID_PREFIX}#{profile.orcid}") unless profile.orcid.start_with?(ORCID_PREFIX) | ||
| end | ||
|
fbacall marked this conversation as resolved.
|
||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| class AddIndexOnOrcidToProfiles < ActiveRecord::Migration[7.2] | ||
| def change | ||
| add_index :profiles, :orcid | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,3 +19,6 @@ eventbrite_api_v3: | |
| fairsharing: | ||
| username: tess-test-bot | ||
| password: abc123 | ||
| orcid: | ||
| client_id: test | ||
| secret: test | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| require 'test_helper' | ||
|
|
||
| class OrcidControllerTest < ActionController::TestCase | ||
| include Devise::Test::ControllerHelpers | ||
|
|
||
| test 'authenticate orcid logged-in user' do | ||
| sign_in users(:regular_user) | ||
|
|
||
| post :authenticate | ||
|
|
||
| assert_redirected_to /https:\/\/sandbox\.orcid\.org\/oauth\/authorize\?.+/ | ||
| end | ||
|
|
||
| test 'do not authenticate orcid if user not logged-in' do | ||
| post :authenticate | ||
|
|
||
| assert_redirected_to new_user_session_path | ||
| end | ||
|
|
||
| test 'handle callback and assign orcid if free' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/get_token_free_orcid') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| profile = user.profile.reload | ||
| assert_equal '0009-0006-0987-5702', profile.orcid | ||
| assert profile.orcid_authenticated? | ||
| assert_redirected_to user | ||
| end | ||
|
|
||
| test 'handle callback and assign orcid if unauthenticated' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/get_token_unauth_orcid') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| profile = user.profile.reload | ||
| assert_equal '0000-0002-0048-3300', profile.orcid | ||
| assert profile.orcid_authenticated? | ||
| assert_redirected_to user | ||
| end | ||
|
|
||
| test 'handle callback and assign orcid even if already used' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| existing_orcid_user = users(:trainer_user) | ||
| assert existing_orcid_user.profile.orcid_authenticated? | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/get_token_existing_orcid') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| profile = user.profile.reload | ||
| assert_equal '0000-0002-1825-0097', profile.orcid | ||
| assert profile.orcid_authenticated? | ||
| refute existing_orcid_user.reload.profile.orcid_authenticated? | ||
| assert_redirected_to user | ||
| assert flash[:error].blank? | ||
| end | ||
|
|
||
| test 'do not handle callback if not logged-in' do | ||
| mock_images | ||
|
|
||
| get :callback, params: { code: '123xyz' } | ||
|
|
||
| assert_redirected_to new_user_session_path | ||
| end | ||
|
|
||
| test 'handle unauth error during callback' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/error_unauth') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| assert_response :unprocessable_entity | ||
| assert_select '#error-message', text: /error occurred.+ORCID/ | ||
| profile = user.profile.reload | ||
| assert profile.orcid.blank? | ||
| refute profile.orcid_authenticated? | ||
| end | ||
|
|
||
| test 'handle unexpected error during callback' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/error_500') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| assert_response :unprocessable_entity | ||
| assert_select '#error-message', text: /error occurred.+ORCID/ | ||
| profile = user.profile.reload | ||
| assert profile.orcid.blank? | ||
| refute profile.orcid_authenticated? | ||
| end | ||
|
|
||
| test 'handle missing orcid during callback' do | ||
| mock_images | ||
| user = users(:regular_user) | ||
| sign_in user | ||
|
|
||
| VCR.use_cassette('orcid/get_token_orcid_missing') do | ||
| get :callback, params: { code: '123xyz' } | ||
| end | ||
|
|
||
| assert_redirected_to user | ||
| assert_includes flash[:error], 'Failed to authenticat' | ||
| profile = user.profile.reload | ||
| assert profile.orcid.blank? | ||
| refute profile.orcid_authenticated? | ||
| end | ||
| end | ||
|
fbacall marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.