Sanjay Kumar
Apr 1, 2026
  294
(3 votes)

Fixing “Published By” After OKTA SSO in Optimizely CMS

Enabling Okta SSO significantly strengthened our authentication and security model in Optimizely CMS. However, like many real-world implementations, it also introduced an unexpected challenge in the CMS authoring experience, as the system now stores the Okta ID as the username.

Problem:

After switching to SSO, we observed a change in how user identities were stored:

  • The “Published By” field displayed Okta ID
  • Instead of names or emails, editors saw technical IDs



Impact on Editorial Exp.

This created friction for content teams:

  • Author attribution became unclear
  • Content review was harder to follow
  • Auditing lost its human-readable context

For non-technical users, this was a noticeable drop in usability.

Solution:

To address this, we introduced a custom transformation:

👉 PublishedByTransform

This transform maps the stored Okta identifier to a friendly display value, such as:

  • Author name
  • Email address
  • Or any readable identity field

Instead of exposing raw Okta IDs, the CMS now resolves and displays meaningful user information.

 [ServiceConfiguration(typeof(IModelTransform), Lifecycle = ServiceInstanceScope.Singleton)]
 public class PublishedByTransform : ContentDataStoreModelTransform
 {
     private readonly EPiServer.Logging.ILogger _logger;
     private readonly IUserService _userService;
     private readonly IPrincipalAccessor _principalAccessor;
     private readonly IContentVersionRepository _contentVersionRepository;

     public PublishedByTransform(
         IUserService userService,
         IPrincipalAccessor principalAccessor,
         IContentVersionRepository contentVersionRepository)
     {
         _userService = userService ?? throw new ArgumentNullException(nameof(userService));
         _principalAccessor = principalAccessor ?? throw new ArgumentNullException(nameof(principalAccessor));
         _contentVersionRepository = contentVersionRepository ?? throw new ArgumentNullException(nameof(contentVersionRepository));
         _logger = LogManager.GetLogger(typeof(PublishedByTransform));
     }

     public override void TransformInstance(IContent source, ContentDataStoreModel target, IModelTransformContext context)
     {
         base.TransformInstance(source, target, context);
         ContentVersion contentVersion1 = this._contentVersionRepository.Load(source.ContentLink);
         ContentVersion contentVersion2 = _contentVersionRepository.LoadPublished(source.ContentLink, source.LanguageBranch());

         if (contentVersion1 != null)
         {
             var versionCreatedBy = string.IsNullOrEmpty(contentVersion1.StatusChangedBy) ? contentVersion1.SavedBy : contentVersion1.StatusChangedBy;
             target.VersionCreatedBy = ResolvePublishedBy(versionCreatedBy);
         }

         if (contentVersion2 != null)
         {
             var publishedBy = !string.IsNullOrWhiteSpace(contentVersion2.StatusChangedBy)
                 ? contentVersion2.StatusChangedBy
                 : contentVersion2.SavedBy;

             target.PublishedBy = ResolvePublishedBy(publishedBy);
         }
     }

     private string? ResolvePublishedBy(string? publishedBy)
     {
         if (string.IsNullOrWhiteSpace(publishedBy))
             return publishedBy;

         try
         {
             var user = _userService.GetUserByName(publishedBy);
             if (user == null)
                 return publishedBy;

             var claimsPrincipal = _principalAccessor.Principal as ClaimsPrincipal;
             var currentUserEmail = claimsPrincipal?.FindFirst("email")?.Value ?? claimsPrincipal?.FindFirst(ClaimTypes.Email)?.Value;
             if (!string.IsNullOrWhiteSpace(currentUserEmail)
                 && !string.IsNullOrWhiteSpace(user.Email)
                 && string.Equals(currentUserEmail, user.Email, StringComparison.OrdinalIgnoreCase))
             {
                 return "you";
             }

             return !string.IsNullOrWhiteSpace(user.Email) ? user.Email : user.UserName;
         }
         catch (Exception ex)
         {
             _logger.Error($"PublishedByTransform failed to resolve PublishedBy for value '{publishedBy}'. Error: {ex}");
             return publishedBy;
         }
     }
 }

Result:

With this small but impactful enhancement, we achieved the best of both worlds:

  • Secure SSO via Okta remains fully in place
  • “Published By” now shows clear, readable author names


Thank you for reading the blog. Cheers!

Apr 01, 2026

Comments

Praful Jangid
Praful Jangid Apr 9, 2026 07:05 AM

Thanks for sharing.

Please login to comment.
Latest blogs
Optimizely migration from CMS 12 to CMS 13

Upgrading from Optimizely CMS 12 to CMS 13 alongside moving the runtime from .NET 8.0 to .NET 10.0 is far more than a routine version upgrade. It i...

Sanjay Kumar | Apr 24, 2026

Optimizely CMS 13 host(s) management

One of the features I found most impressive is the way Optimizely CMS 13 handles hosts with clear definition and intent. It may sound low level...

Harish Yadav | Apr 23, 2026

Building Opal tools on Optimizely Connect Platform: a Mailchimp walkthrough

About the Mailchimp Opal Tool The  Mailchimp Opal Tool  is an Optimizely Connect Platform app that brings Mailchimp audience operations directly in...

Sanjay Kumar | Apr 22, 2026

📣 Build, Automate, and Scale Content Operations with CMS REST API v1 - now live!

Now available for both CMS13 and CMS SaaS We are excited to announce the  v1 release of our CMS REST API —a major milestone in delivering a stable,...

Kathy Copeland | Apr 21, 2026

Introducing AI Assistant v4 for Optimizely CMS 12 and 13

Epicweb AI Assistant v4.0 adds full support for Optimizely CMS 13 on .NET 10 while staying compatible with CMS 12 on .NET 8, plus new AI Chat tools...

Luc Gosso (MVP) | Apr 20, 2026 |