diff --git a/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs b/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs
new file mode 100644
index 0000000000..65e12895cc
--- /dev/null
+++ b/src/Umbraco.Core/Security/ClaimsIdentityExtensions.cs
@@ -0,0 +1,152 @@
+// Copyright (c) Umbraco.
+// See LICENSE for more details.
+
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Claims;
+using Umbraco.Core;
+
+namespace Umbraco.Extensions
+{
+ public static class ClaimsIdentityExtensions
+ {
+ ///
+ /// Returns the required claim types for a back office identity
+ ///
+ ///
+ /// This does not include the role claim type or allowed apps type since that is a collection and in theory could be empty
+ ///
+ public static IEnumerable RequiredBackOfficeClaimTypes => new[]
+ {
+ ClaimTypes.NameIdentifier, //id
+ ClaimTypes.Name, //username
+ ClaimTypes.GivenName,
+ Constants.Security.StartContentNodeIdClaimType,
+ Constants.Security.StartMediaNodeIdClaimType,
+ ClaimTypes.Locality,
+ Constants.Security.SecurityStampClaimType
+ };
+
+ public const string Issuer = Constants.Security.BackOfficeAuthenticationType;
+
+ ///
+ /// Verify that a ClaimsIdentity has all the required claim types
+ ///
+ ///
+ /// Verified identity wrapped in a ClaimsIdentity with BackOfficeAuthentication type
+ /// True if ClaimsIdentity
+ public static bool VerifyBackOfficeIdentity(this ClaimsIdentity identity, out ClaimsIdentity verifiedIdentity)
+ {
+ // Validate that all required claims exist
+ foreach (var claimType in RequiredBackOfficeClaimTypes)
+ {
+ if (identity.HasClaim(x => x.Type == claimType) == false ||
+ identity.HasClaim(x => x.Type == claimType && x.Value.IsNullOrWhiteSpace()))
+ {
+ verifiedIdentity = null;
+ return false;
+ }
+ }
+
+ verifiedIdentity = new ClaimsIdentity(identity.Claims, Constants.Security.BackOfficeAuthenticationType);
+ return true;
+ }
+
+ public static void AddRequiredClaims(this ClaimsIdentity identity, string userId, string username,
+ string realName, IEnumerable startContentNodes, IEnumerable startMediaNodes, string culture,
+ string securityStamp, IEnumerable allowedApps, IEnumerable roles)
+ {
+ //This is the id that 'identity' uses to check for the user id
+ if (identity.HasClaim(x => x.Type == ClaimTypes.NameIdentifier) == false)
+ {
+ identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId, ClaimValueTypes.String,
+ Issuer, Issuer, identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.Name) == false)
+ {
+ identity.AddClaim(new Claim(ClaimTypes.Name, username, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.GivenName) == false)
+ {
+ identity.AddClaim(new Claim(ClaimTypes.GivenName, realName, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+
+ if (identity.HasClaim(x => x.Type == Constants.Security.StartContentNodeIdClaimType) == false &&
+ startContentNodes != null)
+ {
+ foreach (var startContentNode in startContentNodes)
+ {
+ identity.AddClaim(new Claim(Constants.Security.StartContentNodeIdClaimType, startContentNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, identity));
+ }
+ }
+
+ if (identity.HasClaim(x => x.Type == Constants.Security.StartMediaNodeIdClaimType) == false)
+ {
+ foreach (var startMediaNode in startMediaNodes)
+ {
+ identity.AddClaim(new Claim(Constants.Security.StartMediaNodeIdClaimType, startMediaNode.ToInvariantString(), ClaimValueTypes.Integer32, Issuer, Issuer, identity));
+ }
+ }
+
+ if (identity.HasClaim(x => x.Type == ClaimTypes.Locality) == false)
+ {
+ identity.AddClaim(new Claim(ClaimTypes.Locality, culture, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+
+ // The security stamp claim is also required
+ if (identity.HasClaim(x => x.Type == Constants.Security.SecurityStampClaimType) == false)
+ {
+ identity.AddClaim(new Claim(Constants.Security.SecurityStampClaimType, securityStamp, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+
+ // Add each app as a separate claim
+ if (identity.HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false &&
+ allowedApps != null)
+ {
+ foreach (var application in allowedApps)
+ {
+ identity.AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+ }
+
+ // Claims are added by the ClaimsIdentityFactory because our UserStore supports roles, however this identity might
+ // not be made with that factory if it was created with a different ticket so perform the check
+ if (identity.HasClaim(x => x.Type == ClaimsIdentity.DefaultRoleClaimType) == false && roles != null)
+ {
+ // Manually add them
+ foreach (var roleName in roles)
+ {
+ identity.AddClaim(new Claim(identity.RoleClaimType, roleName, ClaimValueTypes.String, Issuer, Issuer, identity));
+ }
+ }
+ }
+
+ public static int[] GetStartContentNodes(this ClaimsIdentity identity) =>
+ identity.FindAll(x => x.Type == Constants.Security.StartContentNodeIdClaimType)
+ .Select(node => int.TryParse(node.Value, out var i) ? i : default)
+ .Where(x => x != default).ToArray();
+
+ public static int[] GetStartMediaNodes(this ClaimsIdentity identity) =>
+ identity.FindAll(x => x.Type == Constants.Security.StartMediaNodeIdClaimType)
+ .Select(node => int.TryParse(node.Value, out var i) ? i : default)
+ .Where(x => x != default).ToArray();
+
+ public static string[] GetAllowedApplications(this ClaimsIdentity identity) => identity
+ .FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToArray();
+
+ public static int GetId(this ClaimsIdentity identity) => int.Parse(identity.FindFirstValue(ClaimTypes.NameIdentifier));
+
+ public static string GetRealName(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.GivenName);
+
+ public static string GetUsername(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Name);
+
+ public static string GetCulture(this ClaimsIdentity identity) => identity.FindFirstValue(ClaimTypes.Locality);
+
+ public static string GetSecurityStamp(this ClaimsIdentity identity) => identity.FindFirstValue(Constants.Security.SecurityStampClaimType);
+
+ public static string[] GetRoles(this ClaimsIdentity identity) => identity
+ .FindAll(x => x.Type == ClaimsIdentity.DefaultRoleClaimType).Select(role => role.Value).ToArray();
+ }
+}