Sanjay Kumar
Apr 1, 2026
  407
(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 Opal: How to Build Effective Workflow Agents

If you're building workflow agents in Optimizely Opal, this post covers how specialized agents pass context to each other, why keeping agents small...

Andre | May 20, 2026

ReviewPR: An Azure Function That Reviews Your Azure DevOps Pull Requests With Claude

A while back I wrote about an  Azure Function App for PDF creation that we use to offload PDF rendering from our Optimizely DXP site. That same...

KennyG | May 19, 2026

Accelerating Optimizely CMS and Commerce upgrades with agentic AI (Part 2 of 2)

The Real Transformation in Optimizely CMS 13: Why the Upgrade Itself Is the Easy Part. A field-tested playbook for enterprise teams moving from...

Hung Le Hoang | May 18, 2026

Is the most powerful AI model really the best value?

Artificial Intelligence is already becoming part of everyday software development. Developers now use AI tools to generate code, write documentatio...

K Khan | May 16, 2026