phil / embed-bundle
Features of a Symfony site that other sites embed: a token signed for one origin by an ordered list of keys, the page framed by that origin alone through frame-ancestors, and a <phil-embed> element sized by a small postMessage protocol, origin checked on both sides, with a plain link without JavaScr
Package info
github.com/parler-haut-interagir-librement/embed-bundle
Type:symfony-bundle
pkg:composer/phil/embed-bundle
Requires
- php: >=8.5
- ext-sodium: *
- phil/url: ^0.1@dev
- psr/clock: ^1.0
- psr/log: ^3.0
- symfony/config: ^8.2
- symfony/console: ^8.2
- symfony/dependency-injection: ^8.2
- symfony/http-foundation: ^8.2
- symfony/http-kernel: ^8.2
- symfony/routing: ^8.2
- twig/twig: ^3.21
Requires (Dev)
- phil/browser-security-bundle: ^0.1@dev
- php-webdriver/webdriver: ^1.16
- phpunit/phpunit: ^13.4
- symfony/clock: ^8.2
- symfony/css-selector: ^8.2
- symfony/dom-crawler: ^8.2
- symfony/framework-bundle: ^8.2
- symfony/panther: ^2.4
- symfony/twig-bundle: ^8.2
- symfony/yaml: ^8.2
Suggests
- phil/browser-security-bundle: To write frame-ancestors into the site's content security policy rather than in a header of the bundle's own
- symfony/asset-mapper: To serve the frame controller without a build step
- symfony/stimulus-bundle: For the frame controller
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-13 22:46:34 UTC
README
Features of a Symfony site that other sites embed, framed by the origins it allows and nobody else.
phil/embed-bundle turns a page of the site — a calendar, a form, a list of events — into something another site can
paste into its own pages. The site gives that other site a code: a <phil-embed> element holding a plain link, and a
script. The element puts the page in a frame, at the height its content asks for, and keeps following it; a reader
without JavaScript follows the link. The frame's address carries a token, signed by the site for that embed and that
origin: the page is answered in the theme the token names, and may be framed by that origin alone — a token copied to
another site shows nothing there.
The height travels by a small postMessage protocol: the frame posts its readiness and its height to the origin of its
token, never to *; the element reads messages from its own frame alone, and only from the origin of its address; the
theme the other site asks for goes the other way, under the same checks. The bundle writes frame-ancestors through
phil/browser-security-bundle when it is installed, or in a header of its own.
It is for any Symfony site whose partners show its content: a venue's calendar on the sites of its artists, a registration form on a partner's page, a list of events on a city's portal.
Status
| Part | State |
|---|---|
Origin, SigningKey, EmbedClaims: an origin read by phil/url, a key, what a token says |
Implemented and tested |
EmbedTokenCodec: tokens pe1.hs256.<key>.<claims>.<signature>, an ordered list of keys |
Implemented, tested and fuzzed |
Embed, Embeds, EmbedTokens: the embeds of the configuration, tokens issued and read back into a context |
Implemented and tested |
EmbedRequestListener: the page of an embed by its route's _embed or #[Embeddable] |
Implemented and tested |
BrowserSecurityFraming, HeaderFraming: frame-ancestors for the token's origin, or none |
Implemented and tested, with and without phil/browser-security-bundle |
EmbedMessage: the frame protocol's reference, shared with the JavaScript by its fixtures |
Implemented, tested and fuzzed |
<phil-embed>: the frame at the height of its content, the theme, the link kept without JavaScript or when refused |
Implemented; driven in Chrome across two origins by the browser suite |
The frame controller: readiness, height and theme inside the frame |
Implemented; driven in Chrome by the browser suite |
EmbedCodes, phil_embed_code(), phil:embed:token: the code a partner pastes, a token from the console |
Implemented and tested |
EmbedScriptController: the script under a stable address, readable across origins |
Implemented and tested |
RemoteCommentThreads: the comments of a page carried by the forum of another site |
Not started: the port is declared, without an adapter until the forum exists |
| The Flex recipe: the bundle, a key from the environment, the script's route | Written in recipe/0.1, for the recipes repository of PHIL |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements, the bundle, the key, the route, the controller. |
| Configuration | Every option, and the services registered. |
| Embedding | Declaring an embed, its page, the code a partner pastes, the element and its fallback. |
| Tokens | The format, the keys and their rotation, the claims, what a copied token can do. |
| Protocol | The postMessage messages and the checks on both sides. |
| Framing | frame-ancestors, with and without phil/browser-security-bundle. |
| Extension points | What an application implements, configures, replaces or relies on. |
| Testing | The PHP and Node tests, the fuzz targets and the checks in a browser. |
| Reference | Every class and module of the bundle, with its role. |
Quick start
composer require phil/embed-bundle
# config/packages/phil_embed.yaml phil_embed: keys: - { id: '1', secret: '%env(EMBED_KEY)%' } embeds: calendar: origins: ['https://partner.example'] themes: [light, dark] default_theme: light
#[Route('/embed/calendar', defaults: ['_embed' => 'calendar'])] public function calendar(): Response { /* the page, with phil_embed_frame_attributes() on <html> */ }
<pre>{{ phil_embed_code('calendar', 'https://partner.example', 'calendar_embed', {}, url('calendar'), 'Our calendar') }}</pre>
Requirements
PHP 8.5 or later with the sodium extension, Symfony 8.2 and phil/url. For the frame controller, Stimulus 3 and
AssetMapper; for the policy written into the site's own, phil/browser-security-bundle.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.