The integration gives web editors direct access to files and folders from Mediaflow inside the Optimizely 13 interface, and allows them to embed images in on-page editing, blocks or the text editor (TinyMCE). It also makes it possible to embed video from Mediaflow using Mediaflow's media player.
The integration connects directly between the Optimizely 13 web server and the Mediaflow servers, which means that files do not need to be downloaded locally first when images are mounted.
Good to know
For performance and stability, mounted images are always copied to the web server (under "For this site" or "For all sites" in Optimizely 13). Your website is therefore not dependent on Mediaflow, and the integration does not affect performance for normal page views. All mounted images remain in Optimizely, and no changes to the website are required if you uninstall the integration.
System requirements
Check that the environment meets the following requirements before installing:
-
.NET 10 (
net10.0) -
Optimizely CMS 13 – compatible with
EPiServer.CMS.UI.Coreversion 12.17.1 or later (under version 13) - A Mediaflow account with at least one server key configured
- Permission to add NuGet packages and change the website configuration (
appsettings.json,Startup.csorProgram.cs)
The integration works both with self-hosted Optimizely CMS 13 and with Optimizely DXP. Sign-in with Opti ID is supported from version 1.2.5; see Opti ID (SSO). For DXP-specific configuration (secrets, environment transformation, deployment), see the section Install on Optimizely DXP below.
The integration is distributed as the NuGet package Mediaflow.Optimizely on Optimizely's NuGet feed.
Settings in Mediaflow
The integration can be used as soon as it has been installed. However, for the files to be available and used optimally, administrators must first have prepared the settings in Mediaflow.
Each server key is a Mediaflow OAuth refresh token. You retrieve it in Mediaflow under Settings > Integrations > EPiServer/Optimizely – the key is in the Refresh token field. The key determines which Mediaflow resources the user can access. With virtual role mapping (recommended), you can assign different keys to different editor groups.
You need the appropriate permission to view the integration settings. If they are not available, you can enter super administrator mode or contact a Mediaflow administrator in your organisation.
Install the integration
Step 1 – Add the NuGet feed and find the package
Mediaflow's integration is installed as a standard NuGet package. Optimizely has its own NuGet feed, which needs to be added for the package to be found. Instructions are available here: https://nuget.optimizely.com/feed/
Once the feed has been added, you can search for the integration in Visual Studio using Mediaflow.Optimizely. More information and the latest version are available on Optimizely's package page: https://nuget.optimizely.com/packages/mediaflow.optimizely/
Step 2 – Install the NuGet package
Use Optimizely's standard NuGet installation, or install it from the command line:
dotnet add package Mediaflow.Optimizely --version [desired version]Step 3 – Configure appsettings.json
All configuration takes place in appsettings.json and in Startup.cs (or Program.cs). Add a Mediaflow section to appsettings.json with your server key and the optional settings you want to customise.
{
"Mediaflow": {
"EnableMediaLibraryUpload": true,
"LimitEmbedType": "javascript",
"ForceAltText": true,
"SaveLocation": "page",
"VirtualRoleMapping": [
{
"VirtualRole": "*",
"ServerKey": "<your-mediaflow-server-key>"
}
]
}
}Settings reference
| Setting | Description |
|---|---|
Key |
Server key (OAuth refresh token) used when no VirtualRoleMapping entry matches the current user. The fallback key is provided to users in CmsEditors, CmsAdmins, WebEditors, WebAdmins, Administrators or Mediaflow. |
VirtualRoleMapping |
Maps Optimizely's virtual roles to Mediaflow server keys. Use "*" as a wildcard for all users. Each entry has VirtualRole (role name or "*") and ServerKey (the refresh token for the role). |
EnableMediaLibraryUpload |
When true: allows files to be uploaded from Mediaflow to Optimizely's media library. |
LimitEmbedType |
Limits how video is embedded (javascript or iframe). See our article on how to implement content in Optimizely. |
ForceAltText |
When true: the editor must enter alt text before an image can be mounted. |
SaveLocation |
Controls where uploaded media is saved. See below. |
AllowedMediaTypes |
Optional list of complete Optimizely media type names (for example Mediaflow.Optimizely.Models.Media.MediaflowImageMedia) to limit which content types uploads can create. |
FileTypeFilter |
Optional comma-separated list of file extensions sent to the Mediaflow picker (for example jpg,png,gif). |
Key and ServerKey
The ServerKey value in VirtualRoleMapping is your Mediaflow OAuth refresh token. Mediaflow recommends using VirtualRoleMapping with a "*" entry for a shared key, or separate entries per role when you need to segment content (see Segment content with role mapping).
SaveLocation
Controls where images are saved in Optimizely's media section. Note that images are always copied to the web server regardless of the selected value.
| Value | Description |
|---|---|
page |
Images are saved in the media folder for the page being edited. |
Another string (for example "MyMediaFolder") |
Images are saved in a global folder with that name under "For all sites". |
| Empty/not specified | Images are saved in a global default folder called Mediaflow. |
Step 4 – Register the integration in Startup.cs
Two registration calls are required: AddMediaflow() and UseMediaflow(). The admin page /mediaflow/settings is precompiled and does not require Razor runtime compilation. Add AddMediaflowViews() if the website uses Mediaflow's bundled display templates – this is recommended if you use TinyMCE, as the display template keeps the alt text, description and photographer for images up to date on the published page. Add these in Startup.cs or the equivalent in your Program.cs.
ConfigureServices
Choose the option that suits your website.
Minimum registration
using EPiServer.Cms.TinyMce.Core;
using Mediaflow.Optimizely;
public void ConfigureServices(IServiceCollection services)
{
services
.AddCms()
// ... other Optimizely services ...
.AddMediaflow();
// Optional: bundled display templates (recommended with TinyMCE)
// services.AddCms().AddMediaflow().AddMediaflowViews();
// Optional: enable the Mediaflow button in TinyMCE (see article 2)
}Option B – the website already enables Razor runtime compilation
Keep your existing call and add Mediaflow's view support after it (only applies if you use the display templates):
using EPiServer.Cms.TinyMce.Core;
using Mediaflow.Optimizely;
public void ConfigureServices(IServiceCollection services)
{
services
.AddCms()
// ... other Optimizely services ...
.AddMediaflow();
services.AddControllersWithViews().AddRazorRuntimeCompilation();
// Mediaflow support for display templates – after runtime compilation!
services.AddMediaflowViews();
}Configure (request pipeline)
Place UseMediaflow() after UseAuthorization() and before UseEndpoints():
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseMediaflow();
app.UseEndpoints(endpoints =>
{
endpoints.MapContent();
});
}What each call does
| Call | Function |
|---|---|
AddMediaflow() |
Binds the Mediaflow configuration, registers the protected module, the settings service, the Mediaflow.Admins and Mediaflow.Editors authorisation policies (including Opti ID when registered), and the precompiled settings page. |
AddMediaflowViews() |
Enables Razor runtime compilation and view support for Mediaflow's bundled display templates, including the one that updates metadata for images in TinyMCE fields. It is not required for /mediaflow/settings. |
UseMediaflow() |
Registers a file provider alias so that the Optimizely shell can find the add-on's static resources under /EPiServer/Mediaflow.Optimizely/. |
AddMediaflowTinyMcePlugin() |
Adds SVG support to TinyMCE and registers Mediaflow's insert media button. Only required if content types use XhtmlString properties with TinyMCE (see article 2). |
Step 5 – Set permissions
Mediaflow is not open to all groups in Optimizely, in order to restrict access and prevent unauthorised use of Mediaflow.
The permitted groups are CmsEditors, CmsAdmins, WebEditors, WebAdmins, Administrators and the Mediaflow group, which you can create and add members to in order to grant them access.
The add-on looks up a Mediaflow server key for the current user through VirtualRoleMapping. If no mapping matches, the fallback key Key is used for users in the roles above. If neither a mapping nor Key exists, the user cannot authenticate with Mediaflow. The user must always be signed in to the CMS – even with a "*" entry.
If the website uses Opti ID, map to CmsEditors and CmsAdmins (or "*") instead of the older WebAdmins/WebEditors roles. Custom roles in VirtualRoleMapping must exist in Opti ID Admin Centre (or your IdP) and be synchronised to the CMS at sign-in. If you map CMS virtual roles, set SupportsClaims: true for them in appsettings.json.
Make sure that editors who are to use the picker belong to a role with a matching VirtualRoleMapping entry, or add them to the Mediaflow group and configure a suitable key.
Segment content with role mapping (optional)
By default, all users in Optimizely 13 access the same shared content when images are mounted – unless virtual role mapping is configured. If you have several editor groups working with different parts of the website, you can segment the content so that groups have access to different Mediaflow folders. This is done using Optimizely's Virtual Role and VirtualRoleMapping in appsettings.json.
Note: Segmenting content in this way requires a unique server key in Mediaflow for each group. This may involve a cost depending on your agreement.
"Mediaflow": {
"VirtualRoleMapping": [
{ "VirtualRole": "CmsAdmins", "ServerKey": "ZZXXCCVVBB" },
{ "VirtualRole": "CmsEditors", "ServerKey": "AABBCCDDEE" },
{ "VirtualRole": "*", "ServerKey": "QQWWEERRTT" }
]
}The add-on checks role membership in list order and uses the first matching entry. Place specific roles before the wildcard "*".
Opti ID (SSO)
From version 1.2.5, the integration works on websites that sign editors in with Opti ID. AddMediaflow() detects the Opti ID scheme and adds it to Mediaflow's authorisation policies. The order of AddMediaflow() and AddOptimizelyIdentity() does not matter, and no additional Mediaflow configuration is required.
services.AddOptimizelyIdentity(useAsDefault: false);
services.AddMediaflow();With useAsDefault: false, the policies accept both an Opti ID session and a session from the website's default sign-in (for example ASP.NET Identity).
Custom policies (optional). If you want to control roles or schemes yourself, you can provide custom policies. Policies under Constants.AdminPolicy and Constants.EditorsPolicy are not overwritten. Specify all schemes to be accepted – if you specify only one, the others are ignored.
services.AddMediaflow(authorization =>
{
authorization.AddPolicy(Constants.AdminPolicy, policy =>
{
policy.AddAuthenticationSchemes(OptimizelyIdentityDefaults.SchemeName);
policy.RequireRole("CmsAdmins");
});
});Cookies. Opti ID cookies (prefix oid-) require SameSite=None and Secure. A site-wide cookie policy with Lax or Strict makes Mediaflow see the user as signed out and return 401. Adjust the cookie policy on the website (see Troubleshooting).
Install on Optimizely DXP
If your Optimizely CMS 13 website runs on Optimizely DXP, the add-on itself is the same NuGet package and uses the same code registration as a self-hosted website. Follow steps 1–4 above. The differences concern how you configure secrets per environment, deploy the code package and ensure outgoing network access.
Include the package in the code package
Add Mediaflow.Optimizely as a standard NuGet reference in your CMS project. When you build and publish a DXP code package (.cms.app.*.nupkg), the add-on is included automatically. Deploy through your usual DXP flow (Integration → Preproduction → Production). No separate DXP-specific package or module upload is required.
Configure settings per environment
DXP applies environment-specific configuration through transform files (appsettings.Integration.json, appsettings.Preproduction.json, appsettings.Production.json) that overwrite values in appsettings.json during deployment.
Put non-secret Mediaflow settings in the appropriate transform file – for example SaveLocation, ForceAltText, LimitEmbedType and the structure for VirtualRoleMapping (role names without keys):
{
"Mediaflow": {
"EnableMediaLibraryUpload": true,
"LimitEmbedType": "javascript",
"ForceAltText": true,
"SaveLocation": "page",
"VirtualRoleMapping": [
{ "VirtualRole": "*", "ServerKey": "" }
]
}
}Leave ServerKey empty in version control and fill in the actual values through DXP App Settings (see below). See Configuring environments in Optimizely DXP.
Store server keys as secrets
Mediaflow server keys are OAuth refresh tokens and must be treated as secrets. If you use version control, exclude this information.
On DXP, add them through PaaS Portal → App Settings for each environment. The settings are applied at the next deployment. See Manage app settings in Optimizely DXP.
Simplest option – one server key:
| Name | Example value |
|---|---|
Mediaflow:Key |
<your-mediaflow-server-key> |
Role mapping – override individual keys:
| Name | Purpose |
|---|---|
Mediaflow:VirtualRoleMapping:0:ServerKey |
Server key for the first mapping entry |
Mediaflow:VirtualRoleMapping:1:ServerKey |
Server key for the second mapping entry |
Define the VirtualRole names in the transform file; override only the ServerKey values in the PaaS Portal. App Settings take precedence over appsettings.json and transform files.
Startup registration on DXP
Use the same registration as for any CMS 13 website. If your DXP solution already calls AddCmsCloudPlatformSupport(), keep it – it does not replace the Mediaflow registration. Both are required:
services
.AddCms()
// ... other Optimizely services ...
.AddMediaflow(); // + .AddMediaflowViews() if needed, see step 4
if (!env.IsDevelopment())
{
services.AddCmsCloudPlatformSupport(configuration);
}Also add app.UseMediaflow() to the request pipeline as in step 4. Many DXP projects already have Razor runtime compilation – if you add the display templates, use Option B in step 4. If the DXP solution uses Opti ID, see Opti ID (SSO).
Outgoing network access
The CMS application server must reach Mediaflow over HTTPS for authentication and file operations:
-
accounts.mediaflow.com– OAuth token requests -
api.mediaflow.com– Mediaflow API
Editors' browsers also load the picker's resources from mfstatic.com and may retrieve thumbnails from m.mediaflow.com. These are client requests and are not blocked by DXP's server network, but ad blockers or corporate proxies on client computers may interfere.
If the picker does not load or uploads fail with an authentication error on DXP but work locally: confirm with Optimizely Support or your network team that outgoing traffic to the endpoints above is permitted from the CMS App Service.
Admin settings and databases
The built-in admin interface at /mediaflow/settings stores overrides in Optimizely's Dynamic Data Store, which is located in the environment's SQL database. Settings changed in Integration are not transferred to Production – configure each environment separately, or use App Settings and transform files for a consistent baseline per environment.
Verify the installation
After restarting the website:
- Open the browser's developer console while signed in to the CMS. Confirm that there are no 404 errors for paths under
/EPiServer/Mediaflow.Optimizely/. - Sign in as a CMS editor and open a page with a Mediaflow property or a TinyMCE field. Confirm that the Mediaflow picker or button is displayed.
- As an administrator (CmsAdmins, WebAdmins or Administrators – including via Opti ID), open
/mediaflow/settingsand confirm that the admin page loads. Here you can overrideForceAltText,VirtualRoleMappingandFileTypeFilterat runtime; saved values take precedence overappsettings.json. - Opti ID: sign in through Opti ID and open the Mediaflow picker. If
/api/mediaflow/settingsreturns 401 for a signed-in user, see Troubleshooting.
Upgrade from an older version
When upgrading from 1.1.x to 1.2.x: rebuild Optimizely's link database or re-save content that uses Mediaflow properties after the upgrade. Version 1.2.x tracks content references (soft links) for Mediaflow properties, which drives Optimizely's References view. Existing content is not indexed automatically until the link database has been rebuilt or the affected pages and blocks have been re-saved.
To 1.2.5: If you added AddMediaflowViews() solely for the admin page, it can be removed. Keep it if you use TinyMCE, so that metadata for inserted images follows the media object. If the website has its own Views/Shared/DisplayTemplates/XhtmlString.cshtml, render through Mediaflow's extension:
@using Mediaflow.Optimizely.Utility
@model EPiServer.Core.XhtmlString
@{ Html.RenderXhtmlString(Model.WithMediaflowMetadata()); }Troubleshooting (installation and operation)
| Problem | Probable cause | Action |
|---|---|---|
404 error under /EPiServer/Mediaflow.Optimizely/
|
UseMediaflow() is missing or incorrectly placed |
Check that the call is after UseAuthorization() and before UseEndpoints()
|
| The picker does not open / is empty | Missing server key or role | Check VirtualRoleMapping and user roles |
| Display templates are not rendered |
AddMediaflowViews() is missing or is placed before runtime compilation |
Place the call after AddRazorRuntimeCompilation() (Option B) |
| References incomplete after upgrade | Link index has not been rebuilt | Rebuild the link database or re-save the affected pages |
| Works in Integration but not Production | Different keys/settings per environment | Check DXP App Settings and the admin UI for each environment |
| The Mediaflow button displays "undefined" or the picker returns 401 with Opti ID | Version earlier than 1.2.5, or Opti ID cookies are being rewritten by the website's cookie policy | Upgrade to 1.2.5. Set SameSite=None and Secure for cookies beginning with oid- and .AspNetCore
|
/mediaflow/settings redirects to Opti ID sign-in even though you are signed in to the CMS |
Custom policy that specifies only one authentication scheme | Specify all schemes to be accepted in the policy, or use the default policies |
| TinyMCE image displays old alt text/photographer on the published page |
AddMediaflowViews() is missing, or there is a custom XhtmlString template |
Add AddMediaflowViews() or use WithMediaflowMetadata() (see Upgrade) |