Noizu.MCP.Auth.ChainVerifier (Noizu MCP v0.1.6)

Copy Markdown View Source

Tries several verifiers in order and takes the first success.

This is what lets one mount serve both an interactive agent holding an OAuth access token and a headless script holding an API key, without the mount needing to know which is which:

auth: [
  verifier: {Noizu.MCP.Auth.ChainVerifier, [
    verifiers: [
      {Noizu.MCP.Auth.JWTVerifier, [resource: resource, secret: {MyApp, :secret}]},
      {Noizu.MCP.Auth.ApiKeyVerifier, [resource: resource, validator: {MyApp.Keys, :verify}]}
    ]
  ]}
]

A bare list is also accepted ({Noizu.MCP.Auth.ChainVerifier, [{Mod, opts}, ...]}).

Order by cost, cheapest first — a JWT verifies with no database round trip, an API key usually needs one.

Failure is uniform

When every verifier rejects, the answer is a single {:error, :invalid_token} with no indication of which link rejected it or how far the token got. A chain is otherwise an oracle: "this failed at the API-key step" tells an attacker their string was accepted as a JWT.

The one exception is {:error, :insufficient_scope, meta} — the first verifier to report it wins, and it propagates so the client gets the 403 step-up hint it needs. That is not an oracle: the caller already holds a token the server accepted, and is only being told which scope is missing.

A verifier that raises is treated as a rejection and logged (without the token), so one misconfigured link cannot 500 the mount or fail it open.