# Architecture The portal is a Linux FastAPI app. It is the **enrollment client**. Apple devices only download a finished, signed configuration profile. ## Flow 1. **OIDC.** The browser is redirected to a generic OpenID Provider (Keycloak, Okta, Authentik, Entra ID, …). Authorization code + PKCE. A small user dict (`sub`, `username`, `email`, `name`) is stored in a signed session cookie. Access tokens are not kept. 2. **LDAP.** Using the username claim (default `email`, then `preferred_username`), the portal binds as a service account and searches Active Directory. It reads `objectSid`, `userPrincipalName`, and `displayName`. An optional group gate uses `LDAP_MATCHING_RULE_IN_CHAIN` (nested membership). 3. **Key + CSR (memory only).** On **Download**, not at login, the portal generates RSA-2048 (configurable), and a CSR: - Subject `CN=` - SAN otherName `1.3.6.1.4.1.311.20.2.3` = UPN - SAN URI `tag:microsoft.com,2022-09-14:sid:` (Microsoft strong mapping / KB5014754) 4. **certsrv.** NTLM POST to `/certsrv/certfnsh.asp` with `CertificateTemplate:`. On issued, GET `/certsrv/certnew.cer?ReqID=&Enc=b64`. Pending or denied is an error; this portal does not wait for manager approval. 5. **Identity PKCS#12.** Private key + issued cert (+ optional CA) encrypted with a one-time password. Not written to disk. 6. **Unsigned plist.** A `com.apple.security.pkcs12` payload holds the identity P12 and that password. Optional CA payload if `PROFILE_CA_CERT_PATH` is set. 7. **Sign.** The completed plist is wrapped in attached CMS (not detached), equivalent to `openssl smime -sign -nodetach -outform der`, using the **profile-signing** PKCS#12. 8. **HTTPS download.** `Content-Type: application/x-apple-aspen-config`. The CA database requester is the **portal NTLM account**. The **user** is bound by SAN (UPN + SID URI), not by Windows impersonation. OIDC does not yield a Kerberos ticket for the end user, so there is no Enrollment Agent / on-behalf-of in this version. ## Two PKCS#12 files | File | Lifetime | Role | | --- | --- | --- | | Identity P12 | One download, RAM only | Embedded in the profile as `com.apple.security.pkcs12`. User cert + private key. | | Profile-signing P12 | Long-lived file on the server | CMS-signs the `.mobileconfig`. Must already be trusted on the device. | Do not reuse the signing P12 as a user identity, and do not save identity P12s. ## Why the phone never talks to the CA SCEP/NDES would have the device generate a key and speak SCEP to NDES. This portal instead returns the cert and key inside the profile. After install, nothing on the phone needs `certsrv` or NDES reachability. ## Code map | Module | Responsibility | | --- | --- | | `auth_oidc.py` | OIDC client registration, claim → username | | `directory.py` | LDAPS, `objectSid` decode, group gate | | `enrollment.py` | RSA key, CSR SAN, PKCS#12 export | | `certsrv.py` | NTLM submit/fetch, HTML disposition parser | | `profile.py` | Unsigned plist + CMS sign | | `issue.py` | Orchestrates one download | | `app.py` | HTTP routes, session, templates |