Skip to content

Allow signing in to the UI through the NDP identity provider#194

Merged
rbardaji merged 4 commits into
mainfrom
feature/193-oidc-login
Jul 20, 2026
Merged

Allow signing in to the UI through the NDP identity provider#194
rbardaji merged 4 commits into
mainfrom
feature/193-oidc-login

Conversation

@rbardaji

Copy link
Copy Markdown
Collaborator

Closes #193

What this adds

A third way into the UI: a button that sends the user to the identity provider's own login page, where the federated providers configured on the realm (CILogon, EarthScope, ORCID) are offered.

This matters because users arriving with institutional credentials have no password in the realm, so the username/password form never worked for them. Their only route in was to sign in on nationaldataplatform.org, find their access token and paste it by hand.

The access token and username/password methods are unchanged and remain available.

How it works

OAuth Authorization Code flow with PKCE (S256) against a public client, so no client secret has to be distributed with the Endpoint, which is self-hosted by many institutions. The resulting token is handed to the existing setAndValidateToken path, so validation, storage and the access-denied (403) request-access flow behave exactly as they do for the other methods.

Configuration

Off by default. Existing deployments are unaffected until they opt in.

Variable Default
OIDC_ENABLED False
OIDC_ISSUER
OIDC_CLIENT_ID
OIDC_SCOPE openid profile email
OIDC_BUTTON_LABEL Sign in with your identity provider
OIDC_HELP_TEXT empty

Nothing about a particular identity provider is baked into the build: endpoints come from the realm's OpenID discovery document, the callback URL is derived from the browser's address and ROOT_PATH, and the button wording is deployment-supplied. Moving from a local Keycloak to production is a change of OIDC_ISSUER.

Switching it on without both OIDC_ISSUER and OIDC_CLIENT_ID renders no button, rather than one that fails on click.

Verified

Tested end to end against a Keycloak realm: sign-in on the provider's page, callback, PKCE code exchange, token validation via AUTH_API_URL, and /user/info returning the user's real roles and groups.

  • Python suite: 1154 passed
  • UI suite: 30 passed (22 of them new, covering the configuration gate, callback detection and the PKCE refusal path)
  • black --check and flake8 clean

App.test.js fails on this branch, but it fails identically on main — it is a pre-existing module-resolution problem, untouched here.

Notes for reviewers

Two failure modes cost real time to diagnose and are now documented in example.env and docs/configuration.md:

  1. A client that mints tokens without a sub claim. AUTH_API_URL looks the user up by sub. Since Keycloak 24 that claim comes from the built-in basic client scope, and a realm imported from an older export may not have it among its default scopes — so a freshly created client silently issues tokens without sub and sign-in fails at the last step.
  2. OIDC_ISSUER and AUTH_API_URL pointing at different identity providers. The token is minted by one and validated by the other; if they disagree the user's sub does not exist on the validating side.

PKCE needs crypto.subtle, which browsers expose only in secure contexts. On a plain-http or bare-IP deployment the button is shown disabled with an explanation rather than silently downgrading to the weaker plain challenge method; the other two sign-in methods still work there.

Raul Bardaji added 4 commits July 20, 2026 03:19
Users who authenticate through a federated provider configured in the
realm (CILogon, EarthScope, ORCID) have no password in the realm, so the
username/password form cannot work for them. Their only route in was to
sign in on nationaldataplatform.org, find their access token and paste it
by hand.

Add a third way in that sends the user to the identity provider's own
login page, where those providers are offered. The Authorization Code
flow with PKCE is used against a public client, so no client secret has
to be shipped with the Endpoint.

The resulting token is handed to the existing setAndValidateToken path,
so validation, storage and the access-denied (403) request-access flow
behave exactly as they do for the other methods.

The button is rendered only when OIDC_ISSUER and OIDC_CLIENT_ID are both
set; deployments that leave them empty are unchanged. PKCE needs
crypto.subtle, which browsers expose only in secure contexts, so over
plain http the button is disabled with an explanation rather than
silently downgrading to the weaker plain challenge method.

Refs #193
After the identity provider authenticated the user, any non-403 failure
was surfaced through the shared "Invalid token: Authentication failed"
wording, which reads as mistyped credentials. It is not: the sign-in
succeeded and the Endpoint could not validate the resulting token.

Seen in practice when the client mints tokens without a `sub` claim:
AUTH_API_URL looks the user up by `sub`, returns 500, and the Endpoint
turns that into a 401. Document the claim as a client requirement so the
next deployment does not have to diagnose it from scratch.

Refs #193
Put the feature behind OIDC_ENABLED, defaulting to False, so a deployment
that says nothing keeps the login screen it already had. Switching it on
without OIDC_ISSUER and OIDC_CLIENT_ID shows no button rather than one
that fails the moment it is clicked.

Move the button wording out of the build. It named the National Data
Platform and its three federated providers, which is wrong for any other
realm and for a realm that has none configured. OIDC_BUTTON_LABEL and
OIDC_HELP_TEXT now supply it, defaulting to provider-neutral wording and
to no second line at all, since only the deployment knows what its realm
offers.

Nothing about a particular identity provider is left in the build: the
endpoints come from the realm's discovery document and the callback URL
is derived from the browser's address and ROOT_PATH, so moving from a
local Keycloak to production is a change of OIDC_ISSUER.

Document the whole thing in example.env and docs/configuration.md,
including what the identity provider administrator has to register and
the two failure modes that cost real time to diagnose: a client that
mints tokens without a sub claim, and OIDC_ISSUER disagreeing with
AUTH_API_URL.

Refs #193
@rbardaji
rbardaji merged commit 5951dd0 into main Jul 20, 2026
1 check passed
@rbardaji
rbardaji deleted the feature/193-oidc-login branch July 20, 2026 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow signing in to the UI through the NDP identity provider

1 participant