# ๐ชช SSO & Identity
> [!NOTE]
> Authentik is the identity provider for this fleet โ self-hosted rather than an external IdP, for the same reason Vault is self-hosted in [`docs/secrets-and-pki.md`](secrets-and-pki.md): internal-only apps shouldn't need to trust a third party just to authenticate a login, and the two-zone reverse-proxy model in [`docs/dns-naming.md`](dns-naming.md) only works if the identity provider itself lives inside that same trust boundary.
## Dual-zone registration
Every new app gets a Proxy Provider + Application pair registered per reverse-proxy zone it's actually exposed on โ up to two registrations for the same app, one per zone from [`docs/dns-naming.md`](dns-naming.md), driven by actual need rather than registering every app on both zones by default.
## OIDC login: the redirect crosses zones, every time
For an app using native OIDC, the login always follows the same path:
1. User opens the app on the **browser-facing zone**.
2. The app redirects the browser to Authentik to do the actual OAuth authorize/token exchange.
3. That exchange happens against Authentik's own address on the **service-to-service zone** โ reachable from a browser for this one hop specifically, even though that zone doesn't get the mutual-TLS treatment the browser-facing zone does.
4. On success, the browser lands back on the app, still on the browser-facing zone.
So an app's OIDC discovery/authority setting always points at the service-to-service zone, even though the app itself is normally reached on the browser-facing zone โ the two zones are defined in [`docs/dns-naming.md`](dns-naming.md). This is confirmed against one real app's actual login flow, not assumed to hold for every app without checking. Every other app-specific setting (token lifetime, scopes, flow type) doesn't generalize the same way โ each app's own OIDC documentation gets read fresh rather than copying another app's provider config, since those details genuinely differ per vendor.
## Git access: least privilege, not the admin account
```mermaid
flowchart LR
CI["CI / Orchestration engine
see docs/orchestration.md"] -->|scoped write| Bot["Dedicated bot account
non-admin"]
Bot --> Repo["Gitea"]
Other["Other hosts"] -->|read-only| Deploy["Deploy keys
per host"]
Deploy --> Repo
```
Automation should never run with more privilege than it needs. CI and orchestration pushes (the flows described in [`docs/orchestration.md`](orchestration.md)) run through a dedicated, non-admin bot account with scoped write access โ never through an administrator identity. Hosts that only need to *read* get separate read-only deploy keys instead of any shared credential. The practical payoff: if an automation credential ever leaks, the blast radius is "can push to specific repos," not "has full admin control of the Git server."
---
Related: [`docs/dns-naming.md`](dns-naming.md), [`docs/orchestration.md`](orchestration.md), [`docs/secrets-and-pki.md`](secrets-and-pki.md) ยท Stacks: [`stacks/identity-and-security/authentik/`](../stacks/identity-and-security/authentik/), [`stacks/platform/gitea/`](../stacks/platform/gitea/)