# Operator or dynamic registration?

> What OAuth 2.0 dynamic client registration (RFC 7591, RFC 7592) is for, and when to use it instead of the Nauthera operator for your own applications.

nauthera-server implements dynamic client registration at `/register`
([Clients](https://www.nauthera.io/docs/v1/clients#dynamic-client-registration)), so an application can register
its own OAuth client at runtime. That raises a fair question: if an app can register
itself, why run an [operator](https://www.nauthera.io/docs/v1/operator) and write
[`OIDCClient`](https://www.nauthera.io/docs/v1/oidc-client) manifests at all?

Because dynamic registration answers a different question. It lets software register with
an authorization server it has **no prior relationship** with. For the applications you
build and deploy yourself, you already have that relationship: you own the app, the
cluster and the identity provider. What those apps need is review, a lifecycle and secret
delivery, and dynamic registration leaves all three to each app.

## What dynamic registration is for

[RFC 7591](https://www.rfc-editor.org/rfc/rfc7591) starts from the observation that
"traditionally, registration of a client with an authorization server is performed
manually" (§1), and defines how a client can instead register itself by `POST`ing its
metadata to a registration endpoint. The RFC names the case it exists for: client
developers "will need to use dynamic registration when they are unable to predict aspects
of the software" (§1.2), such as which authorization servers it will meet, or how many
instances of it will run.

The cases that fit:

- **Software deployed by other people.** A product your customers install, each against
  their own identity provider, can't ship with a client ID baked in.
- **Clients meeting authorization servers they don't know in advance.** Open ecosystems,
  where a client discovers a server and registers on the spot.
- **One registration per installed instance**, such as a native app on each device.

Two of its tools address the trust problem this creates:

- An **initial access token** limits who may register: it "is required when the
  authorization server limits the parties that can register a client" (§1.2). Without one,
  registration is *open*: anyone can register (Appendix A.1.1).
- A **software statement** is a signed JWT in which a publisher vouches for the client's
  metadata, which then "MUST take precedence" over what the client sends (§5).

[RFC 7592](https://www.rfc-editor.org/rfc/rfc7592) adds reading, updating and deleting a
registration with a **registration access token**. It is an *Experimental* RFC, not a
standard, and "not all authorization servers supporting dynamic client registration will
support these management methods" (Abstract).

## Why it isn't the better way for your own apps

### Every value is the client's word

Without a software statement, the authorization server "MUST treat all client metadata as
self-asserted" (RFC 7591 §5). A rogue client "might use the name and logo of a legitimate
client that it is trying to impersonate", and a server allowing open registration "must be
extremely careful with any URL provided by the client" (§5).

nauthera-server takes that at its word. It accepts registrations only with an initial
access token for the target organization, and holds every self-registered client to a
fixed policy that configuration cannot loosen:

- scopes `openid`, `profile` and `email` only, and no `offline_access`, so **no refresh
  tokens**;
- PKCE always `required_s256`;
- the issuer's default token lifetimes;
- no per-client identity providers or second-factor policy, which are set through the
  control plane only.

See [Clients](https://www.nauthera.io/docs/v1/clients#dynamic-client-registration) for the full policy. A
client declared in an `OIDCClient` manifest has gone through your review, so it can have
all of these.

### The app has to keep its own credentials

A registration answers with a `client_secret` and, for RFC 7592, a
`registration_access_token`, a bearer token that "MUST be protected by the developer or
client" (RFC 7592 §5). nauthera-server returns that token only in the registration
response, never again on a read or an update. So each app needs somewhere durable to keep
two secrets it created at runtime, and a way to get the initial access token in the first
place, which is itself a secret someone has to hand it.

With the operator, the server mints the secret once and keeps only a hash, and the
operator writes the secret into a `Secret` in the app's namespace. The app only reads it.
Nobody holds a token for managing the registration.

### Replicas and restarts multiply clients

RFC 7591 allows either a client ID per instance or one shared by all instances (Appendix
A.4), but not shared credentials: a server "SHOULD NOT issue the same client secret to
multiple instances of a registered client, even if they are issued the same client
identifier" (§5), and the registration access token "should not be shared between
instances of a client" (RFC 7592, Appendix A). nauthera-server mints a new client ID for
every registration, so a Deployment with three replicas that each register on start-up
creates three clients, and more on every restart that lost its state. If they instead
share one registration, someone has to coordinate who registers and where the result is
stored, which is the job an operator does.

An `OIDCClient` is one client, whatever the replica count; every pod mounts the same
`Secret`.

### Nobody cleans up after a crash

Under RFC 7592, a client "makes an HTTP DELETE request" to deprovision itself (§2.3). A pod
that is killed, evicted or scaled away never does, so its client stays registered with a
valid secret.

With the operator, deleting the manifest triggers `spec.deletionPolicy`: `Retain` (the
default) keeps the client and releases its `Secret`, and `Delete` removes the client. A
periodic sweep also removes `Delete`-policy clients whose manifest disappeared without the
finalizer running. See [Deleting a manifest](https://www.nauthera.io/docs/v1/operator#deleting-a-manifest).

### No review, no status, no drift

A dynamically registered client exists because some process holding a token decided it
should, and RFC 7592 lets whoever holds the registration access token change it later.
Nothing in your repository records that the client exists or what it should look like.

An `OIDCClient` is a manifest in Git, reviewed like the Deployment beside it. Its `status`
reports `Ready`, `Refused`, `Unreachable` or `Drifted`, and a change made outside the
operator is reported, not silently kept or overwritten. See
[Changes made outside the operator](https://www.nauthera.io/docs/v1/operator#changes-made-outside-the-operator).

## Side by side

| | Dynamic registration (RFC 7591/7592) | `OIDCClient` and the operator |
|---|---|---|
| Who decides a client exists | The app, at runtime, with an initial access token | A manifest, reviewed and merged |
| Trust in its metadata | Self-asserted; nauthera-server applies a fixed, narrow policy | Reviewed; the full client settings are available |
| Refresh tokens | No (`offline_access` is refused) | Yes, with `allowRefresh` |
| Where the secret lives | Wherever the app keeps it | A `Secret` in the app's namespace; the server keeps a hash |
| Managing the registration | A registration access token the app must protect | Kubernetes RBAC on the resource |
| Replicas | One registration per instance, or coordination you build | One client, one `Secret` for every pod |
| Removal | The app sends `DELETE`, if it gets the chance | `deletionPolicy`, plus the orphan sweep |
| Visibility | Nothing outside the server | `status`, events, and drift on the resource |

## When dynamic registration is the right tool

Use `/register` when the client is not yours to declare:

- **Third-party or customer-deployed software** that should register against your
  server. Give it an initial access token for one organization, and the policy above
  bounds what it can do.
- **Software that meets many authorization servers**, and can't be configured for each in
  advance.
- **A client that needs a back-channel logout URI.** `backchannel_logout_uri` can be
  registered only through `/register` today.

Outside Kubernetes, your own automation can also register clients through the gRPC
`AdminService`, as `nauthera-cli` does, with the same settings as the operator.

## Why Nauthera ships both

Dynamic registration is a standard, so clients that expect it work with nauthera-server
(see the [standards page](https://www.nauthera.io/standards) for what is supported). The operator is the path for
your own applications on Kubernetes: their clients are declared, reviewed and delivered
like the rest of the app, and none of the steps above is left for each app to get right.
