Using AD Accounts to Sign In to Cloudflare Through authentik
Using AD Accounts to Sign In to Cloudflare Through authentik
This deployment connected Windows AD → authentik → the Cloudflare dashboard. A domain user signed into authentik and launched Cloudflare from an application card. The last obstacle was the card’s URL: the ordinary dashboard homepage was a bookmark, not an entry point into this SSO flow.
The September 16, 2026 record uses authentik 2026.8.2, two writable DCs, and a cloud RODC. The user confirmed the complete dashboard login. Domains below are examples; no passwords or client secrets are included.
Separate directory access, password validation, and federation
AD DS ── LDAPS ── authentik ── OIDC ── Cloudflare Access
↓
Cloudflare Dashboard SSOAD supplies directory data and password checks; authentik supplies an OIDC identity; Cloudflare consumes it. AD FS was not deployed because an existing authentik installation was reused. Dashboard email-domain verification is a separate requirement.
Browser access uses HTTPS through a reverse proxy, while DC traffic travels over private WireGuard links. One connectivity failure had a route to wg0 but no matching destination in the peer’s AllowedIPs. Check both route and peer selection:
Replace the documentation address below with the target DC private address.
ip route get 192.0.2.10
wg show wg0 allowed-ips
wg show wg0 latest-handshakesHealthy containers can still serve a broken login page
The official Compose deployment contained PostgreSQL, server, and worker, with application ports bound to loopback behind HTTPS. Dependencies are version-specific; an old Redis configuration should not be copied automatically.
Initially all containers were healthy and / returned 302, but the final page returned 500:
PermissionError: [Errno 13] Permission denied: '/templates/if/flow.html'The empty host template directory was root-owned with mode 700. Application UID 1000 could not traverse it. Correcting that directory’s access restored HTTP 200 without broadly relaxing secret-file permissions.
curl -sS -L -o /dev/null \
-w 'HTTP=%{http_code} final=%{url_effective}\n' \
https://sso.example.com/Following redirects exposes failures hidden behind a healthy root URL. A 200 response still does not prove authentication.
A dedicated LDAP reader and explicit password behavior
The ordinary bind account lived in a dedicated OU with default directory-reading rights, without DCSync, password-reset, or directory-write permissions. It was not a gMSA. An empty MemberOf list does not eliminate primary-group membership.
| Setting | Selection |
|---|---|
| Server URI | Three ldaps://FQDN destinations |
| Bind CN | Dedicated account UPN |
| Base DN | DC=corp,DC=example,DC=com |
| Object uniqueness | objectSid |
| User/group synchronization | Enabled |
sync_users_password | False: no LDAP password writeback |
password_login_update_internal_password | False: no local password update after LDAP login |
delete_not_found_objects | False |
Map name, email, and UPN explicitly. SID preserves identity across renames; recreating a deleted username generates a new SID. Name-based exclusions are filtering, not a complete application authorization policy.authentik AD documentation
An open port does not prove valid LDAPS
All three DCs initially accepted TCP 636 but returned no peer certificate available. Validation must cover the server certificate, private key, Server Authentication EKU, FQDN SAN, and trusted issuing chain.
An enterprise CA was used here, but AD CS is not mandatory for LDAPS. Both server and worker need correct FQDN resolution and trust. Import the CA’s public certificate, never its private key.
The final Source configuration enforced CERT_REQUIRED. Each DC passed a real bind and user lookup; removing the trusted CA caused rejection. That negative control verifies identity checking rather than merely encrypted transport.
The deployed version used a random ServerPool. URI order did not establish primary/fallback priority; this implementation detail must be checked again after upgrades.
Follow the server error, not the UI guess
The OIDC provider used a confidential client, openid email profile, and this strict callback:
https://<team>.cloudflareaccess.com/cdn-cgi/access/callbackThe callback is neither the authentik hostname nor dash.cloudflare.com.
Cloudflare initially reported Application ID is likely incorrect, while authentik logged Invalid grant_type for provider. The ID and callback were correct. Programmatic creation had left grant_types=[]. Allowing authorization_code fixed the complete authorization test. An old callback carries old state; start a fresh flow instead of repeatedly refreshing it.
Check the application’s discovery document and public signing keys:
https://sso.example.com/application/o/cloudflare/.well-known/openid-configuration
https://sso.example.com/application/o/cloudflare/jwks/Reachable metadata and a successful code exchange are separate acceptance checks.authentik Cloudflare integration
Dashboard SSO also needs an email-domain connector
A successful IdP test must be followed by verification and activation of a Dashboard SSO Connector for a controlled email domain. Public email domains cannot be registered by their users. Current Cloudflare documentation lists free availability across plans and requires a securely stored recovery API token with SSO Connector Edit permission.Cloudflare Dashboard SSO
The original Outlook super administrator remained independent, while a custom-domain member handled daily SSO. Email forwarding enabled invitation receipt but did not provide a complete mailbox. AD mail, authentik email, and the Cloudflare member email needed to match.
Create the connector in account-level member settings, add its generated TXT record, verify it, and explicitly enable it. Verified and enabled are different states.
The final failure was an authentik card pointing to https://dash.cloudflare.com/, with no matching authorization request in the logs. Its Launch URL was replaced with the actual SSO App Endpoint:
https://<team>.cloudflareaccess.com/cdn-cgi/access/sso/saml/<generated-id>The saml segment does not change authentik’s provider protocol from OIDC. Copy the endpoint generated for the account. In the same browser session, the user signed into authentik, launched the card, selected the IdP, approved authorization, and reached the dashboard successfully.
Diagnose each handoff before changing credentials
Separate browser-to-authentik, authentik-to-directory, federation configuration, and Cloudflare account authorization. Preserve timestamps and request identifiers at each boundary.
| Failure | Inspect first | Reason |
|---|---|---|
| HTTP 500 or broken page | Server/worker, database, proxy | AD password validation may not have started |
| Synchronization works, passwords fail | LDAPS chain/name, bind tests, flow | Directory reading and user authentication use different identities |
| IdP test works, dashboard does not | Email-domain connector, verified email, account access | An IdP test does not establish dashboard routing |
| Application tile uses old entry | Launch URL and generated provider endpoint | A hand-written URL may bypass the intended handoff |
| Only an old browser works | Fresh-session test and current errors | Cached sessions can conceal current failure |
Restrict the LDAP reader to the intended scope. Test user authentication and group/email mapping separately. A synchronized username with incorrect email may fail downstream matching. Record scope changes rather than disabling all group/domain restrictions to obtain a successful login.
Accept LDAPS with certificate checks and negative controls
Validate from authentik's execution environment, not only an administrator's laptop. Replace this diagnostic FQDN and CA path:
openssl s_client -connect dc1.corp.example.com:636 \
-servername dc1.corp.example.com \
-CAfile /path/to/ad-ca.pem \
-verify_hostname dc1.corp.example.com -verify_return_error </dev/nullInspect chain, SAN, expiry, and verification before testing correct, incorrect, and disabled-account credentials with the actual application library. TLS success alone does not establish LDAP authorization. Test every DC individually and identify the selected server: a random pool can hide one invalid certificate as an intermittent failure.
After CA/leaf renewal, confirm server, worker, and persistent mounts use the new content, then create fresh connections. A successful page through an existing connection does not validate every renewed path. Preserve logs and restore the original trust configuration if needed; bypassing certificate validation is not acceptance.
Disabling an AD account is not universal session revocation
Temporary-account tests produced the following observations; test objects were removed afterward:
| Action | Observation |
|---|---|
| Create in AD and synchronize | User and mapped attributes appeared |
| Disable in AD and synchronize | authentik object remained active |
| Bind the disabled account with the correct password | AD rejected it |
| Delete in AD and synchronize | authentik object remained |
The filter excluded disabled users and deletion of missing objects was off. Rejection of a new bind does not revoke every existing authentik session or application token. Deactivation requires state synchronization, session revocation, and controlled cleanup.
The complete login worked, but the record still had gaps: MFA enforcement, the HTTPS renewal deployment hook, complete CA recovery backups, account rotation, and resource alerts on the small VPS. These remain distinct from successful SSO.
Troubleshoot in layers: final page, trusted LDAPS, directory synchronization, OIDC callback, email-domain connector, and launch endpoint. This makes failures easier to isolate than repeatedly entering passwords.
The article date is the main KB’s first Git commit date, 2026-09-16 (UTC+8), commit 9682da8. Experiment dates are stated separately. No production systems were accessed or changed while preparing this article.
