Every sensitive capability an app uses through the FKN extension is gated by an explicit user consent prompt: per app, per capability, per scope. Grants are stored on the user’s device, every use is recorded in a 30-day on-device activity log, and the user can revoke any grant at any time from the extension’s dashboard.
As an app developer you don’t implement any of this. You call the API; the extension prompts. Your job is to ask well.
The first time an action needs an ungranted capability, the extension shows a consent sheet describing the capability, the requesting app, and the exact element scope. The user chooses allow once, allow for this session, always allow, or deny. A denial rejects your call with an error; handle it gracefully.
Pass a reason to tell the user why you are asking:
await
constframe:Frame
frame
.
locator: (selector:string) => Locator
locator('.controls')
.
getByText: (text:string) => Locator
getByText('Play')
.
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number |undefined;
y?: number |undefined;
} |undefined;
}) |undefined) =>Promise<void>
click({
reason?: string |undefined
reason: 'Start playback when you press play in this app' })
ensure(operation, options?) resolves the permission an operation needs without running it. Use it to front-load consent at a natural moment (app start, player mount) instead of interrupting mid-interaction:
ensure(operation, { subtree: true }) asks for the capability on an element and everything inside it. One prompt covers all descendant locators; the prompt marks the request with a “Whole area” badge so the user knows it is broad. On the frame root it becomes the whole-page wildcard.
// One prompt: "click, everything inside .controls"
// Covered: no further prompts, each use audit-logged as "covered"
await
constframe:Frame
frame.
locator: (selector:string) => Locator
locator('.controls').
locator: (selector:string) => Locator
locator('#play').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number |undefined;
y?: number |undefined;
} |undefined;
}) |undefined) =>Promise<void>
click()
await
constframe:Frame
frame.
locator: (selector:string) => Locator
locator('.controls').
locator: (selector:string) => Locator
locator('#mute').
click: (options?: (OperationTimeoutOptions & {
position?: {
x?: number |undefined;
y?: number |undefined;
} |undefined;
}) |undefined) =>Promise<void>
click()
Covered actions still appear in the user’s activity log, attributed to the covering grant. An exact grant or denial on a deeper element always beats a covering one.
Capabilities carry a severity. Low-severity capabilities that expose no user data (for example embedding an iframe, or a cookie-less cross-origin fetch) are granted automatically but still logged. Anything touching the user’s session, cookies, or page content always prompts.