LDAP, SAML, OIDC, External Identity, Credential Rotation, and Authentication Troubleshooting: Guided Hands-On Workflow and Core Operations
Inspect a disposable Nexus instance, model LDAP/SAML/OIDC identities with local fixtures, prove group-to-role mapping, and rehearse credential and certificate rotation without requiring an enterprise IdP.
Learning objectives
- Inspect edition and external-realm availability without changing it.
- Create synthetic LDAP, SAML, and OIDC identity fixtures safely.
- Map external groups/claims to narrow Nexus role IDs.
- Simulate bind-secret and certificate rotation with no live enterprise dependency.
- Interpret failed-login evidence and choose the smallest correction.
Safety invariant: the local recovery path remains untouched throughout the mandatory lab; the real Nexus external-realm configuration is inspected read-only while LDAP, SAML, and OIDC behavior is modeled with synthetic fixtures.
Lab scope. Mandatory work is Community/free-compatible. The running Nexus instance is used read-only for identity-state inspection. LDAP/SAML/OIDC records are local synthetic fixtures under a temporary directory. No corporate directory, IdP, DNS, TLS, database, blob store, or production realm is modified.
1. Preflight and assumptions
Reference baseline: self-hosted Nexus Repository 3.95.0, Java 21, loopback access, disposable H2 for a learning instance only. Production deployments should follow current Sonatype PostgreSQL guidance. Record the actual version/edition first.
export NX_URL="http://127.0.0.1:8081"
export LAB="$(mktemp -d)"
mkdir -p "$LAB/evidence" "$LAB/fixtures" "$LAB/certs"
chmod 700 "$LAB"
export NX_USER="admin"
read -r -s -p 'Disposable Nexus admin password: ' NX_PASS; echo
export NX_AUTH_FILE="$(mktemp)"; chmod 600 "$NX_AUTH_FILE"
printf 'machine 127.0.0.1 login %s password %s
' "$NX_USER" "$NX_PASS" > "$NX_AUTH_FILE"
unset NX_PASS
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/status" > "$LAB/evidence/status.txt"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/system/license" > "$LAB/evidence/license.json" || true
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/realms/active" > "$LAB/evidence/realms-before.json"
curl -fsS --netrc-file "$NX_AUTH_FILE" "$NX_URL/service/rest/v1/security/roles" > "$LAB/evidence/roles-before.json"
2. Inspect LDAP/SAML/OIDC availability before assuming it
Open Settings → Security → Realms. On Community, LDAP should be an available supported external identity source; SAML/OIDC self-hosted configuration is a Pro boundary. On a Pro disposable instance, SAML appears under Security and OIDC/OAuth2 configuration is available on supported versions.
Use the feature matrix and the running license state as evidence. Do
not infer entitlement merely because a documentation page or REST
path exists. For OIDC, current Security Management API documentation
lists
GET/PUT/DELETE /service/rest/v1/security/oauth2 for
self-hosted Pro.
3. Build one identity story in three protocol shapes
The same person and group can appear differently in each protocol.
Our synthetic principal is ada; the external group is
nexus-academy-readers; the intended Nexus role ID is
academy-ch16-reader.
cat > "$LAB/fixtures/ldap.json" <<'JSON'
{
"bind_dn": "cn=nexus-reader,ou=svc,dc=example,dc=invalid",
"user_search_base": "ou=people,dc=example,dc=invalid",
"user": {
"dn": "uid=ada,ou=people,dc=example,dc=invalid",
"uid": "ada",
"mail": "ada@example.invalid",
"groups": ["nexus-academy-readers"]
}
}
JSON
cat > "$LAB/fixtures/saml.json" <<'JSON'
{
"subject": "ada",
"audience": "https://nexus.example.invalid/service/rest/v1/security/saml/metadata",
"acs": "https://nexus.example.invalid/saml",
"attributes": {"groups": ["nexus-academy-readers"]}
}
JSON
cat > "$LAB/fixtures/oidc.json" <<'JSON'
{
"iss": "https://id.example.invalid/realms/academy",
"preferred_username": "ada",
"email": "ada@example.invalid",
"groups": ["nexus-academy-readers"]
}
JSON
cat > "$LAB/fixtures/mapping.json" <<'JSON'
{
"external_group": "nexus-academy-readers",
"nexus_role": "academy-ch16-reader",
"privileges": ["nx-repository-view-raw-academy-ch16-fixture-browse", "nx-repository-view-raw-academy-ch16-fixture-read"]
}
JSON
These files contain no live secret or token. They teach protocol shape: LDAP group membership is discovered through directory search; SAML carries attributes inside an assertion; OIDC carries configured claims inside signed tokens/user info.
4. Validate mapping deterministically
python - <<'PY'
import json, os
from pathlib import Path
root = Path(os.environ['LAB']) / 'fixtures'
ldap = json.loads((root/'ldap.json').read_text())
saml = json.loads((root/'saml.json').read_text())
oidc = json.loads((root/'oidc.json').read_text())
map_ = json.loads((root/'mapping.json').read_text())
want = map_['external_group']
rows = {
'LDAP': ldap['user']['groups'],
'SAML': saml['attributes']['groups'],
'OIDC': oidc['groups'],
}
for proto, groups in rows.items():
mapped = map_['nexus_role'] if want in groups else '<none>'
print(f'{proto}: user=ada groups={groups} -> role={mapped}')
PY
Real Nexus external role mapping performs the same conceptual join: the exact external group/role name must match the configured Nexus mapping. Case, naming, and provider output matter. The mapping grants authorization only after authentication has produced that group.
5. Model LDAP bind → search → groups
flowchart TD N[Nexus LDAP realm] --> B[Bind as configured identity] B --> U[Search user base and filter] U --> G[Resolve group membership] G --> M[Map external group to Nexus role] M --> P[Evaluate repository privilege]
A successful bind proves Nexus can authenticate its directory query identity. It does not prove the end-user base DN, user filter, group base, group object class, member attribute, or username attribute is correct. Current Nexus provides LDAP connection/user/group verification in the LDAP configuration workflow and caches authentication information.
Optional free extension: if you already have a disposable OpenLDAP
container, configure it only on loopback/private networking and use
synthetic DNs such as dc=example,dc=invalid. Do not
connect this course lab to a corporate directory.
6. Simulate LDAP bind-secret rotation
We can model rotation without recording a real password. Store only hashes of two fake randomly generated secret values and validate the phase transition:
OLD_BIND_SECRET="$(python - <<'PY'
import secrets
print(secrets.token_urlsafe(24))
PY
)"
NEW_BIND_SECRET="$(python - <<'PY'
import secrets
print(secrets.token_urlsafe(24))
PY
)"
export OLD_BIND_HASH="$(printf '%s' "$OLD_BIND_SECRET" | sha256sum | awk '{print $1}')"
export NEW_BIND_HASH="$(printf '%s' "$NEW_BIND_SECRET" | sha256sum | awk '{print $1}')"
unset OLD_BIND_SECRET NEW_BIND_SECRET
printf 'old_hash=%s
new_hash=%s
' "$OLD_BIND_HASH" "$NEW_BIND_HASH" > "$LAB/evidence/bind-rotation-hashes.txt"
In a real rotation, change the directory-side service credential and Nexus configuration in a staged sequence that preserves a testable overlap if your directory policy allows it. Verify bind, user search, and group resolution after the cutover. The evidence packet should prove which version/phase succeeded—not contain either secret.
7. Simulate certificate rollover
openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj '/CN=id-old.example.invalid' -keyout "$LAB/certs/old.key" -out "$LAB/certs/old.crt" >/dev/null 2>&1
openssl req -x509 -newkey rsa:2048 -nodes -days 1 -subj '/CN=id-new.example.invalid' -keyout "$LAB/certs/new.key" -out "$LAB/certs/new.crt" >/dev/null 2>&1
openssl x509 -in "$LAB/certs/old.crt" -noout -fingerprint -sha256 > "$LAB/evidence/cert-old.txt"
openssl x509 -in "$LAB/certs/new.crt" -noout -fingerprint -sha256 > "$LAB/evidence/cert-new.txt"
For LDAPS, current Nexus can trust certificates added through the Nexus SSL Certificates UI; new certificates added there begin being used for outbound connections without a Nexus restart. For SAML, signing-key renewal requires updated IdP metadata. For OIDC, JWK rotation is normally discovered through the provider's configured key endpoint; private-CA HTTPS may require the Nexus truststore on supported Pro versions.
8. Create failure evidence, not mystery symptoms
cat > "$LAB/fixtures/failures.log" <<'LOG'
LDAP bind=success user_search=0 base="ou=users,dc=wrong,dc=invalid" user="ada"
SAML idp_login=success audience="https://wrong.example.invalid" acs="https://nexus.example.invalid/saml" cookie=SAML2_AUTH_REQUEST:missing
OIDC issuer="https://id.example.invalid/realms/wrong" redirect="https://nexus.example.invalid/oidc/callback" groups_claim="roles" observed_groups=[]
HTTP status=429 retry_after=30 auth=basic principal="ch16-fixture-user"
LOG
cp "$LAB/fixtures/failures.log" "$LAB/evidence/failures.log"
The first line is not an LDAP network failure: bind worked, then search found no user. The SAML line tells you to investigate audience/ACS/cookie state, not repository privileges. The OIDC line points at issuer and claim mapping. The 429 line is authentication throttling, not TCP outage.
9. Challenge: choose the control
A CI pipeline must publish packages noninteractively, while developers should use browser SSO for the Nexus UI. Which control should you change?
Do not broaden a browser SSO session into CI. Keep human SSO and package-client credentials separate. On Community, use a dedicated local or LDAP-backed service identity with narrow repository privileges. On Pro, a supported User Token may be appropriate for a SAML-authenticated user, but it still carries that principal's Nexus authorization.
10. Cleanup
rm -f "$NX_AUTH_FILE"
unset NX_AUTH_FILE NX_USER OLD_BIND_HASH NEW_BIND_HASH
rm -rf "$LAB"
The lab never mutated external-realm configuration, so there is no Nexus realm rollback. If you chose the optional live LDAP extension, remove only the disposable LDAP server/configuration you created and confirm the original active-realm list is restored exactly.
11. Knowledge check
Why is a synthetic identity fixture useful?
It lets you learn protocol objects, group mapping, rotation, and failure diagnosis without needing or risking an enterprise identity provider.
What does a successful LDAP bind fail to prove?
That end-user search and group membership resolution are configured correctly.
What should a credential-rotation evidence packet contain?
Phase/result identifiers and non-secret fingerprints/hashes—not the old or new credential value.
If a SAML user can sign in but cannot read a repository, which layer is next?
External group/role mapping and Nexus privileges, because authentication has already succeeded.
Why should the lab not enable SAML/OIDC on Community?
Self-hosted SAML and OIDC are Pro-only; the mandatory path must remain Community/free-compatible.
12. Next step
Lesson 3 turns the mechanics into architecture decisions: which people use centralized SSO, which service identities remain noninteractive, how group mapping affects coupling, how to fail closed, and how to preserve recovery without leaving a daily-use admin backdoor.
Official references and version notes
- Sonatype: Authentication and Realms.
- Sonatype: LDAP — bind/search, user/group mapping, LDAP cache, LDAPS trust.
- Sonatype: SAML — Pro-only browser SSO, metadata, ACS, signing, external role mapping.
- Sonatype: OpenID Connect — self-hosted Pro from 3.86, claim mapping, OAuth2 realm, truststore support.
- Sonatype: Security Management API — users, roles, privileges, realms and Pro OIDC configuration endpoints.
- Sonatype: Authentication Attempt Rate Limiting.
- Self-Hosted Nexus Repository Feature Matrix — LDAP Community/Pro; SAML and User Tokens Pro.
- Sonatype: Download and Java Runtime Compatibility Matrix.
Version-sensitive statements were rechecked on 2026-08-26. The current official direct-download page lists Nexus Repository 3.95.0. Nexus 3.87+ on H2/PostgreSQL requires Java 21. The mandatory learning path is Community-compatible and models external identity with local fixtures; SAML and OIDC are optional Pro-only integrations for self-hosted Nexus.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.