nebari-dev / nebari-dev/data-science-pack

Security(M-02): authorization revocation fails open on Keycloak refresh/Admin API errors

Open
#170 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area: security priority: medium ⚡
Dominant language
Python
Stars
5
Forks
7
Avg merge
1d 20h
Merged PRs (30d)
13

Description

Summary

Authorization revocation fails open during Keycloak errors. A transient refresh failure keeps the current auth state, and a failed Admin API query after a successful token refresh preserves the previous mount and profile grants. There is no maximum age on stale grants, so a revoked user can retain access for as long as the Admin API keeps erroring.

Severity: Medium · CWE-754 (Improper Handling of Exceptional Conditions)
Validation: Confirmed against HEAD f932d80 on 2026-07-14 (assessed at 69c84f7; unchanged since).

Evidence

  • config/jupyterhub/00-gateway-auth.py:359-363: on a transient refresh error, refresh_user logs and return True, keeping the current auth state. Only 400 invalid_grant returns False and forces re-login (00-gateway-auth.py:353-358).
  • After a successful refresh, a failed group query preserves the old set: 00-gateway-auth.py:387-396 restores groups_with_permission_to_mount; 00-gateway-auth.py:406-414 does the same for allowed_jupyterlab_profiles.
  • Initial login fails closed: update_auth_model sets filtered = [] on Admin API failure (00-gateway-auth.py:445-452) and _resolve_allowed_profiles returns [] on failure (00-gateway-auth.py:472-478).
  • No max stale-grant age: refresh_user has no timestamp or age comparison; preservation is unconditional on old_* is not None.

Impact

A user removed from a shared-storage role or a restricted profile keeps that access across every subsequent refresh while the Keycloak Admin API is unavailable. The initial-login path is safe; the gap is mid-session refresh.

Remediation

  • Timestamp the last successful authorization evaluation.
  • Apply a short maximum age for sensitive mounts and profiles, and fail closed past that age while still allowing lower-risk login where appropriate.
  • Consume Keycloak identity events where available.
  • Alert on stale authorization state.

Acceptance criteria

  • Role removal takes effect within a documented interval even during an Admin API outage.
  • Stale authorization age is observable.

Source: data-science-pack 0.1.0 security assessment (pinned commit 69c84f72df259ec755ed40bfc83f20158c550d55), finding M-02.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in config/jupyterhub/00-gateway-auth.py at refresh_user, then trace update_auth_model and _resolve_allowed_profiles, comparing the cited refresh and initial-login error paths. Review how authorization state is stored and evaluated, then use the acceptance criteria to verify bounded stale-grant access and observable authorization age.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
authorization, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.