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!
Comments