Skip to content

The v1 docs follow nauthera-server's main branch and its 0.x releases on the way to 1.0, so fields and APIs can still change.

v1

Operator or dynamic registration?

Docs version v1 (main)Nauthera is pre-1.0, in a design-partner programUpdated

nauthera-server implements dynamic client registration at /register (Clients), 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 and write OIDCClient 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 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 POSTing 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 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 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.

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.

Side by side

Dynamic registration (RFC 7591/7592)OIDCClient and the operator
Who decides a client existsThe app, at runtime, with an initial access tokenA manifest, reviewed and merged
Trust in its metadataSelf-asserted; nauthera-server applies a fixed, narrow policyReviewed; the full client settings are available
Refresh tokensNo (offline_access is refused)Yes, with allowRefresh
Where the secret livesWherever the app keeps itA Secret in the app's namespace; the server keeps a hash
Managing the registrationA registration access token the app must protectKubernetes RBAC on the resource
ReplicasOne registration per instance, or coordination you buildOne client, one Secret for every pod
RemovalThe app sends DELETE, if it gets the chancedeletionPolicy, plus the orphan sweep
VisibilityNothing outside the serverstatus, 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 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.