Skip to content

HOG v2.2.0

A minor release: one new field on the IdP resource, verificationOnly, for a deployment that runs one instance for login and another for verification. Nothing existing changes.

An IdP that only verifies tokens

HOG's IdP resource has always described a full OpenID Connect relying party, and refused to build without all four of issuer, clientID, clientSecret and redirectURL. That is right for the instance that logs users in. It is wrong for an instance that never does.

Consider a deployment split across two HOG instances. The first terminates the OIDC session: it runs the authorization-code flow, holds the cookie, and refreshes the access token. The second sits in front of the APIs and only ever verifies a Bearer access token the first obtained; it declares no session, so it mounts no login, logout or callback route and never performs a code exchange. Both need an IdP — verification is a protocol operation, and discovery is what resolves the key set the token is checked against — but only the first needs to be able to start a login flow.

Until now the second one had to be handed a client secret it could never use and a redirect URL naming a route it does not serve. That is not merely untidy: it copies a credential into a second workload and a second secret store for no functional reason, and the verifying instance is typically the one exposed to all of the platform's API traffic. Every extra copy of a secret is another place it can leak.

An IdP can now say what it is for:

kind: IdP
metadata: { name: corp-oidc }
spec:
  type: oidc
  verificationOnly: true          # verifies tokens; never starts a login flow
  issuer: https://idp.example.com # discovery finds the key set; every token is checked against it
  clientID: ${OIDC_CLIENT_ID}     # the expected audience

That is the whole resource. verificationOnly requires the two fields verification genuinely uses and drops the two it does not.

clientSecret and redirectURL are rejected on such an IdP rather than ignored, so a configuration copied from the login instance and left unedited fails at startup instead of quietly carrying a credential that instance can never spend.

Everything on the verification side is unchanged: bearerAudience, the bearer: block, identity.subjectClaim, group derivation, route access/authorize, and both directions of the identity assertion all work exactly as they do on a full instance. Only the login flow is absent.

It is always explicit

HOG does not infer verification-only from the absence of a session block. Inferring it would mean that adding a session to an existing configuration silently changed what its IdP resource had meant — and a configuration file should not change meaning because a different file grew a new block. The declaration is a field an operator writes, or does not.

What is refused, and why

A verification-only IdP holds no client secret and no redirect URL, so it cannot run the authorization-code flow — and the callback of that flow is the only thing in HOG that issues a session cookie. A gateway that configures both a session and a verification-only IdP therefore describes a login that can never happen: no login, callback or logout endpoint would be mounted, and every protected app route would redirect to a path that answers 404. That combination is refused when the gateway is built, with an error that names both halves and the two ways out of it.

A lone auth block (loginPath, logoutPath) is not part of that check. Without a session it is inert for every configuration, verification-only or not, so it says nothing contradictory on its own.

Below the configuration layer, the connector itself refuses to start a login flow out loud rather than quietly producing a broken one: Exchange and Refresh return an error naming the field, and AuthCodeURL — which has no error to return — panics rather than hand back an authorization URL with an empty redirect_uri. A configured gateway never reaches those; they catch the framework-mode caller that wires the auth handlers by hand.

Upgrade notes

Nothing to change. An IdP that does not set verificationOnly keeps exactly the behaviour, and exactly the strictness, it has today: all four of issuer, clientID, clientSecret and redirectURL remain required.

If you run a verifying instance that was given a client secret only to satisfy that requirement, you can now delete the secret from its configuration — and from whatever secret store fed it — and add verificationOnly: true:

kind: IdP
metadata: { name: corp-oidc }
spec:
  type: oidc
  verificationOnly: true
  issuer: https://idp.example.com
  clientID: ${OIDC_CLIENT_ID}
  # clientSecret and redirectURL: removed, and now rejected if present

See authentication: an instance that only verifies tokens, deployment topology and security hardening.

go get github.com/paulopiriquito/hog/v2@v2.2.0