Skip to content

Add a sign-up API for the modern UI, with optional ORCID iD - #12858

Open
ChengShi-1 wants to merge 9 commits into
developfrom
spa-builtin-user-signup
Open

ChengShi-1 wants to merge 9 commits into
developfrom
spa-builtin-user-signup

Conversation

@ChengShi-1

@ChengShi-1 ChengShi-1 commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Adds the API behind the sign-up form in the modern UI (SPA), so new users can create a builtin account without the JSF "Sign Up" page and without Keycloak. They can optionally connect their ORCID iD while signing up.

  • POST /api/users/signup creates a builtin user. It uses the same validation as the JSF "Sign Up" page, respects :AllowSignUp, and is rate-limited per client IP address.
  • GET /api/users/signup/orcid/authorize and POST /api/users/signup/orcid connect an ORCID iD before the account exists:
    • They use the configured ORCID OAuth2 provider, which can stay registered with "enabled": false.
    • The response pre-fills the form with name, email, affiliation, position, the affiliation's ROR ID (affiliationRor) and a suggested username.
    • It also returns a short-lived orcidToken, which the sign-up request uses to save the ORCID iD to the new account.
  • GET /api/users/:me now includes authenticatedOrcid.
  • The ORCID redirect URLs are an allow-list setting (dataverse.api.signup.orcid-redirect-uris), so every installation needs to set its own SPA sign-up URL.

Everything is behind the dataverse.feature.api-builtin-user-signup feature flag and off by default.

Which issue(s) this PR closes:

Special notes for your reviewer:
I wrote most of the code with the help of AI, then manually reviewed it and ran some tests. Please bear with me as I'm not very experienced with Java, and feel free to flag anything that doesn't look right during your review.
If the changes are too complicated to explain in comments, you're also welcome to push a commit or create a separate branch with your suggested fixes. Thank you for your help!

Suggestions on how to test this:

  1. Set DATAVERSE_FEATURE_API_BUILTIN_USER_SIGNUP=1. For local testing, also set DATAVERSE_API_SIGNUP_RATE_LIMIT_PER_HOUR=0, which turns off the rate limit.

  2. Sign up without ORCID:

    curl -X POST -H "Content-Type: application/json" "$SERVER_URL/api/users/signup" \
      --data '{"username":"alovelace","firstName":"Ada","lastName":"Lovelace","emailAddress":"ada@example.edu","password":"Str0ngPassw0rd","termsAccepted":true}'
    

    Check that the user can log in, and that invalid fields, a duplicate username or email, :AllowSignUp=false and the rate limit return errors.

  3. Sign up with ORCID (optional; needs your own ORCID sandbox credentials)

    Setup

    • Get or Create a free account at https://sandbox.orcid.org. (has to be a @mailinator.com email to use developer tools) Under Developer Tools, register an app with the redirect URI http://127.0.0.1:8000/modern/sign-up. ORCID doesn't accept localhost redirect URIs, so use 127.0.0.1 here and everywhere below.
    • Add the ORCID app's client ID and secret(please reach out to me for this) to Dataverse as an ORCID OAuth2 provider (see "ORCID Integration" in the Installation Guide; "enabled": false is fine).
    • Set DATAVERSE_API_SIGNUP_ORCID_REDIRECT_URIS=http://127.0.0.1:8000/modern/sign-up. It must match the redirect URI exactly: same host, port and path, no trailing slash.

    Option A: from the SPA. Open http://127.0.0.1:8000/modern/sign-up with signUpWithOrcidEnabled: true in the SPA's config.js, then click "Create or Connect your ORCID iD" and finish at ORCID. The form should show your ORCID iD and fill in the empty fields.

    Option B: with curl. Stop the SPA dev server first. Otherwise the SPA uses ORCID's one-time code before you can.

    export SERVER_URL=http://127.0.0.1:8000
    export REDIRECT_URI=http://127.0.0.1:8000/modern/sign-up
    
    # 1. Get the ORCID URL and open it in the browser
    curl -s "$SERVER_URL/api/users/signup/orcid/authorize?redirectUri=$REDIRECT_URI" | jq -r .data.authorizationUrl | xargs open

    Sign in at ORCID and click "Authorize access". The browser then goes to http://127.0.0.1:8000/modern/sign-up?code=...&state=...; the page itself may show 502. Copy code and state from the address bar:

    export CODE='...'
    export STATE='...'
    
    # 2. Exchange them. Returns the ORCID iD, pre-fill fields (name, email, affiliation, affiliationRor, position, suggested username) and an orcidToken
    curl -s -X POST -H "Content-Type: application/json" "$SERVER_URL/api/users/signup/orcid" \
      --data '{"code":"'$CODE'","state":"'$STATE'","redirectUri":"'$REDIRECT_URI'"}' | tee /tmp/orcid.json | jq
    export ORCID_TOKEN=$(jq -r .data.orcidToken /tmp/orcid.json)
    
    # 3. Sign up with the orcidToken
    curl -s -X POST -H "Content-Type: application/json" "$SERVER_URL/api/users/signup" --data '{
      "username":"orcidtest","firstName":"Ada","lastName":"Lovelace",
      "emailAddress":"orcidtest@mailinator.com","password":"Str0ngPassw0rd",
      "termsAccepted":true,"orcidToken":"'$ORCID_TOKEN'"}' | jq
    
    # 4. Check the account. Expect "authenticatedOrcid": "https://sandbox.orcid.org/..."
    TOKEN=$(curl -s "$SERVER_URL/realms/test/protocol/openid-connect/token" -d grant_type=password -d client_id=test \
      -d scope=openid -d username=orcidtest -d password=Str0ngPassw0rd | jq -r .access_token)
    curl -s -H "Authorization: Bearer $TOKEN" "$SERVER_URL/api/users/:me" | jq

    What to check

    • A redirect URI that isn't on the allow-list returns 400 from authorize.
    • Posting the same code a second time returns 400; ORCID codes work only once.
    • Signing up again with the same orcidToken and a new username and email is refused with "This ORCID iD is already linked to another account."
    • Connecting the same ORCID iD again (steps 1–2) returns 409 "ORCID iD already in use".
    • An invalid or expired orcidToken is refused. state expires after 15 minutes and orcidToken after 30.

    Without an ORCID provider configured, authorize returns 404 "ORCID is not configured on this installation." Signing up without ORCID (step 2) works the same either way.

  4. Unit tests: mvn test -Dtest='OrcidOAuth2APTest,OrcidSignUpTokensTest,SignUpBuiltinUserCommandTest'.

Does this PR introduce a user interface change? If mockups are available, please link/include them here:

No change to the JSF UI. API only

Is there a release notes update needed for this change?:

Yes, added in doc/release-notes/spa-builtin-user-signup.md.

Additional documentation:

  • The API Guide has a new "Sign Up as a Builtin User" section in native-api.rst.
  • New settings: dataverse.feature.api-builtin-user-signup, dataverse.api.signup.rate-limit-per-hour, dataverse.api.signup.orcid-redirect-uris.

🤖 Generated with Claude Code

ChengShi-1 and others added 3 commits October 19, 2026 13:46
New users can create their own builtin account without an API token or
the :BuiltinUsersKey, for example from the SPA sign-up form. The new
SignUpBuiltinUserCommand applies the same validation rules and messages
as the JSF Sign Up page, creates the builtin and authenticated user,
and sends the standard welcome email with the confirm-email link. The
account is usable right away; email verification before creation is
planned as a follow-up.

The endpoint is off unless the dataverse.feature.api-builtin-user-signup
feature flag is set, honors :AllowSignUp, and is rate-limited per client
IP address via dataverse.api.signup.rate-limit-per-hour (default 10).
affiliationRor is rejected until the ROR column from #12620 exists.

The dev compose file enables the flag and turns off the rate limit so
SPA development and repeated integration test runs work out of the box.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /api/users/signup/orcid/authorize returns the ORCID authorization URL
for an allowed redirect URI (dataverse.api.signup.orcid-redirect-uris).
POST /api/users/signup/orcid exchanges the code, returns the ORCID profile
for pre-filling and an encrypted orcidToken, which POST /api/users/signup
accepts to save the ORCID iD on the new account. An ORCID iD already linked
to another account is rejected.

The user JSON now includes authenticatedOrcid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parse ORCID API v3.0 employment summaries, prefer the current employment
with the latest start date, and return the affiliation's ROR ID and a
suggested username from POST /api/users/signup/orcid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ChengShi-1 ChengShi-1 changed the title APIs support SPA Builtin User Sign Up Add a sign-up API for the modern UI, with optional ORCID iD Oct 27, 2026
@ChengShi-1 ChengShi-1 self-assigned this Oct 27, 2026
@ChengShi-1 ChengShi-1 added this to the 6.13 milestone Oct 27, 2026
@ChengShi-1 ChengShi-1 moved this to In Progress 💻 in IQSS Dataverse Project Oct 27, 2026
@ChengShi-1 ChengShi-1 added Size: 30 A percentage of a sprint. 21 hours. (formerly size:33) SPA These changes are required for the Dataverse SPA Feature: API Type: Feature a feature request labels Oct 27, 2026
@coveralls

coveralls commented Oct 27, 2026 •

Copy link
Copy Markdown

Coverage Status

Coverage is 25.904% — spa-builtin-user-signup into develop. No base build found for develop.

@github-actions

This comment has been minimized.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Test Results

418 tests  +1   404 ✅ +1   23m 53s ⏱️ - 8m 1s
 59 suites ±0    14 💤 ±0 
 59 files   ±0     0 ❌ ±0 

Results for commit dff3a38. ± Comparison against base commit 3cbdeab.

♻️ This comment has been updated with latest results.

Cover POST /api/users/signup and the ORCID sign-up endpoints with
Mockito tests, and restore the thread's interrupt flag when the ORCID
token exchange is interrupted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

This comment has been minimized.

@ChengShi-1 ChengShi-1 added this to the 6.13 milestone Oct 29, 2026
…ault

dataverse.api.signup.orcid-redirect-uris is now only needed when the
SPA's sign-up page is somewhere else. Also tidy the sign-up docs and
formatting in Users.java.
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

…dbox

An installation can reuse its existing production ORCID client by
registering the SPA's sign-up URL as a redirect URI. Development
installations using the sandbox register 127.0.0.1 instead of localhost.
@github-actions

This comment has been minimized.

@ChengShi-1
ChengShi-1 marked this pull request as ready for review November 3, 2026 16:06
@ChengShi-1 ChengShi-1 moved this from In Progress 💻 to Ready for Review ⏩ in IQSS Dataverse Project Nov 3, 2026
@ChengShi-1 ChengShi-1 removed their assignment Nov 3, 2026
ChengShi-1 added a commit to IQSS/dataverse-client-javascript that referenced this pull request Nov 3, 2026
The sign-up endpoints aren't in the unstable image yet. Switch back to
docker.io/unstable once #12858 is merged.
@sonarqubecloud

sonarqubecloud Bot commented Nov 3, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

📦 Pushed preview images as

ghcr.io/gdcc/dataverse:spa-builtin-user-signup
ghcr.io/gdcc/configbaker:spa-builtin-user-signup

🚢 See on GHCR. Use by referencing with full name as printed above, mind the registry name.

ChengShi-1 added a commit to IQSS/dataverse-frontend that referenced this pull request Nov 3, 2026
Sets up the backend from IQSS/dataverse#12858 with Maven and this
branch's dev environment, with a checklist for testers. To be deleted
before merging.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature: API Size: 30 A percentage of a sprint. 21 hours. (formerly size:33) SPA These changes are required for the Dataverse SPA Type: Feature a feature request

Projects

Status: Ready for Review ⏩

Development

Successfully merging this pull request may close these issues.

Feature Request: Sign up builtin users via API, with optional ORCID iD

2 participants