interledger / web-monetization-extension Goto Github PK
View Code? Open in Web Editor NEWAn open-source browser extension that enables Web Monetization.
License: Apache License 2.0
An open-source browser extension that enables Web Monetization.
License: Apache License 2.0
With the current extension, checking for WM support is possible only after window
's load event. The page should be able to check for WM support before window
's load event is triggered.
WM extension compatibility with the Microsoft Edge browser.
TBC
TBD
Consider implementing this after streaming/continuous payments.
One-time payments should also be easier to implement once the continuous payment flow is complete.
Discussion point
Q: To discuss with Alex - does the one-time payment draw from the (monthly) budget already available, or not?
A: @raducristianpopa - To enhance UX, performing a one-time payment will deduct funds directly from the available (monthly) budget. This approach eliminates the need for an additional user action - approving a new grant.
Decision
alpha release: split once-off payments equally between the wallet addresses found on the website
Implement settings (setup) page.
Store secret keys into extension storage
Generate key/pair for extension
default
destructive
Note
For handling different variants CVA can be used.
WM extension compatibility with the Safari web browser.
TBC
Create a storage wrapper that has the ability to return values (single or multiple) in a type-safe manner.
TBD
Related tickets: #92 (please see PR review comments for the #93 ). The intention is to (possibly) move some of the code that to this wrapper, which was originally submitted in the PR.
Info: related to task #58
recurring: true | false
)Info: related to task #59
When a user has the WM extension installed, and they have successfully connected their wallet address to the extension, then the icon of the extension must look different depending on whether the user is currently visiting a web monetized website or not.
When a user is visiting a site:
CORS on MV3
Use MonetizationEvent class rather than using custom events
Add script injection into content script on MV3 with monetization event (if necessary add script CSP sha)
References on Chrome extension MV2 sunset timeline:
Prettier doesn't care if we've got unused vars lying around or if there's some weird scoping issue. This is where ESLint comes in handy. I reckon it's a good idea to keep our formatting and linting separate.
WM extension compatibility with the Firefox browser.
TBC
Note
If the slider is dragged to its minimum value (0.00), we can disable the WM for the current website.
Linked to #53
Content Script
authServer
, resourceServer
etc ... + a unique identifier - uuidv4, w/e)START_MONETIZATION
(payload wallet JSON - opt requestId TBD) event to background scriptMonetizationEvent
to the link taghidden
, stop monetization
visible
, resume monetization
Background
4. In background - call monetizationServer.start()
4.1 request incoming payment grant
4.2 create incoming payment - NO AMOUNT
4.3 try to send payment - if received status code is 403, rotate token
4.4 revoke grant after creating incoming payment
4.5 add incoming payment details to a map - (key: tabId, value: {"uuidv4" : { ...incomingPaymentDetails, active: true }, ...})
5. Create quote with receiveAmount = RATE_OF_PAY / 3600
to incoming payment (active: true)
6. Create outgoing payment with quote identifier (active: true)
7. Send "MONETIZATION_EVENT" to content script with MonetizationEvent
payload + request id
8. Watch for document visibility change
hidden
, stop monetization
false
for that requestIdvisible
, resume monetization
true
for that requestIdTODO:
For security reason, the OP usage is gonna be moved to a server.
sequenceDiagram
autonumber
%% Extension ->> Extension: When the extension is installed generate an <br> opaque token
%% Extension ->>+ Proxy: Send opaque token
%% Proxy ->> Proxy: Store the opaque token in <br> Redis/memory/SQLite ?
%% Proxy ->>- Extension: Response (200/400)
%% Note over Extension, Proxy: On every request, the extension is going <br> to include the opaque token in a header
Extension ->> Extension: User fills in the Connect your wallet form <br> and submits it
Extension ->>+ Proxy: Send form data (wallet address, <br> amount, amount type)
%% Proxy ->> Proxy: Verifies if the opaque token exists <br> and increases it's usage by 1
%% Note right of Proxy: The opaque token will be used for rate limiting. <br> 100req/min ?
Proxy ->> ASE Resource: Fetch wallet address information
ASE Resource ->> Proxy: Return wallet address information <br> or throws an error
break if the wallet address does not exist
Proxy ->> Extension: Return an error
end
Proxy ->> ASE Auth: Request a grant with access to quotes <br/> and outgoing payments
ASE Auth ->> Proxy: Return interactive grant details
Proxy ->>- Extension: Return interactive grant information <br> (interaction URL, continue URI and continue token) <br> continue URI and token needs to be stored <br> in extension until the user interacts with the grant
Extension ->> Browser: Open a new tab and navigate to the interaction URL
Extension ->> Extension: Listen for tab changes
Browser ->> Browser: User interacts with the grant <br> (accept/decline)
Browser ->> Browser: User gets redirect to the finish URL
Extension ->> Extension: Check if the finish URL has an interaction <br> reference
break if the user declined the grant - no interaction reference
Extension ->> Extension: Clear extension state & storage (delete <br> continue URI, token)
Note over Extension: The interaction "response" can be checked <br> by verifying the "result" query <br> parameter as well: <br> - "grant_rejected": user declined <br> - "grant_invalid": not in a state where it may be <br> accepted or rejected
end
Extension ->> Proxy: Send the interaction reference alongside with <br> the continuation URI and token
Proxy ->> ASE Auth: Make continuation request
ASE Auth ->> Proxy: Return access token
Note over Proxy, ASE Auth: This access token has access to quotes <br> and outgoing payments. There is no <br> need to have two access tokens <br> (one for quotes and one for outgoing <br> payments).
Proxy ->> Proxy: Generate a signed token based on the user wallet address
Note over Proxy: Look into ways to sign a token (JWT, Iron, <br> PASETO)
Proxy ->> Extension: Return access token(s) + manage URL(s) <br> + signed token
Extension ->> Extension: Store access token + manage URL and <br> the signed token in extension storage
Note over Extension: User wallet address <br> is now connected
sequenceDiagram
autonumber
WM Provider ->> WM Provider: Check if WM is enabled globally
break if WM is disabled
WM Provider-->WM Provider: STOP
end
WM Provider ->> WM Provider: Check if WM is enabled for the <br> current website
break if WM is disabled
WM Provider-->WM Provider: STOP
end
WM Provider ->> WM Page: Grabs website wallet address
WM Provider ->>+ ASE1 Resource: Request wallet address information
ASE1 Resource -->>- WM Provider: Returns wallet address information
WM Provider ->> WM Provider: If the JSON response passes validation <br> (matches OpenAPI Spec) fire the `load` <br> event on the link element
WM Provider ->>+ Proxy: Send the wallet address URL
Proxy ->> ASE1 Auth: Requests an incoming payment grant <br> with the "create" access
ASE1 Auth ->> Proxy: Returns access token
Proxy ->> ASE1 Resource: Create an incoming payment <br> with no amount
ASE1 Resource ->> Proxy: Returns incoming payment information
Proxy ->> ASE1 Auth: Revokes token
Note over Proxy, ASE1 Auth: Should the token be stored for later usage?
Proxy -->>- WM Provider: Returns incoming payment URL
WM Provider ->> WM Provider: Stores the incoming payment URL until the vistor <br> changes the tab or navigates to another website
Note left of WM Provider: We need to perform the flow from step 1 if: <br> - a navigation event is happening <br> - the wallet address that was found <br> in the page changed <br> - when the IP expires
break if WM is disabled
WM Provider-->WM Provider: STOP
end
WM Provider ->> WM Provider: Calculate the amount that needs to be sent <br> every second (based on the rate of pay) <br> and save it in the extension state for later use
loop Every second
WM Provider ->>+ Proxy: Send incoming payment URL, amount, access token + include signed token in the header <br> The manage URLs should be sent as well to rotate the tokens if they are expired.
Note over WM Provider, Proxy: We should not have a retry mechanism for failed requests. Even if <br> requests are failing we need to keep this loop going.
Proxy ->> ASE2 Resource: Create quote (with incoming payment URL and amount)
break if the incoming payment is expired we need to perform the whole flow again
WM Proxy -->> Proxy: STOP
end
critical if the access token is expired, rotate it and return the new token and manage URL
Proxy -->> ASE2 Auth: Rotate access token
ASE2 Auth -->> Proxy: Return new access token and manage URL
end
Note over Proxy: If the token got rotated, we will need to use the <br> new access token for the upcoming requests
ASE2 Resource ->> Proxy: Returns quote information
Proxy ->> ASE2 Resource: Create outgoing payment
ASE2 Resource ->> Proxy: ASE Resource Server response (success/failure)
Proxy ->>- WM Provider: Forward ASE Resource Server response
Note over WM Provider, Proxy: Since the access token manage URL is passed as well, the backend <br> can return the new access token and the new manage URL <br> if the token gets rotated.
end
Automatically applies labels to pull requests to provide additional context about the changes.
default
(used in the popup header to disable WM globally)sm
(used in the main page to disable continuous payments stream for a certain site)Note
For handling different variants CVA can be used.
CustomEvent
in Web Monetization PolyfillWhen using the CustomEvent
object in the Web Monetization Polyfill, all properties are bundled inside event.detail
. This behavior does not align with the Web Monetization specification, which expects these properties to be directly accessible on the event object.
To conform to the spec, an Event
subclass can be created. However, this subclass must be created in the main page JS context.
In MV2, directly injecting a <script>
tag with src=...
into the main document will not work reliably. The script will load asynchronously and may not execute before it's actually needed or used.
The workaround is to inject the script content directly, ensuring synchronous loading and execution. Here's a simplified version of the tested code for this:
function injectCode(code: string) {
inject(script => (script.innerHTML = code));
}
function inject(configure: (script: HTMLScriptElement) => void) {
const script = document.createElement('script');
configure(script);
document.documentElement.appendChild(script);
document.documentElement.removeChild(script); // Clean up
}
To handle CSP issues, you can include a content_security_policy
in the manifest.json
:
"content_security_policy": "script-src 'self' 'sha256-POLYFILL-HASH='; object-src 'self'"
In your build script:
const data = Buffer.from(polyfill.content, 'utf-8');
const digest = createHash('sha256').update(data).digest();
const polyfillHash = `sha256-${digest.toString('base64')}`;
v2.content_security_policy = v2.content_security_policy.replace(
'sha256-POLYFILL-HASH=',
polyfillHash
);
In MV3, the scripting
API from Chrome can be used to register a content script that runs in the main page JS context. Below is a sample TypeScript code snippet:
chrome.scripting.registerContentScripts([
{
id: 'wm-polyfill.js', // Unique identifier for the content script
matches: ['https://*/*', 'http://*/*'], // URL patterns where the script will run
js: ['wm-polyfill.js'], // JavaScript file to be injected
allFrames: true, // Run the script in all frames
world: 'MAIN', // Execute in the main world
runAt: 'document_start' // Timing for script injection
}
])
Note: You may need to deal with Duplicate script ID
issues due to the service worker life cycles. Handling this is an exercise left to the reader.
world
property to MAIN
is essential for the solution to work.test description
react-number-format
Note
We can have two separate input components (text and amount).
A separate label component can be added, which can be used for other form items as well.
The private key and key id should not be bundled with the extension
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.