Skip to content

plugin authn/authz rfc - #19

Merged
mprahl merged 8 commits into
mlflow:mainfrom
PatrickKoss:rfc/enterprise-authn-authz
Sep 2, 2026
Merged

plugin authn/authz rfc#19
mprahl merged 8 commits into
mlflow:mainfrom
PatrickKoss:rfc/enterprise-authn-authz

Conversation

@PatrickKoss

@PatrickKoss PatrickKoss commented May 29, 2026

Copy link
Copy Markdown
Contributor

RFC 0008: Pluggable Authentication and Authorization

Tracking issue: mlflow/mlflow#21240

Summary

Adds a new RFC proposing two small plugin contracts — AuthenticationProvider
and AuthorizationBackend — to replace MLflow's single authorization_function
hook. The split separates who you are from what you may do, and keeps the
load-bearing route → requirement mapping in core so plugins never need to
track MLflow's routing surface.

This is the extension point that RFC 0005 ("Role-Based Access Control for
MLflow OSS") flagged as future work. It builds on 0005's role model and
resolver surface, and the default plugins reproduce today's behavior
byte-for-byte — operators who upgrade and change nothing see no difference.

What's in this PR

  • New file: rfcs/0006-pluggable-auth/0006-pluggable-auth.md (823 lines, one
    commit on top of main).

No code changes, no implementation — this is the design document. Reference
adapters described in the RFC (OIDC, Kubernetes TokenReview /
SubjectAccessReview, OPA, upstream proxy headers) are sketched in enough
detail to validate the interface shape but are not built here.

Why now

The existing surface has three structural problems:

  1. One hook does two jobs. authorization_function returns a
    werkzeug.datastructures.Authorization carrying only a username — too thin
    for bearer tokens, OIDC claims, group membership, or JIT provisioning.
  2. FastAPI silently ignores it. The FastAPI request path refuses any
    non-default function (mlflow/server/auth/__init__.py:4141), so the hook
    is effectively Flask-only.
  3. Route → permission knowledge is fused into ~200 validators across six
    dispatch structures.
    Any external authorization system (Kubernetes SAR,
    OPA, a corporate policy engine) has to rediscover and re-sync that mapping
    every time MLflow adds a route.

Design rule worth calling out

Core retains sole ownership of the route → requirement mapping via a single
authoritative OPERATION_REGISTRY. Plugins only ever see the normalized tuple
(resource_type, resource_id, action, workspace) — never a route, a protobuf
class, or a GraphQL field. A CI guard fails the build if any route ships
without a declared requirement.

Out of scope (intentionally)

  • Changing RFC 0005's role storage or permission levels.
  • New permission semantics beyond READ / USE / EDIT / MANAGE.
  • Multi-tenant data isolation at the storage layer.
  • A built-in policy DSL.

Reviewer guide

Suggested reading order if you're short on time:

  1. Summary + Basic example (lines 15–115) — the operator-facing shape.
  2. Motivation (117–161) — the three structural problems, with file refs.
  3. The three layers (184–211) — the contract boundary in one diagram.
  4. Core keeps owning route → requirement (466–547) — the centerpiece; the
    rest of the design hangs off this.
  5. OPERATION_REGISTRY + CI guard (548–636) — how core stays the source
    of truth as routes evolve.
  6. Drawbacks / Alternatives / Open questions (698–end) — where I'd most
    like pushback.

Open questions I'd like input on

These are spelled out at the bottom of the RFC; flagging them here so they
don't get lost:

  • Whether authn_providers should be an ordered chain or a single provider
    with explicit fallback rules.
  • How fine-grained workspace should be for the Kubernetes SAR adapter
    (namespace? label selector? both?).
  • Whether the CI guard belongs in this RFC or as a follow-up.

Checklist

  • RFC follows 0000-template.md structure
  • start_date set, mlflow_issue linked, rfc_pr left empty per
    template instructions
  • Motivation references concrete code paths in mlflow/server/auth/
  • Builds on (does not contradict) RFC 0005
  • Default behavior is byte-for-byte compatible with today

Signed-off-by: Patrick Koss <pati.koss@gmx.de>
@jwm4

jwm4 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Hi! I've updated #10 to renumber the RFCs 5 and 6 that were in there to RFCs 8 and 9 to avoid conflicts with the now merged RFC 5, this PR, and #13 which proposes an RFC 7. In the future, I'd recommend the following to avoid more numbering conflicts:

  1. Check the open PR list to see which RFC numbers are already in progress.
  2. Put your RFC numbers in the PR title so other people can see what RFC numbers you are using.

Of course that only works if everybody does it, but I think it's worth trying. In my opinion, a better solution would be to stop numbering the RFC's, but presumably that's a broader community discussion.

Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
Comment thread rfcs/0006-pluggable-auth/0006-pluggable-auth.md Outdated
@mprahl

mprahl commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

@B-Step62 what are your thoughts on refactoring how search authorization works? Right now, we do post response filtering and then backfill the pages. If a user has permission to one experiment, it could lead all experiments for the workspace being queried just looking for experiments to the fill the page.

I think a better approach is to ask the authorization system, does the user have permission to all experiments? If not, which experiments do they have access to? Then modify the request to filter by only those experiment IDs. Then pagination just works and it's more efficient.

@B-Step62

B-Step62 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@mprahl Yeah I agree with the approach, the current backfill logic is pretty dirty and suboptimal.

Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md
q
Merge branch 'main' into rfc/enterprise-authn-authz
Signed-off-by: Patrick Koss <pati.koss@gmx.de>
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated
Comment thread rfcs/0008-pluggable-auth/0008-pluggable-auth.md Outdated

@mprahl mprahl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good! Once my small comment is addressed and @rrrkharse 's comments are addressed, I'll approve the RFC.

@mprahl mprahl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me!

@PatrickKoss

Copy link
Copy Markdown
Contributor Author

@mprahl shall we merge the pr?

@rrrkharse

Copy link
Copy Markdown

Hey @PatrickKoss thanks for all the hard work on the RFC! Just wanted to let you know that I'd be happy to pick up the RFC implementation work unless you were looking to own that too.

@PatrickKoss

Copy link
Copy Markdown
Contributor Author

Hey @PatrickKoss thanks for all the hard work on the RFC! Just wanted to let you know that I'd be happy to pick up the RFC implementation work unless you were looking to own that too.

Hey @rrrkharse, if we're all aligned on the design, please feel free to pick up the implementation! Happy for you to take it from here.

@mprahl

mprahl commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

@mprahl shall we merge the pr?

I think we are waiting for @B-Step62 to take a look. I'll tag him to see if he still wants to review it.

@B-Step62

B-Step62 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@PatrickKoss @mprahl @rrrkharse The design overall looks good, thank you for pushing this forward!

The only question I want to align before proceeding is integration with RFC #32. The RFC introduces fine-grained access control, for example, run-level. The main impact seems to be the inheritance design proposed in the other RFC. Basically, acl on the child resource first check its own permission and then fallback to its parent container. To achieve this, we need to extend the design of "Core" part in this RFC.

One option is to produce more flexible expressions like this, instead of AND only.

OR (
    ("run", run_id, "update", workspace),
    ("experiment", experiment_id, "update", workspace),
)

Then MLflow deligates the evaluation of each child expression to the backend, which continue authorizing only normalized leaf requirements.

Is this approach reasonable?

@mprahl

mprahl commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

@PatrickKoss @mprahl @rrrkharse The design overall looks good, thank you for pushing this forward!

The only question I want to align before proceeding is integration with RFC #32. The RFC introduces fine-grained access control, for example, run-level. The main impact seems to be the inheritance design proposed in the other RFC. Basically, acl on the child resource first check its own permission and then fallback to its parent container. To achieve this, we need to extend the design of "Core" part in this RFC.

One option is to produce more flexible expressions like this, instead of AND only.

OR (
    ("run", run_id, "update", workspace),
    ("experiment", experiment_id, "update", workspace),
)

Then MLflow deligates the evaluation of each child expression to the backend, which continue authorizing only normalized leaf requirements.

Is this approach reasonable?

Since RFC #32 is still under review, perhaps we can merge this one and RFC #32 can account for this requirement?

@B-Step62

B-Step62 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Since RFC #32 is still under review, perhaps we can merge this one and RFC #32 can account for this requirement?

I'm fine with that as long as we release them together (if we decide to ship #32) rather than migrating twice.

@mprahl
mprahl merged commit 8a3217c into mlflow:main Sep 2, 2026
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.

6 participants