{"id":41980201,"url":"https://github.com/globus/example-search-portal-with-embeds","last_synced_at":"2026-01-25T23:41:01.504Z","repository":{"id":261852855,"uuid":"885534520","full_name":"globus/example-search-portal-with-embeds","owner":"globus","description":"An example search portal that includes content embedded from a Globus Collection (via GCS HTTPS Access)","archived":false,"fork":false,"pushed_at":"2025-01-22T17:52:30.000Z","size":173,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-01-22T18:37:17.654Z","etag":null,"topics":["example","portal","serverless"],"latest_commit_sha":null,"homepage":"https://globus.github.io/example-search-portal-with-embeds/","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/globus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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":"2024-11-08T19:20:38.000Z","updated_at":"2025-01-22T17:52:27.000Z","dependencies_parsed_at":"2025-01-22T18:38:38.149Z","dependency_job_id":null,"html_url":"https://github.com/globus/example-search-portal-with-embeds","commit_stats":null,"previous_names":["globus/example-search-portal-with-embeds"],"tags_count":0,"template":false,"template_full_name":"globus/template-search-portal","purl":"pkg:github/globus/example-search-portal-with-embeds","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globus%2Fexample-search-portal-with-embeds","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globus%2Fexample-search-portal-with-embeds/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globus%2Fexample-search-portal-with-embeds/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globus%2Fexample-search-portal-with-embeds/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/globus","download_url":"https://codeload.github.com/globus/example-search-portal-with-embeds/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/globus%2Fexample-search-portal-with-embeds/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28761816,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T23:06:19.311Z","status":"ssl_error","status_checked_at":"2026-01-25T23:03:50.555Z","response_time":113,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["example","portal","serverless"],"created_at":"2026-01-25T23:40:59.856Z","updated_at":"2026-01-25T23:41:01.499Z","avatar_url":"https://github.com/globus.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Example: Serverless Search Portal with Globus Embeds\n\nThis repository is an example of the [@globus/template-search-portal](https://github.com/globus/template-search-portal)\n\nYou can create your own portal with similar functionality by following the [**Creating Your Own Research Search Portal**](https://github.com/globus/template-search-portal?tab=readme-ov-file#creating-your-own-static-research-search-portal) section in the template repository and then referencing the sections below.\n\n## Background\n\nMetadata in a Globus Search often includes references to assets hosted on a Globus Connect Server. When the Globus Connect Server (and Collection) is configured to support [HTTPS Access](https://docs.globus.org/globus-connect-server/v5.4/https-access-collections/) these assets can be configured to be embedded in search results (e.g. \"preview\").\n\n## Pre-Requisites\n\n- A [Globus Search Index](https://docs.globus.org/api/search/) you administer.\n- A Globus Collection that hosts data relevant to the documents in your Globus Search Index.\n- A Search Portal, created following: [**Creating Your Own Research Search Portal**](https://github.com/globus/template-search-portal?tab=readme-ov-file#creating-your-own-static-research-search-portal) in our template repository, **with Authentication enabled.**\n\n\n## Globus Embeds\n\nA Globus Embed is an asset that is rendered to your portal from a configured Globus Connect Server via HTTPS. When rendering these assets, there are [performance](#performance-considerations) and [security](#security-considerations). In both cases, we have made a best effort in embedding assets in a performant and secure way, but be sure to review and understand these considerations before enabling this functionality.\n\nWhen providing a `FieldDefinition` to a component (e.g. `Result`), the referenced `property` can be processed as an embed by:\n\n- Setting the `property` to a JSON pointer to the location of the asset (the GCS HTTPS URL and path).\n- Setting `\"type\"` to `\"globus.embed\"`\n- Providing the UUID of the `collection` the asset is hosted on via `options`[^1]\n\n[^1]: The `options.collection` configuration is required in order to ensure the proper authorization token is sent along with the HTTPS request for the asset; At this time, the collection cannot be derived from the asset HTTPS URL alone.\n\nhttps://github.com/globus/example-search-portal-with-embeds/blob/1a4fae30e9e52124e3a8e150b1e18cdc7ab5091a/static.json#L31-L38\n\n\u003cdetails\u003e\n  \u003csummary\u003e\n    \u003ccode\u003eGMetaResult\u003c/code\u003e Reference\n  \u003c/summary\u003e\n\n```json\n\n{\n  \"@datatype\": \"GMetaResult\",\n  \"@version\": \"2019-08-27\",\n  \"subject\": \"5352507d-1293-4827-8ba9-c2d4a0eb78d1\",\n  \"entries\": [\n    {\n      \"content\": {\n        \"path\": \"/portal/catalog/dataset_atl\",\n        \"name\": \"Atlanta International Airport Climate Data (ATL)\",\n        \"id\": \"5352507d-1293-4827-8ba9-c2d4a0eb78d1\",\n        \"region\": \"south\",\n        \"sample\": \"https://g-fe1c1.fd635.8443.data.globus.org/portal/catalog/dataset_atl/1951.csv\",\n        \"tags\": [\n          \"airport\"\n        ],\n        \"globus\": {\n          \"transfer\": {\n            \"type\": \"directory\",\n            \"path\": \"/portal/catalog/dataset_atl\",\n            \"collection\": \"a6f165fa-aee2-4fe5-95f3-97429c28bf82\"\n          }\n        }\n      },\n      \"entry_id\": null,\n      \"matched_principal_sets\": []\n    }\n  ]\n}\n```\n\u003c/details\u003e\n\n\nUsing this configuration, the portal code will introspect the `Content-Type` returned by the asset and attempt to render the asset for the end user.\n\nIn this particular case, because we are referencing a valid CSV, the portal will render the data as a table.\n\n\u003cimg width=\"1061\" alt=\"Screenshot 2024-11-12 at 3 57 05 PM\" src=\"https://github.com/user-attachments/assets/c2bef5f3-a2a0-46fd-9859-caeb1b0b64f1\"\u003e\n\nFor all other `Content-Type` values the portal will attempt to render the asset as an [`\u003cobject\u003e` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object) with the returned `Content-Type` as the [`type` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/object#type). By using an HTML standard as the default rendering method, the resource will be rendered to the screen based on the end-user's browser environment.\n\nThe `type` attribute of the rendered tag can be override by providing an explicit `mime` type in your `FieldDefinition.options`.\n\n\u003cdetails\u003e\n  \u003csummary\u003eExample with \u003ccode\u003eoptions.mime\u003c/code\u003e\u003c/summary\u003e\n\n```json\n  {\n    \"label\": \"Sample\",\n    \"property\": \"entries[0].content.sample\",\n    \"type\": \"globus.embed\",\n    \"options\": {\n      \"collection\": \"a6f165fa-aee2-4fe5-95f3-97429c28bf82\",\n      \"mime\": \"application/pdf\"\n    }\n  }\n```  \n\u003c/details\u003e\n\n\n### Renderers\n\nIn addition to the default rendering behaviors of a Globus Embed Field, you can opt-in to specific `renderer` built to process and render referenced assets.\n\n#### Plotly\n\nThe Plotly renderer uses the [Plotly JavaScript Open Source Graphing Library](https://plotly.com/javascript/) to render the referenced asset.\n\nTo use this functionality `options.renderer` should be set to `plotly` and the sourced asset should be a JSON object that is compatible with [`Plotly.newPlot`](https://plotly.com/javascript/plotlyjs-function-reference/#plotlynewplot) single configuration object parameter.\n\nhttps://github.com/globus/example-search-portal-with-embeds/blob/1a4fae30e9e52124e3a8e150b1e18cdc7ab5091a/static.json#L39-L47\n\n![Screen Cast 2024-11-12 at 4 09 06 PM](https://github.com/user-attachments/assets/406efc21-7e98-4c05-a358-92e52229ef89)\n\n\n\n### Performance Considerations\n\n**Assets that are referenced using the `globus.embed` configuration will be requested using HTTPS to the configured Collection.** Factors such as, the total size of individual embedded assets and the number of embedded assets rendered on a single page will result in HTTPS requests made to in\n\n\n### Security Considerations\n\n**We have put forth a best effort in supporting embedding assets in your search portal in a secure way, but it is important to note that embedding, untrusted, user-provided content can be dangerous for your end-users.**\n\nOur default protections are based on best practices related to avoiding [Cross Site Scripting (XSS)](https://owasp.org/www-community/attacks/xss/) – please be sure to consider these when enabling embedding _and_ making adjustments to the defaults.\n\n#### Default Protections\n\n- A default [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) is set using a `\u003cmeta\u003e` tag.\n  - You can provide your own value for this tag using `data.attributes.contentSecurityPolicy` in your `static.json`, or setting the value to `false` to remove the `\u003cmeta\u003e` tag.\n- The default `\u003cobject\u003e` based embed is rendered as a child of a [`\u003ciframe\u003e`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) where the [`sandbox`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox), [`allow`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#allow), and additional attributes are used to create as much as an isolated runtime environment as possible.\n\n\n## Optimizaitons\n\n### Requesting HTTPS Scope on Sign In\n\nGlobus Connect Server requires tokens to include [specific scopes](https://docs.globus.org/globus-connect-server/v5.4/https-access-collections/#access_tokens_for_https) when requesting assets over HTTPS. By default, when you use the `globus.embed` type the underlying component will attempt to parse authorization errors from the request and prompt the user to address them (where possible). This default implementation often means the first render of an asset will be a consent error the user needs to address (pictured below).\n\n\u003cimg width=\"1030\" alt=\"Screenshot 2024-11-12 at 4 19 03 PM\" src=\"https://github.com/user-attachments/assets/0aa41b7d-cbd4-4451-ae23-b4179449a2a8\"\u003e\n\nOne way to avoid this initial error is to prompt for the GCS-required scopes at _Sign In_ if the are known – the below example adds a known scope to the configured client.\n\n```jsonc\n{\n  \"data\": {\n    \"globus\": {\n      \"application\": {\n        \"client_id\": \"7442cbd9-2766-42b9-9512-9195b12ed167\",\n        \"scopes\": [\n          // The UUID here would reference the Collection where your assets are served from.\n          \"https://auth.globus.org/scopes/a6f165fa-aee2-4fe5-95f3-97429c28bf82/https\" \n        ]\n      }\n    }\n  }\n}\n```\n\n**You very likely want to include this optimization when using `globus.embed`**[^2]. The only time you might not want to use this configuration is if your embedded assets are being loaded from many (dynamic) collections. In these cases, you might not be able to enumerate the scopes as a configuration option, or degrade user experience by asking for unnecessary consent at Sign In.\n\n\n[^2]: **This** portal does not use this configuration as a way to demonstrate the default behaviors.\n\n\n\n\n\n\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobus%2Fexample-search-portal-with-embeds","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fglobus%2Fexample-search-portal-with-embeds","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fglobus%2Fexample-search-portal-with-embeds/lists"}