Identity and security leaders Reading time 8 minutes Status Proof of value, phase 4

Every OAuth client delivered to the team that owns it, with no secret changing hands

Courier turns OAuth client registration into a reviewed request. After approval it creates the client in the identity provider and places the credentials in a vault path that only the owning team's identity group can open. Nobody copies, emails or pastes a client_secret.

The challenge

Every integration with the identity provider needs a client registration: web apps, backend jobs, and now MCP servers and AI agents that call APIs on a user's behalf. The number of clients grows with every team and every new tool, but the process for issuing them has not changed. An identity administrator creates the client in a console, then sends the client_id and client_secret to someone.

Secrets travel through people

Credentials are copied into email, chat and tickets. They stay there permanently, and nobody can say who has seen them.

The identity team becomes a queue

Every new client, redirect URI change or cleanup waits on a small group of administrators with console access.

Clients outlive their owners

Registrations carry no reliable owner. When a project ends or a person leaves, the client and its secret keep working.

Behind these sit two quieter problems. Configuration drifts from standards because each client is set up by hand: an extra grant here, a wildcard redirect there. And when an auditor asks who approved a client, who can read its secret and when it was last issued, the answers live in several systems or nowhere.

What changes with Courier

Today

  • A team files a ticket or sends a message
  • An administrator configures the client by hand
  • The secret is sent to whoever asked
  • Access to the secret follows whoever received it
  • Removing a client depends on someone remembering

With Courier

  • A team opens a pull request describing the client
  • Automated checks enforce standards; the right people approve
  • Courier creates the client and stores the secret directly in the vault
  • Only members of the owning identity group can read it
  • Deleting the request deletes the client and destroys the secret

The identity team moves from doing every registration to owning the rules every registration must follow. Teams get credentials in minutes instead of waiting in a queue, and the handoff that used to be the weakest step no longer involves a person.

How it works

Four stages, each owned by the system best suited to it: Git records intent and approval, the identity provider holds the client, and the vault is the only place the secret is ever kept.

The secret appears only in the amber box. It is generated inside Courier and written straight to the vault.

Under the hood, in plain terms

Six design choices carry the promise. Each has a one-line technical note for readers who want to know how it is enforced rather than just that it is.

PrincipleWhat it meansHow it is enforced
Approval is a pull request The decision, the reviewer and the reason sit in one permanent record. Branch protection requires code-owner review and a passing courier validate check before merge.
Access follows identity groups Joining or leaving a team in the identity provider grants or removes access to its secrets. No separate list to maintain. Vault trusts the same IdP; the groups claim maps to a vault policy scoped to kv/teams/<team>/.
Courier can write, but not read Even if Courier were compromised, it could not reveal secrets it has already delivered. Courier's vault policy allows create, update, patch and delete, not read.
Stored before it is live A failure at any moment can never leave a working secret that nobody has. The secret is written as pending before the IdP accepts it, then promoted to active.
Only touches what it created Existing, hand-built clients are safe from accidental changes. Taking one over is a deliberate, approved step. Courier marks its applications with the owning team and refuses to modify any application without the marker, or owned by another team, unless an adoption request carries the identity team's approval label.
Every rule is checked twice What reviewers see in the pull request is exactly what will be enforced after merge. The CI check and the in-cluster controller run the same validation code.
A note on MCP and AI agents

Many MCP clients (IDEs, desktop apps, CLI agents) are public clients that use PKCE and hold no secret, and newer MCP authorization guidance reduces how often they must be pre-registered at all. Courier still records their ownership. Its strongest value is for confidential clients: MCP servers, backends and machine-to-machine jobs, where a real secret exists and must reach the right workload safely.

Risks and the controls that address them

RiskControlEvidence you can show an auditor
Secret disclosed in transit between peopleNo human handoff; secret generated and stored by CourierVault audit log shows every read, by identity
Wrong team obtains a client or its secretOwner group is fixed by the team directory; vault path scoped per groupPull request history; vault policy per team
Unreviewed or non-standard client configurationAutomated policy check plus code-owner approval before mergeRequired status check and approvals on every merged PR
High-risk grants issued casuallyGrants such as client_credentials need an explicit approval labelLabel and approver recorded on the PR
Orphaned clients with live secretsEvery client has an owning team; removing the request deletes client and secretRequest inventory in Git; controller status per client
Automation itself becomes a liabilityLeast privilege: write-only vault access, IdP role limited to OAuth clientsVault policy and IdP role definitions in the repository
A secret stays valid for years, or after a suspected leakRotation by pull request, or automatically after a maximum age; the IdP rejects the old secret as soon as the new one is storedrotationHandled and lastSecretIssued in the request's status; version history in the vault
Clients built by hand before Courier, secrets already emailedAdoption: Courier takes over the existing client, keeps its client ID and replaces the secret; needs an identity-team approval labelLabel on the PR; application marked as managed by Courier in the IdP
What we learned about proving a secret works

In Authentik, a client_credentials request that also carries a service account's username and app password gets a token whatever client secret is sent, including none. In that mode the app password is the real credential and the client secret is decoration. We found this during Phase 4, when an old, rotated secret still "worked".

Two consequences. For identity teams: treat service-account app passwords as the credential to govern, and do not read "a token was issued" as evidence a client secret is valid. For Courier: every check now proves the secret at the token revocation endpoint, which always authenticates the client, and also shows that a wrong secret is refused.

What it means for each role

Identity team

Owns the policy file and the vault and IdP integrations. Reviews only what needs judgment, such as new grant types or unusual redirect hosts, instead of every registration.

Application teams

Request, change and retire their own clients with a pull request. Read credentials with the identity they already use, from a laptop or a workload.

Security and audit

Get a single, reviewable record of who asked for what, who approved it, where the secret lives and who read it.

Status

Courier is running end to end in a reference environment built on Authentik, HashiCorp Vault Community Edition, Kubernetes and ArgoCD. The identity-provider integration is a small adapter, so Okta and similar providers can be added without changing the rest of the system.

Phase 0

Access model proven

Vault with automatic unsealing; identity groups decide who can read each team's secrets.

Phase 1

Delivery automated

A request becomes a client plus stored credentials; the owning team can use them and other teams cannot.

Phase 2

Pull request workflow

Requests live in Git with policy checks, code-owner approval and automatic sync after merge.

Phase 2b

Consumption made easy

A Python sample and a per-team External Secrets store, both proven to serve only the owning team; the controller ships from a registry, pinned by digest.

Phase 3

Self-service portal

A Backstage form opens the pull request; each client appears on its team's page with live delivery status.

Phase 4

Lifecycle

Secret rotation by pull request or on a schedule, adoption of hand-built clients, and drift repair that writes to the IdP only when something really changed. Audit dashboards remain open.

Phase 5

Portability

Okta adapter and key-based client authentication (private_key_jwt).

Where to go next

See it working

The walkthrough follows a real request, pull request #1, from the policy gate through merge, delivery and a check that only the owning team can read the credentials. It shows the evidence captured at every step. The team-bravo case study lists everything that request created in GitHub, ArgoCD, Kubernetes, Authentik and Vault, and what the team can do now.