{"id":19584722,"url":"https://github.com/oracle-samples/xfc","last_synced_at":"2025-04-27T11:32:20.357Z","repository":{"id":20956745,"uuid":"91378100","full_name":"oracle-samples/xfc","owner":"oracle-samples","description":"A javascript library for Cross Frame Communication","archived":false,"fork":false,"pushed_at":"2023-11-29T22:56:19.000Z","size":1293,"stargazers_count":17,"open_issues_count":32,"forks_count":24,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-10-06T17:20:33.338Z","etag":null,"topics":["cross-domain","iframe-embeds","javascript"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oracle-samples.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-05-15T19:45:47.000Z","updated_at":"2024-05-03T07:18:00.000Z","dependencies_parsed_at":"2024-06-18T18:38:43.264Z","dependency_job_id":"32120571-c0bb-41a9-8f49-93e1ba641a3e","html_url":"https://github.com/oracle-samples/xfc","commit_stats":{"total_commits":118,"total_committers":24,"mean_commits":4.916666666666667,"dds":0.788135593220339,"last_synced_commit":"40e2c8427b7fc137a8dba59738d5d8ae314331e3"},"previous_names":["oracle-samples/xfc"],"tags_count":25,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle-samples%2Fxfc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle-samples%2Fxfc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle-samples%2Fxfc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oracle-samples%2Fxfc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oracle-samples","download_url":"https://codeload.github.com/oracle-samples/xfc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224069554,"owners_count":17250454,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cross-domain","iframe-embeds","javascript"],"created_at":"2024-11-11T07:49:29.309Z","updated_at":"2024-11-11T07:49:30.301Z","avatar_url":"https://github.com/oracle-samples.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XFC (Cross-Frame-Container)\n\n[![Build Status](https://travis-ci.org/cerner/xfc.svg?branch=master)](https://travis-ci.org/cerner/xfc)\n[![npm version](https://badge.fury.io/js/xfc.svg)](https://badge.fury.io/js/xfc)\n\nThis project handles securely embedding web content into a 3rd party domain. Out of the box, it provides several features:\n\n* Clickjacking protection using either a trusted origin or secret\n* Automatic iFrame resizing\n* Event dispatching from embedded content into a framework\n\n## Usage\nInclude `xfc.js` in your project.\n\nEnsure `process.env.NODE_ENV` is set correctly in the build enviornment. Logging is only enabled in non-production environments. The environment can be set in webpack using the [DefinePlugin](https://webpack.js.org/guides/production-build/)\n\n```js\n// webpack.config.js\nconst webpack = require('webpack');\n\nmodule.exports = {\n  /*...*/\n  plugins:[\n    new webpack.DefinePlugin({\n      'process.env.NODE_ENV': JSON.stringify('production')\n    })\n  ]\n};\n```\n\n### Setting Up A Consumer\nThe consumer is the application which is embedding the 3rd party applications within it.\n\n```js\n// Create an app broker to manage embedded apps\nXFC.Consumer.init()\n\n// Mount the app at this URL and append its frame to the body element.\nvar frame = XFC.Consumer.mount(document.body, 'http://localprovider.com:8080/example/provider.html')\n```\n\nIf the embedded app does not know which domain to trust, it may require secret authorization.\n\n```js\nvar frame = XFC.Consumer.mount(document.body, 'http://localprovider.com:8080/example/provider_b.html', {secret: 'abc123'})\n```\n\nTo remove and clean up a mounted app, simply call `unmount` method.\n\n```js\nframe.unmount()\n```\n\nTo load a new page within the existing frame, simply call `load` method with the new URL.\n\n```js\nframe.load(newURL)\n```\n\n\n#### Iframe Resizing Config\nBy default, the height of iframe will automatically resize based on the height of the embedded content. This behavior can be changed by passing an extra option (`resizeConfig`) into `mount` method.\n\n```js\n// Pass scrolling as true to resizeConfig to make scrollbar show up.\nXFC.Consumer.mount(document.body, 'http://localprovider.com:8080/example/provider.html', { resizeConfig: { scrolling: true } });\n```\n\n`resizeConfig` is an object that accepts the following attributes.\n\n| name                    | type     | default value  | usage |\n|:---:                    |:---:     |:---:           |---    |\n| scrolling               | boolean  | `false`        | When set to be `true`, scrollbar may show up on iframe. |\n| autoResizeWidth         | boolean  | `false`        | When set to be `true`, iframe will autoresize on width instead of on height |\n| fixedHeight             | string   | empty string   | If specified (e.g. '200px'), the height will stay at the specified value.\u003cbr\u003e\u003cbr\u003e NOTE: setting this attribute will turn off autoresizing.|\n| fixedWidth              | string   | empty string   | If specified (e.g. '400px'), the width of iframe will stay at the specified value.\u003cbr\u003e\u003cbr\u003e NOTE: setting this attribute will turn off autoresizing. |\n| heightCalculationMethod | string   | `'bodyOffset'` | Accepted values:\u003cbr\u003e `'bodyOffset'` - use `document.body.offsetHeight`\u003cbr\u003e\u003cbr\u003e `'bodyScroll'` - use `document.body.scrollHeight`\u003cbr\u003e\u003cbr\u003e `'documentElementOffset'` - use `document.documentElement.offsetHeight`\u003cbr\u003e\u003cbr\u003e `'documentElementScroll'` - use `document.documentElement.scrollHeight`\u003cbr\u003e\u003cbr\u003e `'max'` - max of all of above options.\u003cbr\u003e\u003cbr\u003e `'min'` - min of all of above options.|\n| widthCalculationMethod  | string   | `'scroll'`     | Accepted values:\u003cbr\u003e `'bodyOffset'` - use `document.body.offsetWidth`\u003cbr\u003e\u003cbr\u003e `'bodyScroll'` - use `document.body.scrollWidth`\u003cbr\u003e\u003cbr\u003e `'documentElementOffset'` - use `document.documentElement.offsetWidth`\u003cbr\u003e\u003cbr\u003e `'documentElementScroll'` - use `document.documentElement.scrollWidth`\u003cbr\u003e\u003cbr\u003e `'scroll'` - max of `bodyScroll` and `documentElementScroll`\u003cbr\u003e\u003cbr\u003e `'max'` - max of all of above options.\u003cbr\u003e\u003cbr\u003e `'min'` - min of all of above options.|\n| customCalculationMethod | function | null           | When specified, XFC will use the given method to update iframe's size when necessary (e.g. dom changes, window resized, etc.)\u003cbr\u003e\u003cbr\u003e NOTE: context `this` is provided as iframe to this method, so in the method you can access the iframe by accessing `this` |\n| targetSelectors         | string   | null           | When the embedded page contains elements styled with `position: absolute`, the iframe resizing logic won't calculate the height of the embedded page correctly because those elements are removed from normal document flow.\u003cbr\u003e\u003cbr\u003eIn this case, targetSelectors can be used to specify those absolute positioned elements so that they will be taken into consideration when calculating the height of the embedded page. Multiple selectors may be specified by separating them using commas.\u003cbr\u003e\u003cbr\u003eIf not specified, normal resizing logic will be used.\u003cbr\u003e\u003cbr\u003e NOTE: this attribute can be also specified from Provider's side, e.g. `XFC.Provider.init({targetSelectors: '#target'})`|\n\n\n### Setting Custom Attributes on Iframe\nSometimes, it's useful for developers to add more attributes onto mounted iframes. A common use case, for instance, is adding `allow` attribute to `\u003ciframe\u003e` tag for cross-origin iframes in Chrome 64+ (See reference [here][3]). In those cases, we can pass an extra option called `iframeAttrs` into `mount` method as follows.\n\n```js\nXFC.Consumer.mount(document.body, 'http://localprovider.com:8080/example/provider.html', { iframeAttrs: { allow: 'geolocation; camera' }});\n```\n\nHere `iframeAttrs` is an object that contains entries, each of them being an entry of attribute's name and value.\n\n### Applying Visual Focus Indicator Style on Iframes\nCertain configuration within this library such as `fixedWidth` or `fixedHeight` may prevent the content to be fully displayed within the viewport, and thereby needing `scrolling` to be enabled. For keyboard only users, it is important to display a visual focus indicator to indicate where the focus currently is at to help guide them with page navigation.\n\nExample CSS and JS Code:\n\n```css\n  .iframe-focus-style {\n    outline: 2px dashed #000;\n    outline-offset: 1px;\n  }\n```\n\n```js\nXFC.Consumer.mount(document.body,\n  'http://localprovider.com:8080/example/provider.html',\n  {\n    iframeAttrs: {\n      id: \"frame-id\",\n      style: \"margin-top: 4px; margin-bottom: 4px;\",\n    },\n    focusIndicator: {\n      classNameFocusStyle: \"iframe-focus-style\",\n    }\n  }\n);\n```\n\nPlease note that the style associated with `classNameFocusStyle` will be applied to the iframe by setting the `classNameFocusStyle` to `class` attribute on the iframe when there is a focus event. During a blur event, the `classNameFocusStyle` name will be removed.\n\n### Monitoring Embedded App Lifecycles\n\nApplication lifecycles go through 3 stages as they load:\n\n1. ```mounted``` The application frame has been appended to the DOM and is loading the remote application site.\n2. ```launched``` The application frame has loaded and the embedded application has begun authorization sequence. At this time the app is loaded, but is hidden to prevent clickjacking.\n3. ```authorized``` The application has approved authorization and is now visible.\n4. ```unload``` The application frame is about to unload due to redirect or other causes.\n\nThese statuses are communicated to the consumer application environment in 2 ways.\n\n* ```data-status``` attribute on the embedded iFrame wrapper\n* A custom application event originating from the embedded iFrame.\n\n#### Styling Cross Frame Containers Based On Status\nThe cross frame container ```data-status``` attribute can be used as a styling hook to hide containers until they have authorized\n\n```css\n/* Hide mounted apps that haven't loaded or authorized */\n.xfc[data-status='mounted'],\n.xfc[data-status='launched'] {\n  display: none;\n}\n\n/* Reveal the app once its been authorized */\n.xfc[data-status='authorized'] {\n  display: block;\n}\n```\n\n### Listening for Lifecycle Events\nEvent listeners can be set up to listen for lifecycle changes to a cross frame container.\nThe target of the event will be the embedded application frame which is an instance of EventEmitter.\n\n```js\n// Listen for a container to trigger a mounted event\nframe.on('xfc.mounted', function() {\n  console.log('mounted', frame.wrapper);\n})\n\n// Listen for a container to trigger a launched event\nframe.on('xfc.launched', function() {\n  console.log('launched', frame.wrapper);\n})\n\n// Listen for a container to trigger an authorized event\nframe.on('xfc.authorized', function(detail) {\n  console.log('authorized', detail);\n})\n\n// Listen for a container to trigger an unload event\nframe.on('xfc.unload', function(detail) {\n  console.log('unloading', detail);\n})\n```\n\n### Fullscreen Events\nA provider application may request to launch another provider app fullscreen.\n\n```js\n// Simulate opening a fullscreen link.\n// In real implementation this would invoke a modal.\nframe.on('xfc.fullscreen', function(url) {\n  window.open(url)\n})\n```\n\n### Sending custom events to a provider\nEach frame of the consumer can send custom events to its embedded provider through its _trigger_ method.\n\n```js\n// The following code will emit 'fetchDetail' event with an object containing additional info (e.g. 'id')\n// onto the provider embedded in the frame\nframe.trigger('fetchDetail', {id: 10});\n```\n\n\n## Setting Up A Provider\nThe provider is the application which is embedded by the consumer.\n\n```js\n// Only load within http://localconsumer.com:8080 origins\nXFC.Provider.init({\n  acls: ['http://localconsumer.com:8080']\n})\n```\n\nIf the provider is being used across the same domain with different subdomains, a wildcard character can be set as the first character of the acl.\n\n```js\n// Only load within domain.com (http://test.domain.com, https://test2.domain.com, https://test.test2.domain.com, etc.)\nXFC.Provider.init({\n  acls: ['*.domain.com']\n})\n\n```\n\nIf the app is using secret authorization, it may pass in a secret and wildcard for authorization.\n\n```js\n// Don't know which origin to trust, trust all origins that know the secret.\nXFC.Provider.init({\n  acls: ['*'],\n  secret: 'abc123'\n})\n```\n\nIf the app is using a custom secret callback function for authorization, it may pass in a callback function for validation of the secret.\n\n```js\n// Don't know which origin to trust, trust all origins that know the secret.\nXFC.Provider.init({\n  acls: ['*'],\n  secret: function(secret) {\n    return Promise.resolve('Success');\n  }\n})\n```\n\nIf the case of secret validation failures, acceptance is a new Error\n\n```js\nXFC.Provider.init({\n  acls: ['*'],\n  secret: function(secret) {\n    return Promise.reject(new Error('Failure'));\n  }\n})\n```\n\nIf the app is using an alternate form a security and does require XFC to provide clickjacking support, a wildcard with no secret may be passed. Under these conditions, XFC will not hide the content and the consumer will automatically be authorized.\n\n```js\nXFC.Provider.init({\n  acls: ['*']\n})\n```\n\nIf the app wants to transmit details to frame after authorization, it may pass in an options object.\n\n```js\nXFC.Provider.init({\n  acls: ['*'],\n  options: { moreDetail: 'detail' }\n})\n```\n\nIf the app and framework wants to register new custom methods with JSONRPC, it may pass in an customMethods object and Provider\ncan call custom events on the frame using invoke method.\n\n```js\nXFC.Consumer.mount(\n  document.body,\n  'http://localprovider.com:8080/example/provider.html',\n  { customMethods: { add(x, y) { return Promise.resolve(x + y); } } }\n);\n```\n\n```js\nXFC.Provider.invoke('add', [1, 2]);\n```\n\n### Launching Fullscreen\nAn application may request to launch a pagelet fullscreen within the consumer application.\n\n```js\nXFC.Provider.init({ acls: ['http://localconsumer.com:8080'] })\nXFC.Provider.fullscreen('http://localconsumer.com:8080/workflow')\n```\n\n### Sending custom events to its frame\nProvider can send custom events to the frame where it's embedded through _trigger_ method.\n\n```js\n// The following code will emit 'ProviderURL' event with an additional object containing URL info\n// onto the frame where the provider is embedded\n// NOTE: trigger method can only be called after initializing provider.\nXFC.Provider.trigger('ProviderURL', {url: window.location.href});\n```\n\n### Sending http errors to its frame\nProvider can send http errors to its frame by calling _httpError_ method. This method will emit an 'xfc.provider.httpError' event onto the frame where the provider is embedded.\n\n```js\n// httpError accepts a parameter that is an object containing message's detail information.\nXFC.Provider.httpError({code: 500, message: 'Internal Server Error'});\n```\n\n## Browser Support\nAll supported browsers are defined in [here][1] with browserslist [queries][2].\n\n\n## Development\nAdd localconsumer.com to```/etc/hosts```.\nAdd localprovider.com to```/etc/hosts```.\n\nInstall dependencies and start the server.\n```\nnpm install\nnpm run dev\n```\n\nNavigate to http://localconsumer.com:8080/example\n\n\n[1]: ./.babelrc#L5\n[2]: https://github.com/ai/browserslist#queries\n[3]: https://dev.chromium.org/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle-samples%2Fxfc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foracle-samples%2Fxfc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foracle-samples%2Fxfc/lists"}