@@ -191,6 +191,30 @@ class TestController: public jsg::Object {
191191 JSG_RESOURCE_TYPE (TestController) {}
192192};
193193
194+ // Base class for the ctx.access object providing Cloudflare Access authentication context.
195+ // Subclass when embedding to provide an implementation.
196+ class AccessContext : public jsg ::Object {
197+ public:
198+ // Returns the audience claim from the Access JWT.
199+ //
200+ // The default implementation throws — only meaningful when overridden by the embedding.
201+ virtual kj::StringPtr getAud ();
202+
203+ // Fetches the full identity information for the authenticated user.
204+ //
205+ // The default implementation throws — only meaningful when overridden by the embedding.
206+ virtual jsg::Promise<jsg::JsValue> getIdentity (jsg::Lock& js);
207+
208+ JSG_RESOURCE_TYPE (AccessContext) {
209+ JSG_READONLY_INSTANCE_PROPERTY (aud, getAud);
210+ JSG_METHOD (getIdentity);
211+ JSG_TS_OVERRIDE (CloudflareAccessContext {
212+ readonly aud: string;
213+ getIdentity (): Promise<CloudflareAccessIdentity | undefined>;
214+ });
215+ }
216+ };
217+
194218class ExecutionContext : public jsg ::Object {
195219 public:
196220 ExecutionContext (jsg::Lock& js, jsg::JsValue exports)
@@ -239,24 +263,9 @@ class ExecutionContext: public jsg::Object {
239263 return js.undefined ();
240264 }
241265
242- // Called by the host runtime (edgeworker) to set the Access context for this request.
243- // Must be called before the worker's handler is invoked.
244- //
245- // Unlike other ExecutionContext fields (props, version, exports) which are injected through the
246- // constructor, access uses a post-construction setter because the Access context is assembled by
247- // the host runtime after ExecutionContext construction but before handler invocation. The access
248- // data (audience claim, identity fetcher) originates from the Cloudflare Access integration
249- // pipeline and is not available during ExecutionContext construction in edgeworker.
250- void setAccess (jsg::Lock& js, jsg::JsRef<jsg::JsValue> value) {
251- access = kj::mv (value);
252- }
253-
254- jsg::JsValue getAccess (jsg::Lock& js) {
255- KJ_IF_SOME (a, access) {
256- return a.getHandle (js);
257- }
258- return js.undefined ();
259- }
266+ // Returns an AccessContext for the current request, or empty jsg::Optional otherwise.
267+ // Called by the runtime to provide Cloudflare Access authentication context.
268+ jsg::Optional<jsg::Ref<AccessContext>> getAccess (jsg::Lock& js);
260269
261270 JSG_RESOURCE_TYPE (ExecutionContext, CompatibilityFlags::Reader flags) {
262271 JSG_METHOD (waitUntil);
@@ -268,9 +277,7 @@ class ExecutionContext: public jsg::Object {
268277 if (flags.getEnableVersionApi ()) {
269278 JSG_LAZY_INSTANCE_PROPERTY (version, getVersion);
270279 }
271- if (flags.getEnableCtxAccess ()) {
272- JSG_LAZY_INSTANCE_PROPERTY (access, getAccess);
273- }
280+ JSG_LAZY_INSTANCE_PROPERTY (access, getAccess);
274281
275282 if (flags.getWorkerdExperimental ()) {
276283 // TODO(soon): Before making this generally available we need to:
@@ -287,11 +294,6 @@ class ExecutionContext: public jsg::Object {
287294 }
288295
289296 // TODO(soon): This is getting unwieldy.
290- // Note: `access` is included unconditionally in all TS_OVERRIDE branches (unlike `version`
291- // which is gated by enableVersionApi). This is intentional — adding another conditional would
292- // double the branch count (from 4 to 8). Since `access` is optional (`?`), the type is
293- // correct regardless of whether the flag is enabled (the property will be undefined at runtime
294- // when the flag is off or when setAccess() hasn't been called).
295297 if (flags.getEnableCtxExports ()) {
296298 if (flags.getEnableVersionApi ()) {
297299 JSG_TS_OVERRIDE (<Props = unknown> {
@@ -336,19 +338,16 @@ class ExecutionContext: public jsg::Object {
336338 void visitForMemoryInfo (jsg::MemoryTracker& tracker) const {
337339 tracker.trackField (" props" , props);
338340 tracker.trackField (" version" , version);
339- tracker.trackField (" access" , access);
340341 }
341342
342343 private:
343344 jsg::JsRef<jsg::JsValue> exports;
344345 jsg::JsRef<jsg::JsValue> props;
345346 kj::Maybe<jsg::JsRef<jsg::JsValue>> version;
346- kj::Maybe<jsg::JsRef<jsg::JsValue>> access;
347347
348348 void visitForGc (jsg::GcVisitor& visitor) {
349349 visitor.visit (props);
350350 visitor.visit (version);
351- visitor.visit (access);
352351 }
353352};
354353
@@ -1058,6 +1057,6 @@ class ServiceWorkerGlobalScope: public WorkerGlobalScope {
10581057 api::WorkerGlobalScope, api::ServiceWorkerGlobalScope, api::TestController, \
10591058 api::ExecutionContext, api::ExportedHandler, \
10601059 api::ServiceWorkerGlobalScope::StructuredCloneOptions, api::Navigator, \
1061- api::AlarmInvocationInfo, api::Immediate, api::Cloudflare
1060+ api::AlarmInvocationInfo, api::Immediate, api::Cloudflare, api::AccessContext
10621061// The list of global-scope.h types that are added to worker.c++'s JSG_DECLARE_ISOLATE_TYPE
10631062} // namespace workerd::api
0 commit comments