diff --git a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
index 420d66b0b4..345a404fcf 100644
--- a/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
+++ b/src/Umbraco.Infrastructure/Security/MemberUserStore.cs
@@ -181,6 +181,7 @@ namespace Umbraco.Cms.Core.Security
{
// we have to remember whether Logins property is dirty, since the UpdateMemberProperties will reset it.
var isLoginsPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.Logins));
+ var isTokensPropertyDirty = user.IsPropertyDirty(nameof(MemberIdentityUser.LoginTokens));
MemberDataChangeType memberChangeType = UpdateMemberProperties(found, user);
if (memberChangeType == MemberDataChangeType.FullSave)
@@ -203,6 +204,16 @@ namespace Umbraco.Cms.Core.Security
x.ProviderKey,
x.UserData)));
}
+
+ if (isTokensPropertyDirty)
+ {
+ _externalLoginService.Save(
+ found.Key,
+ user.LoginTokens.Select(x => new ExternalLoginToken(
+ x.LoginProvider,
+ x.Name,
+ x.Value)));
+ }
}
return Task.FromResult(IdentityResult.Success);
@@ -535,6 +546,37 @@ namespace Umbraco.Cms.Core.Security
return found;
}
+ ///
+ /// Overridden to support Umbraco's own data storage requirements
+ ///
+ ///
+ /// The base class's implementation of this calls into FindTokenAsync and AddUserTokenAsync, both methods will only work with ORMs that are change
+ /// tracking ORMs like EFCore.
+ ///
+ ///
+ public override Task SetTokenAsync(MemberIdentityUser user, string loginProvider, string name, string value, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ ThrowIfDisposed();
+
+ if (user == null)
+ {
+ throw new ArgumentNullException(nameof(user));
+ }
+
+ IIdentityUserToken token = user.LoginTokens.FirstOrDefault(x => x.LoginProvider.InvariantEquals(loginProvider) && x.Name.InvariantEquals(name));
+ if (token == null)
+ {
+ user.LoginTokens.Add(new IdentityUserToken(loginProvider, name, value, user.Id));
+ }
+ else
+ {
+ token.Value = value;
+ }
+
+ return Task.CompletedTask;
+ }
+
private MemberIdentityUser AssignLoginsCallback(MemberIdentityUser user)
{
if (user != null)
diff --git a/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs b/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs
index d58abfc871..8308abafff 100644
--- a/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs
+++ b/src/Umbraco.Web.Website/Security/MemberAuthenticationBuilder.cs
@@ -41,7 +41,7 @@ namespace Umbraco.Cms.Web.Website.Security
// Validate that the prefix is set
if (!authenticationScheme.StartsWith(Constants.Security.MemberExternalAuthenticationTypePrefix))
{
- throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.BackOfficeExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForMembers)}");
+ throw new InvalidOperationException($"The {nameof(authenticationScheme)} is not prefixed with {Constants.Security.MemberExternalAuthenticationTypePrefix}. The scheme must be created with a call to the method {nameof(SchemeForMembers)}");
}
// add our login provider to the container along with a custom options configuration