View Source Contributing to Ueberauth Orcid
Release status
The repository targets 0.3.0. Registered ORCID sandbox authorization and
denial passed before release without recording secrets. The GitHub workflow,
public tag, Hex package and HexDocs must still be verified for the exact release
commit. Application-specific staging remains each consumer's responsibility.
Toolchains and quick start
The library retains its Elixir ~> 1.14 requirement; the default Httpc TLS
implementation requires OTP 25.1 or later
(native TLS API).
The checked-in CI workflow
defines these explicit
compatible Elixir/OTP pairs,
not every combination:
| Purpose | Elixir build | Erlang/OTP |
|---|---|---|
| Compatibility floor | 1.14.5-otp-25 | 25.3.2.21 |
| Existing consumer baseline | 1.18.4-otp-27 | 27.3.4 |
| Current stable / canonical development | 1.20.4-otp-27 | 27.3.4 |
Elixir 1.20.4 was the current stable release when checked on 2026-09-18. OTP 27 is supported by both 1.18 and 1.20. The floor preserves consumer compatibility; it is not a recommendation to deploy an end-of-life release.
Install mise, then run these commands
from the repository root. .tool-versions selects the canonical pair locally;
there is no need to change global defaults:
mise install
mise exec -- mix local.hex 2.5.1 --force
mise exec -- mix local.rebar --force
export MIX_ENV=test
mise exec -- mix deps.get --check-locked
mise exec -- mix compile --warnings-as-errors
mise exec -- mix format --check-formatted
mise exec -- mix test --warnings-as-errors --cover
CI installs Rebar through setup-beam, explicitly installs/logs Hex 2.5.1,
and uses MIX_ENV=test on Ubuntu 24.04. Formatting runs in the canonical quality
job; coverage runs in the canonical test job. The other test jobs run compile
and test commands without --cover; no coverage service or extra dependency is
needed. The built-in coverage threshold is not lowered.
Use separate clean source directories for each matrix pair, with no copied
_build, deps, or priv/plts artifacts. Never share compiled dependencies
between OTP versions. From the respective clean source roots:
mise install erlang@25.3.2.21 elixir@1.14.5-otp-25
MIX_ENV=test mise exec erlang@25.3.2.21 elixir@1.14.5-otp-25 -- mix deps.get --check-locked
MIX_ENV=test mise exec erlang@25.3.2.21 elixir@1.14.5-otp-25 -- mix compile --warnings-as-errors
MIX_ENV=test mise exec erlang@25.3.2.21 elixir@1.14.5-otp-25 -- mix test --warnings-as-errors
mise install erlang@27.3.4 elixir@1.18.4-otp-27
MIX_ENV=test mise exec erlang@27.3.4 elixir@1.18.4-otp-27 -- mix deps.get --check-locked
MIX_ENV=test mise exec erlang@27.3.4 elixir@1.18.4-otp-27 -- mix compile --warnings-as-errors
MIX_ENV=test mise exec erlang@27.3.4 elixir@1.18.4-otp-27 -- mix test --warnings-as-errors
Install the pinned Hex/Rebar as in quick start under each selected toolchain. Use the quick-start commands for the canonical pair.
Tests use synthetic identities and the installed HTTP client's test adapter.
The TLS regression uses a loopback-only server and ephemeral certificates
generated by OTP; no external service, OpenSSL executable, or ORCID credentials
are needed after dependency installation.
Tests changing application environment must use async: false and restore
every changed key in on_exit. A passing offline suite is not proof of a live
ORCID login or consumer deployment.
Quality checks
Analysis and documentation tools are development-only, non-runtime dependencies. Run them on the canonical pair, not the compatibility floor:
export MIX_ENV=dev
mise exec -- mix deps.get --check-locked
mise exec -- mix credo --strict
mise exec -- mix dialyzer
mise exec -- mix sobelow --private --exit low
mise exec -- mix docs
mise exec -- mix hex.build
Dialyzer includes the project's transitive runtime applications. PLTs live in
priv/plts/; CI caches only this directory, keyed by OS, architecture, Elixir,
OTP, environment, and lockfile. A cache miss builds from scratch. Dependencies,
build artifacts and the Hex home are not cached. No release job consumes PR
artifacts; publication remains manual.
Sobelow's threshold is confidence, not vulnerability severity. --exit low
fails for every reported confidence level; its default exit behavior would not
gate CI. --private disables update checks. Sobelow complements, but does not
replace, the OAuth regression tests, Hex advisories or a security review. This
library has no Phoenix router, so Sobelow's missing-router notice is expected.
Its generic code-execution checks were exercised with an isolated injected
finding: the normal scan exited 0 and the injected scan exited 1.
ExDoc stays on the 0.36.x line compatible with the package's Elixir floor.
Its older dependencies emit range, bitstring, API-deprecation and inferred-type
warnings on Elixir 1.20; these are development tooling, not runtime dependencies.
The floor-compatible locked Plug 1.19.5 also emits xref, bitstring and an
inferred unreachable-clause warning on 1.20. Both older test pairings compile
without these warnings. Mix's --warnings-as-errors gates the project's
compilation and tests, not dependencies' compilation. No global warning flags,
ignore files or allowed-failing jobs hide these upstream findings.
SOURCE_REF selects the source revision for generated documentation; CI uses
its actual commit SHA. Release documentation must use the actual 0.3.0
tag, not a fabricated v prefix. That tag does not exist yet: use a real
commit SHA for preview links and do not treat the default version-based
source links as validated until the release tag is public. mix hex.build
builds locally; it does not publish.
CI runs on pushes to master and pull_request, including fork contributions,
using a read-only token, no repository secrets, and full action commit pins.
Review upstream release notes and action inputs before changing those pins.
Hosted CI results must be checked on GitHub; local results do not establish
that a remote run has passed.
Dependency maintenance
Run with the pinned Hex version; older Hex releases checked retirement only:
mise exec -- mix hex.audit
mise exec -- mix hex.outdated --all
hex.audit is a required CI job on PRs, pushes to master, manual dispatch,
and a weekly Tuesday schedule. Scheduled runs only audit; they do not repeat
the test matrix. A finding fails the job. Enable GitHub Actions failure
notifications for this repository; this workflow does not automatically open
issues. Scheduled jobs and Dependabot activate after these files reach the
default branch.
Dependabot proposes monthly Mix updates in separate development/runtime groups (at most two open PRs), plus one grouped GitHub Actions PR. There is no auto-merge. GitHub supports Mix version updates, not Mix security-update PRs, so scheduled Hex auditing remains necessary.
The locked baseline is authoritative. An unlocked/latest-compatible CI job is not enabled: it would test dependency resolution rather than this lockfile. Reconsider it after the expanded matrix has a stable hosted history.
The stable merge-check names are test floor, test consumer, test current,
quality, and audit. Approved master protection requires all five from
the GitHub Actions app, an up-to-date branch, and applies to administrators.
Force pushes and deletion are disabled. When changing job names, update
protection in the same rollout. Until the workflow is pushed, these new checks
cannot run on GitHub and PRs cannot satisfy the protection rule.
The lockfile uses Plug 1.19.5 to exercise the Elixir 1.14 floor. Published requirements also permit patched newer branches; do not collapse the branch-aware constraint to a single lower bound that re-admits vulnerable 1.17–1.20 releases. When updating, reproduce the login contract after each runtime group, check all supported pairs, and check a production-only consumer. Applications resolve their own dependencies; this repository's lockfile is not distributed as their resolution. Re-audit the application before rollout.
Review advisory notifications weekly and investigate failures promptly; review the monthly update PRs rather than auto-merging. At least quarterly, triage open issues, upstream deprecations and supported runtime pairs. Before every authentication-affecting library release, repeat the sandbox protocol below. Consumers should separately validate upgrades in their own staging environment.
Manual acceptance: library sandbox and downstream consumers
This requires a maintainer's explicit approval, sandbox credentials, consenting
test accounts and authorized access to staging. Missing access is a blocked
check, never a pass. Use the ORCID sandbox
and the README's sandbox configuration; sandbox and production credentials,
hosts and records are separate. Register the exact callback, keep HTTPS/TLS
verification enabled, and keep config :oauth2, debug: false. Do not put
credentials in commands, tickets or screenshots.
Record the candidate source SHA, package checksum, runtime pair, callback origin/path, requested/granted scope names, test time and pass/fail outcomes. Use synthetic account labels; never attach codes, state cookies, tokens, client secrets, full callbacks, auth structs or provider response bodies.
- Start login through the application's Ueberauth request route, not a handcrafted provider URL. Complete consent and check that the callback authenticates the intended test account. The UID must remain the bare ORCID subject (not an ORCID URL, email or display name).
- Verify the registered callback equals both the authorization and token
exchange callback. Keep the default
openid email profilescope unchanged; a request'sscopeoverrides it. Inspect granted scopes separately: absence means[], not the requested permissions. - Deny consent. Expect a controlled failure, no authenticated session and no account/token writes. Repeat with a fresh flow and missing/mismatched state or missing state cookie: rejection must occur before token exchange. Replay a consumed callback: it must not authenticate again.
- Use a minimal/private sandbox profile. Missing names must not crash;
info.name,info.nicknameandinfo.emailmay be nil. Email remains unpopulated by this strategy; application email collection is separate. If a live record cannot exercise a case, record it as unexercised and use the deterministic regression separately, not as a claimed live pass. - Check application logs, errors and telemetry through approved redacted
views. Only safe failures should be emitted. Temporary
:orcid_userand:orcid_tokenfields must be cleaned; successful credentials andextra.raw_info.tokenintentionally still contain secrets. Never log them.
After publication, consuming applications should validate the exact release in their own authorized staging environments:
- Existing identities still resolve to the same account without duplicates.
- Fresh identities follow the application's intended registration flow.
- Denial and invalid state create no account, session or token writes.
- Missing optional profile fields follow documented application behavior.
- Token rotation, granted scopes and optional refresh tokens satisfy the application's persistence constraints.
Downstream rollout and rollback
Before adopting a new release, applications should record their current dependency requirement, complete lockfile, runtime pair, deployment artifact and configuration revision without recording secret values. Validate the exact new resolution in staging.
Rollback should redeploy the recorded prior artifact with its compatible configuration. If rebuilding is unavoidable, restore the complete lockfile and dependency requirement in a separate checkout; changing only this package's lock entry can produce an untested transitive resolution. Re-run identity, denial and duplicate-account checks after rollback.
Manual release checklist
0.3.0, rather than a 0.2.x patch, signals the breaking full custom-OAuth
interface, explicit callback/configuration requirements and effective OTP
floor. See CHANGELOG.md. There is no automatic release job.
[ ] Review a clean candidate source snapshot and its complete diff; exclude secrets, build artifacts and unrelated consumer changes. Run the three isolated matrix pairs, canonical quality commands and Hex audit above. Check hosted results for the exact reviewed SHA after the workflow reaches GitHub; all five required checks must pass there, not only locally.
[ ] Inspect ExDoc, every example and package contents/metadata. Build the actual tarball and install its extracted contents in a fresh production-only consumer, not a path pointing at the working source. For example, from the candidate repository root on the canonical pair:
MIX_ENV=dev mise exec -- mix hex.build shasum -a 256 ueberauth_orcid-0.3.0.tarExtract and inspect that exact tarball, then create a fresh consumer:
release_check=$(mktemp -d) mkdir "$release_check/archive" "$release_check/package" tar -xf ueberauth_orcid-0.3.0.tar -C "$release_check/archive" tar -xzf "$release_check/archive/contents.tar.gz" -C "$release_check/package" export ORCID_PACKAGE_PATH="$release_check/package" mise exec -- mix new "$release_check/orcid_release_consumer"In the generated consumer's
mix.exs, make itsdeps/0return exactly[{:ueberauth_orcid, path: System.fetch_env!("ORCID_PACKAGE_PATH")}]. Continue in that shell from the consumer root (not the package source):cd "$release_check/orcid_release_consumer"MIX_ENV=prod mise exec erlang@27.3.4 elixir@1.20.4-otp-27 -- mix deps.get MIX_ENV=prod mise exec erlang@27.3.4 elixir@1.20.4-otp-27 -- mix compile --warnings-as-errors MIX_ENV=prod mise exec erlang@27.3.4 elixir@1.20.4-otp-27 -- mix hex.audit MIX_ENV=prod mise exec erlang@27.3.4 elixir@1.20.4-otp-27 -- mix run -e 'json = Ueberauth.json_library(); %{"sub" => "0000-0002-1825-0097"} = json.decode!(~s({"sub":"0000-0002-1825-0097"})); false = Code.ensure_loaded?(Credo); false = Code.ensure_loaded?(ExDoc)'Also exercise the documented Ueberauth flow with the actual consumer wrapper and a synthetic provider, checking success and controlled failure. Repeat production consumption on the floor in a separate clean consumer using its supported pair and compatible dependency resolution. The JSON smoke alone is not authentication acceptance.
[ ] Obtain and record the registered sandbox acceptance above. Downstream application staging and rollback remain the adopting application's gate.
[ ] Obtain explicit maintainer authorization for commit, push, tag and publication. Finalize the release date/changelog only then, commit the approved source, and create/push the exact
0.3.0tag pointing to that commit (novprefix). Confirm the tag exists publicly.[ ] From a clean checkout of that tag, repeat local package/quality checks and rebuild/review docs with
SOURCE_REF=0.3.0. With authorized Hex credentials, publish manually, preserving the confirmation prompt:SOURCE_REF=0.3.0 MIX_ENV=dev mise exec -- mix docs SOURCE_REF=0.3.0 MIX_ENV=dev mise exec -- mix hex.build SOURCE_REF=0.3.0 MIX_ENV=dev mise exec -- mix hex.publishHex publishing publishes package and docs;
hex.buildalone does not. Do not publish from PR artifacts or expose publishing credentials to untrusted code.[ ] Verify
https://hex.pm/packages/ueberauth_orcid/0.3.0,https://hexdocs.pm/ueberauth_orcid/0.3.0/, the GitHub tag/source links and README badge destinations. In another fresh production consumer, replace the path dependency with{:ueberauth_orcid, "~> 0.3.0"}, fetch, compile, audit and repeat the smoke. Add the CI badge once the hosted workflow exists on the default branch.
Record evidence and explicit maintainer approval per release. Never infer deployment or publication authorization from local verification alone.
Pull Requests Welcome
- Fork ueberauth_orcid
- Create a topic branch
- Make logically-grouped commits with clear commit messages
- Push commits to your fork
- Open a pull request against ueberauth_orcid/master
Issues
If you believe there to be a bug, please provide the maintainers with enough detail to reproduce or a link to an app exhibiting unexpected behavior. For help, please get in touch!