Skip to content

DRAPI OIDC certificate trust: reproduce & diagnose

Key finding: DRAPI (Java) trusts an external IdP's certificate via its own JVM truststore (<Domino>\jvm\lib\security\cacerts) — not certstore.nsf, not the Windows cert store, not the chain the IdP sends.

Background

Scenario: DRAPI integrates with an external IdP (ADFS here, HTTPS); after entering the password at the IdP and being redirected back to DRAPI, you get Error fetching token. Please try again.

Symptom: Error fetching token

To pin down the root cause, we reproduced the same class of "DRAPI doesn't trust the IdP cert" issue locally using a self-signed HTTPS Keycloak.


Key concept: two trust layers, used at different steps

Trust layer When Failure symptom
Browser trusts the IdP front-end redirect to the IdP, front-end fetches metadata redirect blocked / Error initiating authorization request
DRAPI (Java) trusts the IdP backend fetch metadata, exchange token, fetch JWKS provider fails to load / Error fetching token
  • The user can "reach the ADFS password page" → browser layer OK (corporate machines trust ADFS).
  • But after returning, Error fetching tokenthe DRAPI (Java) layer doesn't trust it ← this is what to fix.

Reproduction (local Keycloak)

1. Make Keycloak serve HTTPS with a self-signed cert

On the Keycloak host (WSL):

ip=$(hostname -I | awk '{print $1}')
openssl req -x509 -newkey rsa:2048 -nodes -keyout kc-key.pem -out kc-cert.pem \
  -days 825 -subj "/CN=keycloak-test" -addext "subjectAltName=IP:$ip"
bin/kc.sh start-dev \
  --https-certificate-file=$HOME/kc-cert.pem \
  --https-certificate-key-file=$HOME/kc-key.pem

2. Prove "not trusted" (reproduce the PowerShell behavior)

From the DRAPI host:

curl   https://<keycloak>:8443/realms/drapi/.well-known/openid-configuration  → HTTP 000 (fails, not trusted)
curl -k https://<keycloak>:8443/realms/drapi/.well-known/openid-configuration  → HTTP 200 (only passes when skipping verification)

This maps to "PowerShell curl fails, curl.exe works" — different tools use different trust mechanisms, which is misleading.

3. Point DRAPI at the HTTPS Keycloak → reproduce the failure

Change providerUrl in keepconfig.d to https://<keycloak>:8443/realms/drapi, restart DRAPI. Check …/api/v1/auth/idpList?configFor=adminui: - The provider disappears from the list (only the built-in DRAPI remains) → on startup DRAPI can't fetch the https metadata (Java doesn't trust it). - Forcing a login gets stuck at the token exchange → the same Error fetching token.


Fix (verified locally)

A. (key) Import the IdP's CA into DRAPI's JVM cacerts

# Use Domino's bundled keytool to import into the cacerts of the JVM DRAPI runs (default password changeit)
"<Domino>\jvm\bin\keytool" -import -trustcacerts -alias keycloak-test \
  -file kc-cert.pem \
  -keystore "<Domino>\jvm\lib\security\cacerts" -storepass changeit -noprompt
Restart DRAPI → keycloak-drapi comes right back into idpList (with its https wellKnown) → proving DRAPI uses exactly this truststore.

A self-signed cert is its own root; in production (e.g. an ADFS internal CA) import both root + intermediate (use distinct aliases).

B. Browser layer (only needed for self-signed; the user usually already has this)

A self-signed cert isn't trusted by the browser either → import into the OS trusted roots:

certutil -addstore -user -f Root kc-cert.pem

The user's ADFS is already trusted on corporate machines, so this layer is usually fine — they're only missing A.

Result: full login success ✅

Login success (HTTPS)


Does certstore.nsf work? (tested: no)

To check whether we could avoid touching cacerts and use certstore.nsf instead, an isolation test:

  1. load certmgr to create certstore.nsf
  2. Import the same self-signed cert under Trusted Roots (Paste Certificate → whole block incl. BEGIN/END → Submit Request → Issued)
  3. Remove that cert from the JVM cacerts (keytool -delete), leaving only the certstore path
  4. Restart DRAPI → check idpList

Result: keycloak-drapi did not appear → DRAPI does not trust it.

Conclusion: DRAPI's outbound OIDC connection does not read certstore.nsf Trusted Roots. Reason: DRAPI (Java) goes outbound via JSSE, which reads only the JVM truststore; certstore.nsf Trusted Roots feed the Domino C engine's TLS cache (HTTP task, idpcat), which KEEP/DRAPI's outbound path doesn't use. (Note: DRAPI's own HTTPS inbound can use certstore's TLSCertStore — that's a different thing, don't conflate them.)

⚠️ Upgrade note: editing the shipped jvm\lib\security\cacerts gets overwritten on a Domino/JVM upgrade. For production prefer a self-managed truststore + -Djavax.net.ssl.trustStore=... (survives upgrades), or re-do the cacerts import as part of the upgrade process.


Why "I added the intermediate but it still doesn't work"

Most likely one of:

  1. Wrong place: added to certstore.nsf / Windows / the ADFS side, but DRAPI only honors the JVM cacerts. ← prime suspect
  2. Only the intermediate, not the root: keytool's trust anchor is the root CA; without the root, PKIX still can't build a path.
  3. Wrong cacerts / no restart: not the JVM DRAPI runs, or overridden by -Djavax.net.ssl.trustStore; or no restart after importing.

Diagnosis steps

# 1) See the real error (add DEBUG_OIDCLogin=1 to notes.ini, restart, then log in)
#    PKIX / unable to find valid certification path → cert trust (continue below)
#    invalid_client / 401                          → it's a client secret issue instead

# 2) See what certs ADFS actually sends (verify the chain matches what you imported)
"<Domino>\jvm\bin\keytool" -printcert -sslserver <ADFS-host>:443

# 3) Import ADFS's "root + intermediate" into DRAPI's JVM cacerts, restart
"<Domino>\jvm\bin\keytool" -import -trustcacerts -alias adfs-root \
  -file adfs-root.cer -keystore "<Domino>\jvm\lib\security\cacerts" -storepass changeit

Wording note: on "Domino 12.0.2 + DRAPI 1.1.7" we verified the JVM cacerts approach works. So the safest advice is "make sure ADFS's CA (root + intermediate) is in DRAPI's JVM truststore, then restart".


One-line summary

Reaching the password page but getting Error fetching token = the browser trusts ADFS, but DRAPI (Java) doesn't. The fix is to import ADFS's root + intermediate CA into DRAPI's JVM cacerts and restart — don't just drop it into certstore/Windows.