# SSO API

Module: `citadel_operator.k3s.sso`

Configures Dex OIDC identity provider for K3s API server authentication — K3s flags, kubeconfig generation, RBAC bindings, and connector management.

## Constants

```python
VALID_CONNECTOR_TYPES = {"saml", "ldap", "github", "oidc", "static"}
DEFAULT_SCOPES = ("openid", "profile", "email", "groups")
```

## OIDCConfig

Frozen dataclass defining OIDC client configuration.

```python
@dataclass(frozen=True)
class OIDCConfig:
    issuer_url: str                                    # Dex issuer URL, e.g., "https://dex.citadel.local"
    client_id: str
    client_secret: str | None = None                   # None for public clients
    scopes: tuple[str, ...] = ("openid", "profile", "email", "groups")
    groups_claim: str = "groups"                       # JWT claim for group membership
    username_claim: str = "email"                      # JWT claim for username
```

### Validation

- `issuer_url` must start with `https://`
- `scopes` must include `"openid"`

## DexConnector

Frozen dataclass defining a Dex identity provider connector.

```python
@dataclass(frozen=True)
class DexConnector:
    connector_type: str          # One of VALID_CONNECTOR_TYPES
    name: str
    connector_id: str = ""       # Auto-generated from name if empty
    config: dict[str, Any] = {}

    @property
    def effective_id(self) -> str: ...
    def to_dex_config(self) -> dict[str, Any]: ...
```

## SSOManager

### Constructor

```python
SSOManager(dex_url: str | None = None)
```

### Methods

| Method | Signature | Description |
|--------|-----------|-------------|
| `generate_k8s_oidc_flags` | `@staticmethod (config) -> dict[str, str]` | K3s server `--oidc-*` flags |
| `generate_kubeconfig` | `@staticmethod (config, token, ...) -> dict` | OIDC-authenticated kubeconfig |
| `list_connectors` | `() -> list[dict]` | Query Dex API for configured connectors |
| `create_rbac_binding` | `@staticmethod (group, cluster_role, namespace=None) -> dict` | RBAC binding for OIDC group |

### Usage

```python
from citadel_operator.k3s.sso import SSOManager, OIDCConfig, DexConnector

config = OIDCConfig(
    issuer_url="https://dex.citadel.local",
    client_id="kubernetes",
)

# Generate K3s API server flags
flags = SSOManager.generate_k8s_oidc_flags(config)
# {"oidc-issuer-url": "https://dex.citadel.local", "oidc-client-id": "kubernetes", ...}

# Generate kubeconfig for an authenticated user
kubeconfig = SSOManager.generate_kubeconfig(
    config, token="eyJ...",
    cluster_name="my-cluster",
    cluster_server="https://10.0.0.1:6443",
)

# Create RBAC binding for OIDC group
binding = SSOManager.create_rbac_binding(
    group="platform-admins",
    cluster_role="cluster-admin",
)
# ClusterRoleBinding mapping "oidc:platform-admins" to cluster-admin

# Namespace-scoped binding
ns_binding = SSOManager.create_rbac_binding(
    group="developers",
    cluster_role="edit",
    namespace="default",
)
# RoleBinding in "default" namespace

# Define a connector
connector = DexConnector(
    connector_type="github",
    name="GitHub Org",
    config={
        "clientID": "...",
        "clientSecret": "...",
        "orgs": [{"name": "my-org"}],
    },
)
dex_config = connector.to_dex_config()
```

## OIDC Group Mappings (Default)

Installed by `10-install-dex.sh`:

| OIDC Group | ClusterRole | Scope |
|------------|-------------|-------|
| `oidc:platform-admins` | `cluster-admin` | Cluster-wide |
| `oidc:platform-ops` | `citadel-operator` | Cluster-wide |
| `oidc:developers` | `citadel-developer` | Namespace: default |
| `oidc:viewers` | `view` | Cluster-wide |
