Example: the issuer generates a client's secret¶
Goal¶
Stop delivering a confidential client's secret by hand. The issuer makes it and keeps it at external/oidc/<client>. The relying party reads it with its own secret operator.
What you need¶
- A confidential client.
- A Secrets adapter that writes only if absent (
ssmoropenbao), and a State shared between replicas.
The policy snippet¶
clients:
grafana:
kind: confidential
secret: {generate: true} # was: secret: grafana-oidc
requires: [devel:grafana:viewer]
redirects: [/login/generic_oauth]
The exchange / command¶
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata: {name: grafana-oidc, namespace: grafana}
spec:
secretStoreRef: {kind: ClusterSecretStore, name: sluis}
target: {name: grafana-oidc}
data:
- secretKey: client-secret
remoteRef:
key: external/oidc/grafana
property: client-secret
Once the relying party signs in with the generated value, delete the input clients/grafana/secret. The stored record takes precedence over it.
Verify¶
sluisctl clients show grafana prints created and generated in policy true. The audit trail shows
roster.client.secret.created, or .adopted when an input secret existed. A sign-in to the relying party succeeds.
Undo¶
Put the name back in secret:; the stored record is reported orphaned and kept. Rotate with sluisctl clients rotate.
Recipe: Let the issuer generate a client's secret; rotation:
rotate a client secret. The document is the oidc/v1 shape in
secrets.
Snippet source: docs/guides/sluis/let-the-issuer-generate-a-clients-secret.md; policy/clientsecret_test.go holds the parser
cases for the object form.