Skip to content

Cloudflare.Access reference

Source: src/Cloudflare/Access/Application.ts

A Cloudflare Zero Trust Access application.

Replaces the curl-based POST /accounts/{accountId}/access/apps workflow with an Alchemy-managed resource. Supports every Cloudflare application type including warp, which Cloudflare requires for device enrolment via the WARP client.

Access policies are authored as standalone Policy resources and referenced here by id — there is no inline-policy support.

Self-hosted application gated by a reusable Access policy

const allowMyOrg = yield* Cloudflare.Access.Policy("AllowMyOrg", {
name: "Allow example.com via Google",
decision: "allow",
include: [{ emailDomain: { domain: "example.com" } }],
});
const app = yield* Cloudflare.Access.Application("InternalDashboard", {
type: "self_hosted",
domain: "dashboard.example.com",
sessionDuration: "24h",
policies: [allowMyOrg],
});

Managed OAuth for an MCP server

const app = yield* Cloudflare.Access.Application("McpServer", {
type: "self_hosted",
domain: "mcp.example.com",
oauthConfiguration: {
enabled: true,
grant: {
sessionDuration: "24h",
accessTokenLifetime: "15m",
},
dynamicClientRegistration: {
enabled: true,
allowAnyOnLocalhost: true,
allowAnyOnLoopback: true,
},
},
});

Application: Protecting Cloudflare Workers

Section titled “Application: Protecting Cloudflare Workers”

Require Access on a specific Worker

// The application owns the policies (inline here — no separate Policy
// resource needed); the Worker enrolls itself via its `access` prop,
// covering its custom domains, routes, workers.dev URL, and version
// preview URLs.
const App = Cloudflare.Access.Application("TeamOnly", {
type: "self_hosted",
policies: [
{ decision: "allow", include: [{ emailDomain: "example.com" }] },
],
});
export default class Api extends Cloudflare.Worker<Api>()("Api", {
main: import.meta.url,
access: { application: App },
}, /* ... *​/) {}

Require Access on every Worker in the account

// Covers all current AND future Workers. Hostname-level policies beat
// Worker-level policies, which beat this account-level policy — so an
// individual Worker can still be opened up with its own application.
yield* Cloudflare.Access.Application("ProtectAllWorkers", {
type: "self_hosted",
destinations: [
Cloudflare.Access.AllWorkers, // production traffic of every Worker
Cloudflare.Access.AllWorkerPreviews, // every Worker's preview URLs
],
policies: [
{ decision: "allow", include: [{ emailDomain: "example.com" }] },
],
});
// There can only be ONE warp app per account; Cloudflare auto-derives the
// domain (`${authDomain}/warp`) so do not pass `domain` for this type.
const allowCorp = yield* Cloudflare.Access.Policy("AllowCorpUsers", {
name: "Allow corp users",
decision: "allow",
include: [{ emailDomain: { domain: "example.com" } }],
});
const enroll = yield* Cloudflare.Access.Application("warp-login", {
type: "warp",
allowedIdps: [googleIdpId],
autoRedirectToIdentity: true,
sessionDuration: "720h",
policies: [allowCorp],
});
const admins = yield* Cloudflare.Access.Policy("AdminsOnly", {
name: "Admins only",
decision: "allow",
include: [
{
gsuite: {
email: "admins@example.com",
identityProviderId: googleIdpUuid,
},
},
],
});
const app = yield* Cloudflare.Access.Application("AdminConsole", {
type: "self_hosted",
domain: "admin.example.com",
allowedIdps: [googleIdpUuid],
autoRedirectToIdentity: true,
policies: [admins],
});

Source: src/Cloudflare/Access/Bookmark.ts

A Cloudflare Zero Trust Access bookmark application — an unprotected link shown in the App Launcher.

Basic bookmark

const bookmark = yield* Cloudflare.Access.Bookmark("Wiki", {
domain: "wiki.example.com",
});

Bookmark with a logo, hidden from the App Launcher

const bookmark = yield* Cloudflare.Access.Bookmark("Wiki", {
name: "internal-wiki",
domain: "wiki.example.com",
logoUrl: "https://example.com/logo.png",
appLauncherVisible: false,
});
const app = yield* Cloudflare.Access.Application("Wiki", {
type: "bookmark",
domain: "wiki.example.com",
});

Source: src/Cloudflare/Access/Certificate.ts

A Cloudflare Zero Trust Access mTLS certificate. Uploads a CA certificate that Access uses to validate client certificates presented to protected applications on the associated hostnames.

The certificate body is immutable — changing the PEM replaces the resource. The name and associated hostnames converge in place.

Upload a CA certificate

const ca = yield* Cloudflare.Access.Certificate("ClientCa", {
certificate: CA_PEM, // -----BEGIN CERTIFICATE----- ...
});

Certificate with associated hostnames

const ca = yield* Cloudflare.Access.Certificate("ClientCa", {
name: "corp-client-ca",
certificate: CA_PEM,
associatedHostnames: ["app.example.com"],
});
const ca = yield* Cloudflare.Access.Certificate("ClientCa", {
certificate: CA_PEM,
associatedHostnames: ["app.example.com", "admin.example.com"],
});

Source: src/Cloudflare/Access/CustomPage.ts

A Cloudflare Zero Trust Access custom page. Replaces the default Access block pages (identity_denied / forbidden) with custom HTML, which can then be selected on an Access application.

Custom forbidden page

const page = yield* Cloudflare.Access.CustomPage("Forbidden", {
type: "forbidden",
customHtml: "<html><body><h1>Access denied</h1></body></html>",
});

Custom identity-denied page with an explicit name

const page = yield* Cloudflare.Access.CustomPage("Denied", {
name: "corp-identity-denied",
type: "identity_denied",
customHtml: "<html><body><h1>Who are you?</h1></body></html>",
});
const page = yield* Cloudflare.Access.CustomPage("Forbidden", {
type: "forbidden",
customHtml: "<html><body><h1>Still denied</h1></body></html>",
});

Source: src/Cloudflare/Access/GetIdentityProvider.ts

Looks up an existing Access identity provider by display name and/or type, returning its attributes — or undefined when nothing matches.

As a data source, invoke it at plan time via getIdentityProvider — the result is an Output resolved during plan/deploy and inert inside deployed bundles. Useful for referencing IdPs that are managed outside the stack (e.g. the dashboard-provisioned cloudflare WARP login method, whose display name is often ""). The implementation is registered by Cloudflare.providers().

As a runtime binding inside a Worker, provide GetIdentityProviderHttp — it mints a scoped AccountApiToken with the Access: Organizations, Identity Providers, and Groups Read permission and binds it into the Worker so the lookup can run at runtime.

GetIdentityProvider: Looking Up Identity Providers

Section titled “GetIdentityProvider: Looking Up Identity Providers”

Restrict an Access application to the managed WARP IdP

const warpIdp = Cloudflare.Access.getIdentityProvider({
type: "cloudflare",
});
yield* Cloudflare.Access.Application("Admin", {
domain: "admin.example.com",
allowedIdps: [warpIdp.identityProviderId.as<string>()],
});

Look up an IdP by display name

const okta = Cloudflare.Access.getIdentityProvider({ name: "Okta SSO" });

Look up an IdP at runtime inside a Worker

// init — bind the lookup
const findWarpIdp = yield* Cloudflare.Access.GetIdentityProvider({
type: "cloudflare",
});
// runtime — resolve the IdP
const warp = yield* findWarpIdp();

Source: src/Cloudflare/Access/Group.ts

A Cloudflare Zero Trust Access group — a reusable, account-scoped set of Access rule criteria. Groups are referenced from Access policies via a { group: { id } } rule, letting many policies share one membership definition.

Allow a single email domain

const group = yield* Cloudflare.Access.Group("ExampleDomain", {
include: [{ emailDomain: { domain: "example.com" } }],
});

Combine include, exclude and require rules

const group = yield* Cloudflare.Access.Group("UsEngineers", {
include: [{ emailDomain: { domain: "example.com" } }],
exclude: [{ email: { email: "intern@example.com" } }],
require: [{ geo: { countryCode: "US" } }],
});
const group = yield* Cloudflare.Access.Group("Team", {
include: [{ emailDomain: { domain: "example.com" } }],
});
const policy = yield* Cloudflare.Access.Policy("AllowTeam", {
decision: "allow",
include: [{ group: { id: group.groupId } }],
});

Source: src/Cloudflare/Access/IdentityProvider.ts

A Cloudflare Zero Trust Access identity provider — the login method (one-time PIN, generic OIDC/SAML, or a named provider like GitHub, Google, Okta, or Azure AD) users authenticate with before Access policies evaluate.

Props are a discriminated union on type: each provider type only accepts (and requires) its own config fields, so a missing directoryId on an azureAD IdP or a GitHub config on an oidc IdP is a compile-time error. The type is immutable (config shapes are disjoint per type — changing it replaces the IdP); name, config, and SCIM settings converge in place. Cloudflare masks secret config fields (clientSecret, API tokens) on read, so those fields diff against your previously declared props instead of observed cloud state.

By default the IdP is created at the account level (the modern Zero Trust organization scope); pass zoneId to scope it to a single zone (legacy zone-level Access). Moving between scopes replaces the IdP.

IdentityProvider: Creating an Identity Provider

Section titled “IdentityProvider: Creating an Identity Provider”

One-time PIN (no external dependencies)

const otp = yield* Cloudflare.Access.IdentityProvider("Pin", {
type: "onetimepin",
});

Generic OIDC provider

const oidc = yield* Cloudflare.Access.IdentityProvider("Sso", {
type: "oidc",
config: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
authUrl: "https://idp.example.com/authorize",
tokenUrl: "https://idp.example.com/token",
certsUrl: "https://idp.example.com/keys",
scopes: ["openid", "email", "profile"],
},
});

Microsoft Entra ID (Azure AD)

const entra = yield* Cloudflare.Access.IdentityProvider("Entra", {
type: "azureAD",
config: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
directoryId: "my-tenant-id",
supportGroups: true,
},
});

Zone-scoped IdP (legacy zone-level Access)

const zoneIdp = yield* Cloudflare.Access.IdentityProvider("ZoneSso", {
zoneId: zone.zoneId,
type: "github",
config: {
clientId: "my-client-id",
clientSecret: "my-client-secret",
},
});

IdentityProvider: Restricting an Application to an IdP

Section titled “IdentityProvider: Restricting an Application to an IdP”
yield* Cloudflare.Access.Application("Admin", {
domain: "admin.example.com",
allowedIdps: [oidc.identityProviderId],
});

Source: src/Cloudflare/Access/InfrastructureTarget.ts

A Cloudflare Access Infrastructure Target — a server (hostname + IPv4/IPv6 address) protected by Access for Infrastructure (SSH).

Targets are referenced by infrastructure Access applications, which attach SSH access policies to them. Hostname and IP are both mutable in place; the target’s identity is its Cloudflare-assigned UUID.

Basic IPv4 target

const target = yield* Cloudflare.Access.InfrastructureTarget("Bastion", {
hostname: "bastion.internal",
ip: { ipv4: { ipAddr: "10.0.0.5" } },
});

Target scoped to a virtual network

const vnet = yield* Cloudflare.Tunnel.VirtualNetwork("Staging", {});
const target = yield* Cloudflare.Access.InfrastructureTarget("DbHost", {
hostname: "db.staging.internal",
ip: {
ipv4: {
ipAddr: "10.4.0.10",
virtualNetworkId: vnet.virtualNetworkId,
},
},
});
// Hostname and IP update in place — same targetId, no replacement.
const target = yield* Cloudflare.Access.InfrastructureTarget("Bastion", {
hostname: "bastion.internal",
ip: { ipv4: { ipAddr: "10.0.0.6" } },
});

Source: src/Cloudflare/Access/KeyConfiguration.ts

The Cloudflare Zero Trust Access service-key rotation configuration for an account (/accounts/{account_id}/access/keys).

The key configuration is an account singleton — it always exists, so this resource never creates or deletes anything physical. Reconcile PUTs the rotation interval when the observed value differs from the desired one; destroy restores the interval the account had before Alchemy first managed it (captured as initialKeyRotationIntervalDays).

KeyConfiguration: Managing the rotation interval

Section titled “KeyConfiguration: Managing the rotation interval”

Rotate Access service keys every 30 days

const keys = yield* Cloudflare.Access.KeyConfiguration("Keys", {
keyRotationIntervalDays: 30,
});

Inspect rotation status

const keys = yield* Cloudflare.Access.KeyConfiguration("Keys", {
keyRotationIntervalDays: 90,
});
// keys.daysUntilNextRotation, keys.lastKeyRotationAt

Source: src/Cloudflare/Access/McpPortal.ts

A Cloudflare Zero Trust AI Controls MCP portal — a hosted gateway that aggregates MCP servers behind a single Access-protected hostname so administrators can govern which AI tools and prompts are exposed to users.

The product surface is in beta and requires the AI Controls entitlement; accounts without it receive the typed Forbidden error on all writes. Attaching servers to the portal is managed out of band (a future Cloudflare.Access.McpServer resource).

Minimal portal

const portal = yield* Cloudflare.Access.McpPortal("AiPortal", {
hostname: "mcp.example.com",
});

Portal with gateway egress

const portal = yield* Cloudflare.Access.McpPortal("AiPortal", {
hostname: "mcp.example.com",
description: "Company-approved AI tools",
secureWebGateway: true,
});

Source: src/Cloudflare/Access/Organization.ts

Account-level Cloudflare Zero Trust organization settings — the team domain, login branding, session lifetimes, WARP authentication toggle, etc.

Wraps PUT /accounts/{account_id}/access/organizations.

Organization: Configuring the organization

Section titled “Organization: Configuring the organization”
const org = yield* Cloudflare.Access.Organization("Org", {
authDomain: "acme.cloudflareaccess.com",
name: "Acme",
sessionDuration: "24h",
allowAuthenticateViaWarp: true,
loginDesign: {
logoPath: "https://acme.example/logo.png",
backgroundColor: "#111111",
textColor: "#ffffff",
},
});

Source: src/Cloudflare/Access/Policy.ts

A reusable, account-scoped Cloudflare Access policy. Distinct from the inline policies attached directly to an Application — a reusable policy can be referenced by multiple applications by id.

Allow a single email domain

const policy = yield* Cloudflare.Access.Policy("AllowExampleDomain", {
decision: "allow",
include: [{ emailDomain: { domain: "example.com" } }],
});

Allow everyone but require purpose justification

const policy = yield* Cloudflare.Access.Policy("OpenWithJustification", {
decision: "allow",
include: [{ everyone: {} }],
purposeJustificationRequired: true,
sessionDuration: "12h",
});
const policy = yield* Cloudflare.Access.Policy("EngineersExceptInterns", {
decision: "allow",
include: [{ emailDomain: { domain: "example.com" } }],
exclude: [{ email: { email: "intern@example.com" } }],
require: [{ geo: { countryCode: "US" } }],
});

Source: src/Cloudflare/Access/ServiceToken.ts

A Cloudflare Zero Trust Access service token. Service tokens let machine-to-machine clients authenticate to Access-protected applications by sending the CF-Access-Client-ID / CF-Access-Client-Secret headers.

The client secret is only revealed by Cloudflare on create and rotate; the provider stores it redacted in state and carries it forward across reads.

Basic token with a generated name

const token = yield* Cloudflare.Access.ServiceToken("Ci", {});
// token.clientId / token.clientSecret authenticate requests

Token with an explicit name and validity

const token = yield* Cloudflare.Access.ServiceToken("Deploys", {
name: "deploy-bot",
duration: "17520h", // 2 years
});
const token = yield* Cloudflare.Access.ServiceToken("Ci", {
clientSecretVersion: 2, // was 1 — bumping rotates the secret
});
const token = yield* Cloudflare.Access.ServiceToken("Ci", {});
const policy = yield* Cloudflare.Access.Policy("AllowCi", {
decision: "non_identity",
include: [{ serviceToken: { tokenId: token.serviceTokenId } }],
});

Source: src/Cloudflare/Access/Tag.ts

A Cloudflare Zero Trust Access tag. Tags are plain labels that can be attached to Access applications to group and filter them.

The tag’s name is its identity — there is nothing to update in place, so renaming replaces the tag.

Tag with a generated name

const tag = yield* Cloudflare.Access.Tag("Team", {});

Tag with an explicit name

const tag = yield* Cloudflare.Access.Tag("Team", {
name: "platform-team",
});
const tag = yield* Cloudflare.Access.Tag("Team", { name: "platform-team" });
const app = yield* Cloudflare.Access.Application("Dashboard", {
type: "self_hosted",
domain: "dash.example.com",
tags: [tag.name],
});