AuthPolicy
Two CRDs manage policy — ClusterAuthPolicy for cluster-wide defaults and AuthPolicy for namespace-scoped overrides. This two-CRD model leverages Kubernetes RBAC so that security teams control cluster-wide defaults while application teams can override specific settings for OidcClients within their own namespaces.
Example
ClusterAuthPolicy (Cluster-Wide Default)
apiVersion: auth.nauthera.io/v1alpha1
kind: ClusterAuthPolicy
metadata:
name: production-policy
spec:
scopes:
- openid
- profile
- email
- "api:read"
- "api:write"
tokenConfig:
accessTokenTtl: 15m
refreshTokenTtl: 8h
idTokenTtl: 15m
rotateRefreshToken: true
claimMappings:
- claim: email
attribute: email
- claim: name
attribute: name
- claim: groups
attribute: groups
- claim: "https://myapp.example.com/roles"
attribute: appRoles
conditions:
requireMfa: false
allowedNetworkCidrs:
- "10.0.0.0/8"
- "172.16.0.0/12"AuthPolicy (Namespaced Override)
A namespaced AuthPolicy overrides specific fields from the cluster-wide defaults for all OidcClients in the same namespace:
apiVersion: auth.nauthera.io/v1alpha1
kind: AuthPolicy
metadata:
name: long-lived-sessions
namespace: internal-tools
spec:
scopes:
- openid
- profile
- email
tokenConfig:
accessTokenTtl: 1h
refreshTokenTtl: 24h
idTokenTtl: 1h
conditions:
requireMfa: trueIn this example, the ClusterAuthPolicy sets a 15-minute access token TTL, but the AuthPolicy in the internal-tools namespace gives OidcClients there 1-hour tokens with mandatory MFA instead.
ClusterAuthPolicy Spec Reference
Both ClusterAuthPolicy and AuthPolicy share the same spec fields. The scope is determined by the CRD kind — ClusterAuthPolicy is cluster-scoped and AuthPolicy is namespaced.
spec.claimMappings
Type: []object | Optional
Mappings from user-store attributes to JWT claims.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
attribute | string | Yes | — | User record attribute to source the value from. |
claim | string | Yes | — | JWT claim name. Use URL-namespaced names for custom claims (e.g. "https://app.example.com/roles"). |
tokenTypes | string | No | — | Restrict mapping to a specific token type: "access", "id", or omit for both. One of: access, id. |
transform | string | No | — | Optional transformation: "lowercase", "uppercase", "json". One of: lowercase, uppercase, json. |
spec.conditions
Type: object | Optional
Additional conditions that must be satisfied for authentication.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
allowedNetworkCidrs | []string | No | — | Restrict authentication to these network CIDRs. |
requireMfa | boolean | No | false | Require multi-factor authentication. |
spec.consentMode
Type: object | Optional
Consent screen behavior for clients cluster-wide.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | No | "once" | Consent display strategy. One of: always, once, implicit. |
retentionDays | integer | No | 30 | Number of days to remember consent decisions. |
spec.scopes
Type: []string | Required
Permitted OAuth2 scopes. Requests for unlisted scopes are silently dropped per RFC 6749. The openid scope is implicitly included.
spec.tokenConfig
Type: object | Optional
Token lifetime and rotation configuration.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
accessTokenTtl | string | No | — | Access token lifetime (e.g. "15m", "1h"). |
idTokenTtl | string | No | — | ID token lifetime (e.g. "1h"). |
refreshTokenTtl | string | No | — | Refresh token lifetime (e.g. "7d", "24h"). |
rotateRefreshToken | boolean | No | false | Issue a new refresh token on each use, invalidating the old one. |
AuthPolicy Spec Reference
The AuthPolicy spec mirrors ClusterAuthPolicy but applies only to a single namespace.
spec.claimMappings
Type: []object | Optional
Mappings from user-store attributes to JWT claims.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
attribute | string | Yes | — | User record attribute to source the value from. |
claim | string | Yes | — | JWT claim name. Use URL-namespaced names for custom claims (e.g. "https://app.example.com/roles"). |
tokenTypes | string | No | — | Restrict mapping to a specific token type: "access", "id", or omit for both. One of: access, id. |
transform | string | No | — | Optional transformation: "lowercase", "uppercase", "json". One of: lowercase, uppercase, json. |
spec.conditions
Type: object | Optional
Additional conditions that must be satisfied for authentication.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
allowedNetworkCidrs | []string | No | — | Restrict authentication to these network CIDRs. |
requireMfa | boolean | No | false | Require multi-factor authentication. |
spec.consentMode
Type: object | Optional
Consent screen behavior for clients in this namespace.
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
mode | string | No | "once" | Consent display strategy. One of: always, once, implicit. |
retentionDays | integer | No | 30 | Number of days to remember consent decisions. |
spec.scopes
Type: []string | Required
Permitted OAuth2 scopes for this namespace. Requests for unlisted scopes are silently dropped per RFC 6749. The openid scope is implicitly included.
spec.tokenConfig
Type: object | Optional
Token lifetime and rotation configuration (overrides ClusterAuthPolicy).
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
accessTokenTtl | string | No | — | Access token lifetime (e.g. "15m", "1h"). |
idTokenTtl | string | No | — | ID token lifetime (e.g. "1h"). |
refreshTokenTtl | string | No | — | Refresh token lifetime (e.g. "7d", "24h"). |
rotateRefreshToken | boolean | No | false | Issue a new refresh token on each use, invalidating the old one. |
Policy Composition
Policy composition happens in two layers:
1. Cluster-Wide Merge
When multiple ClusterAuthPolicy resources exist, they are merged:
scopes: Union of all policies.tokenConfig: Most restrictive value wins (shortest TTL,rotateRefreshToken: truetakes precedence).claimMappings: Union. If two policies map the same claim, the policy with the lower alphabetical name wins.conditions: Most restrictive wins.
2. Namespaced Override
After the cluster-wide merge produces a baseline, any AuthPolicy in a given namespace overrides the merged cluster defaults for OidcClients in that namespace. The override is field-level — only the fields specified in the namespaced AuthPolicy replace the cluster defaults; unspecified fields inherit from the cluster baseline.
Example: If the cluster-wide merge results in accessTokenTtl: 15m and an AuthPolicy in internal-tools sets accessTokenTtl: 1h, OidcClients in internal-tools get 1-hour access tokens while all other namespaces continue to use 15 minutes.
Enforcement Floors
Namespaced AuthPolicy overrides are clamped by the cluster-wide baseline to prevent weakening security:
requireMfa: A namespaced policy cannot setrequireMfa: falseif anyClusterAuthPolicysets it totrue. MFA enforcement can only be tightened, never relaxed, at the namespace level.- Token TTLs: Namespaced overrides cannot exceed the cluster-wide maximum. If the merged
ClusterAuthPolicysetsaccessTokenTtl: 15m, a namespaceAuthPolicysettingaccessTokenTtl: 1hwill be clamped to15m. scopes: Namespaced policies can only restrict the allowed scopes — they cannot grant scopes that are not in the cluster-wide set.
This ensures that cluster-wide security invariants set by the security team cannot be weakened by namespace-level overrides.
ClusterAuthPolicy Status
| Field | Type | Description |
|---|---|---|
affectedClients | integer | Number of OidcClients governed by this policy. |
lastReconciled | string | Last time the resource was reconciled. |
message | string | Human-readable message about current state. |
observedGeneration | integer | Current observed generation. |
ready | boolean | Whether the policy is active and applied. |
AuthPolicy Status
| Field | Type | Description |
|---|---|---|
affectedClients | integer | Number of OidcClients in this namespace governed by this policy. |
lastReconciled | string | Last time the resource was reconciled. |
message | string | Human-readable message about current state. |
observedGeneration | integer | Current observed generation. |
ready | boolean | Whether the policy is active and applied. |
Related Resources
- OidcClient — Clients governed by this policy.
- EmailProvider — Configure email delivery for the namespace.
- Architecture — How policies fit into the overall system.