I've recently completed a project to integrate an external approval step into Cloudflare Access request workflows, and I believe the approach might be of interest to this community, particularly for organizations requiring a higher degree of audit or separation-of-duties than Access's native "require all logged-in users to approve" model provides.
The core challenge was that while Access excels at authentication and policy enforcement, its built-in "everyone approves" mechanism for multi-user approvals doesn't suit scenarios where a specific manager, a security team member, or a designated approver group must explicitly grant permission. Our requirement was to intercept an initial Access grant and route it to a third-party system for business logic evaluation before the policy is ultimately enforced.
The solution leverages Access's webhook functionality, specifically the `access.approved` event. However, it's crucial to understand this webhook fires *after* the user has been granted access by Access's own mechanisms. Therefore, it cannot be used to prevent the initial grant. Our workaround was to design a two-policy system. The first policy is a very restrictive, time-bound "landing zone" that simply allows the user to authenticate and trigger the webhook. A separate service, listening for the `access.approval.approved` webhook event, then processes the request. This service validates the request against our internal directory, notifies the human approver via our internal ticketing system, and awaits a decision.
Upon a positive decision from the external system, a second, more permissive Access policy—scoped to the intended application and user—is dynamically created via the Cloudflare API (using a service token with appropriate permissions). The user is then redirected to the actual application, now covered by this new policy. A negative decision results in no policy being created, and the user remains confined to the landing zone, which expires shortly thereafter. All actions are logged for audit trails.
This architecture introduces a slight delay between initial authentication and resource access, but it provides the granular approval control we needed. It also separates the approval authority from the Access policy configuration itself, which was a key security requirement. I am curious if others have tackled similar governance challenges with Access and what alternative patterns or pitfalls they might have encountered. The integration of webhooks and the Policies API proved quite powerful, albeit requiring a moderate amount of custom middleware development.
— EthanP
Let's keep it constructive
That's a clever workaround with the two-policy system. It reminds me of setting up a similar gate for AWS console access, where you'd route a successful IAM login through a Lambda to check a separate ticket system before issuing temporary elevated credentials.
My immediate thought on the `access.approved` webhook firing *after* the grant is the potential for a brief access window before the restrictive second policy kicks in. How did you handle the timing? I'm guessing the first policy's session duration is measured in minutes.
Numbers don't lie – vendors do.
That's a good catch on the timing window. In our testing, that brief period was our biggest concern. We set the first policy's session duration to the absolute minimum, which I think is 5 minutes in Cloudflare, and our listener is configured to revoke the session immediately upon receiving the webhook if the external system says no.
But it does feel like a race condition. We rely on our external system processing the webhook and calling the API faster than a user could launch a browser session. Have you seen a pattern for eliminating that gap entirely, or is accepting a tiny, theoretical window just part of the architecture?