umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
Other
4.49k stars 2.69k forks source link

umbraco/backoffice/umbracoapi/authentication/PostUnLinkLogin Erroring when trying to manually unlink an external account #11890

Closed ben-powley closed 1 year ago

ben-powley commented 2 years ago

Which exact Umbraco version are you using? For example: 9.0.1 - don't just write v9

9.2.0

Bug summary

When trying to unlink an external account via the CMS, I am getting the following error -

The DELETE statement conflicted with the REFERENCE constraint "FK_umbracoExternalLoginToken_umbracoExternalLogin_id". The conflict occurred in database "XX", table "dbo.umbracoExternalLoginToken", column 'externalLoginId'. The statement has been terminated.

It looks like the BackOfficeUserStore RemoveLoginAsync is trying to remove the external login, but is not first removing the external login tokens that exist for that login. The user is supposed to be removed from the umbracoExternalLogin table, but the umbracoExternalLoginToken table contains a reference to the login ID and so is preventing the default and causing an error. The error is only visible in the console too as the front-end breaks when this occurs.

image

Specifics

No response

Steps to reproduce

Follow the Umbraco autolinking of external accounts documentation, whilst keeping manual linking enabled.

Login to the CMS and go to the view profile section and press the "Un-link your XX account" button.

Check the console for the failed PostUnLinkLogin API call.

Expected result / actual result

The local umbraco account should be unlinked from the external account and the external account login and user tokens should be deleted from their corresponding database tables.

RyuLindow commented 2 years ago

I've also encountered it twice on Umbraco Cloud projects for around 4 different users. I can't reproduce it, though :(

Same exact error and version of the CMS as you.

The weird thing about this is that it only happened for users that were invited via the backoffice and never actually accepted the invite.

That results in an entry in the UmbracoUser table but nothing in the umbracoExternalLoginToken and umbracoExternalLogin ones, so it's looking for data that doesn't even exist in the DB.

Have you tried upgrading to 9.3.x to see if the issue persists?

ben-powley commented 2 years ago

@RyuLindow Thanks for the suggestion. I upgrade the project to 9.3.1 and tried unlinking my account via the CMS, but got the same error unfortunately.

System.Data.SqlClient.SqlException (0x80131904): The DELETE statement conflicted with the REFERENCE constraint "FK_umbracoExternalLoginToken_umbracoExternalLogin_id". The conflict occurred in database "zz-Crafted-Umbraco-Demo", table "dbo.umbracoExternalLoginToken", column 'externalLoginId'. The statement has been terminated.

RyuLindow commented 2 years ago

This is what I did to get rid of these users since I could delete them via the backoffice:

  1. Go to the [umbracoUser] and find the id​ of the user you want to get rid of.
  2. Go to [umbracoUser2UserGroup] and delete the row where the userId has the same value as the id​ value you've just looked up in the previous step
  3. Go back to [umbracoUser] ​and delete the user.

    Do note that you should only use this approach for users which never interacted with the backoffice.

mayhammf commented 2 years ago

I think the bug is caused by an incorrect implementation here: https://github.com/umbraco/Umbraco-CMS/blob/release-9.3.1/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ExternalLoginRepository.cs

See line 245 then 254, in case the tokens to be deleted are not present in the existingTokens because they have been filtered out by line 245, they will never be added the toDelete collection, thus will not be deleted.

mayhammf commented 2 years ago

not 100% sure, but i think line 245 doesn't actually make sense in this context.. We want the list of stored tokens for that user to match the state provided by the token parameter.. So the need for filtering the tokens based on the LoginProvider is not so clear here.

Domitnator commented 2 years ago

Hi Guys,

I just connected umbraco 10.0.1 with identity provider 4 (based on this page: https://our.umbraco.com/documentation/Reference/Security/auto-linking/)

But unlinking a user is giving me an error as well:

Possibly unhandled rejection: {"errorMsg":"Unlinking login provider failed","data":"Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 19: 'FOREIGN KEY constraint failed'.\r\n   at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)\r\n   at Microsoft.Data.Sqlite.SqliteDataReader.NextResult()\r\n   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior)\r\n   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteNonQuery()\r\n   at Umbraco.Cms.Infrastructure.Persistence.FaultHandling.RetryPolicy.ExecuteAction[TResult](Func`1 func)\r\n   at NPoco.Database.ExecuteNonQueryHelper(DbCommand cmd)\r\n   at NPoco.Database.Execute(String sql, CommandType commandType, Object[] args)\r\n   at Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement.ExternalLoginRepository.Save(Guid userOrMemberKey, IEnumerable`1 logins)\r\n   at Umbraco.Cms.Core.Services.ExternalLoginService.Save(Guid userOrMemberKey, IEnumerable`1 logins)\r\n   at Umbraco.Cms.Core.Security.BackOfficeUserStore.UpdateAsync(BackOfficeIdentityUser user, CancellationToken cancellationToken)\r\n   at Microsoft.AspNetCore.Identity.UserManager`1.UpdateUserAsync(TUser user)\r\n   at Microsoft.AspNetCore.Identity.UserManager`1.RemoveLoginAsync(TUser user, String loginProvider, String providerKey)\r\n   at Umbraco.Cms.Web.BackOffice.Controllers.AuthenticationController.PostUnLinkLogin(UnLinkLoginModel unlinkLoginModel)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()\r\n--- End of stack trace from previous location ---\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()\r\n--- End of stack trace from previous location ---\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\r\n   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)\r\n   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)\r\n   at Umbraco.Cms.Web.Common.Middleware.BasicAuthenticationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Umbraco.Cms.Web.BackOffice.Middleware.BackOfficeExternalLoginProviderErrorMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)\r\n   at Microsoft.AspNetCore.Session.SessionMiddleware.Invoke(HttpContext context)\r\n   at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)\r\n   at Microsoft.AspNetCore.Authorization.Policy.AuthorizationMiddlewareResultHandler.HandleAsync(RequestDelegate next, HttpContext context, AuthorizationPolicy policy, PolicyAuthorizationResult authorizeResult)\r\n   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\r\n   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\r\n   at SixLabors.ImageSharp.Web.Middleware.ImageSharpMiddleware.Invoke(HttpContext httpContext, Boolean retry)\r\n   at StackExchange.Profiling.MiniProfilerMiddleware.Invoke(HttpContext context) in C:\\projects\\dotnet\\src\\MiniProfiler.AspNetCore\\MiniProfilerMiddleware.cs:line 119\r\n   at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Umbraco.Cms.Web.Common.Middleware.PreviewAuthenticationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Umbraco.Cms.Web.Common.Middleware.UmbracoRequestLoggingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next)\r\n   at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.<>c__DisplayClass6_1.<<UseMiddlewareInterface>b__1>d.MoveNext()\r\n--- End of stack trace from previous location ---\r\n   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)\r\n\r\nHEADERS\r\n=======\r\nAccept: application/json, text/plain, */*\r\nAccept-Encoding: br, gzip, deflate\r\nAccept-Language: nl,en-US;q=0.9,en;q=0.8,nl-NL;q=0.7\r\nCache-Control: no-cache\r\nConnection: close\r\nContent-Length: 94\r\nContent-Type: application/json;charset=UTF-8\r\nCookie: ai_user=wDIZ7|2021-12-07T10:05:30.463Z; _ga=GA1.1.405895752.1647862884; CookieConsent={stamp:%27iHluENcEmMd0K5ZFYpitGqmZDjlxUqjsbOFZFu8V/RmoY/uuKPHmqA==%27%2Cnecessary:true%2Cpreferences:true%2Cstatistics:true%2Cmarketing:true%2Cver:1%2Cutc:1647862953213%2Cregion:%27nl%27}; _ga_KPJFDX8S7M=GS1.1.1648323143.5.0.1648323143.0; UMB_UPDCHK=1; ActiveSolution=BeukenhorstAPI%24aae7ab3f-7ca4-4f34-b4fd-07370e6c7bfc; idsrv.session=D1D58BE1BF28EC49677D5A8B6C061B3F; .AspNetCore.Antiforgery.wV2LY6geXm4=CfDJ8JW1a4QJpJpDon2m1AlZPfKwCn8_8M2Z3c6UUPG8ckZR15WTE7M2PYlKgoPF65jukWCmuUhzKpLwLYEWvyCqbKTLPdqeIb-GB99On4MYwqphMY9ISYa-e0B5wz0Al3-GMqaECM8Gbd5NmkOTJpVOabQ; .AspNetCore.Identity.Application=CfDJ8JW1a4QJpJpDon2m1AlZPfJ7VUpsRhmg4VBXxdWXFjzhUEKykQjuC8fKrBCYfXl_9MC2dgg465n4xrVtQE1vDHLr1-9Jxs8AGCslHbXDBmOxWbQaqeIEnpXB4Qu5eMEtTMjua8u84WdVs3pyGGk2hlrK6-noMlne1WpuE_XqNg2TmXIrBQnA2D-C4iZ7WYe3uZCrC2rCDN6LXoyqNnABC5LNQiaYrEsURagyormiNYNF1jFx3czj2FM3LEIZTvU7waU1DXSAtRA-s5h1VWNC62l5AGy_SNLDKxUX29tbP8I5P6stRIEKx5YBDPaWLoJfayK0C8u1FY79USFcLWo9BU38j7dx341O7yJXuSbZwRDwQ0RifnsOynUZ83BVkM7RkJdidsIkK6Pz8B2_6w4xSaWJf9mZ0bCTcQ5Y3wOg70YH2ssryt6CO3XSwO4_Je-UuLeY2Tn1YOR9jNAVWoGqLPTlBQ7-CdRMpDGTGWeDvbBnWQzEnDEbggUIb1ntG0LWsf7onzcY2PJuwEBh33zpsjov0T_o8K8zTe7LRPLWSKf2VMcLf6LAnw8mIKFVugAHjJag-swjTjXORZ-lgm9eRSlc88DZQyhsat5lcpvFVhJ2dGSbtL5a94_m4qii2TEr6ejDo_ktE9EDXPczfIelHXOmoAhlAOnUK5Nb28w3YKisN6qpkIE6T7cnputzPFhOIs0g1BK76T0QOddG8NRCxSfHtU598YhrMS-blU2F96ociaSzzKbPEXRExGhPv4fkyHpvWKFPcbbeXkUTJDTGmoMUmHan0xmUASMExk3PXMAi8_9c7luf_MJf2OrTSxKCprRUVDg54mLgNzjnwmmoNSvo6kNKO09poG-9yLRrmfyThFY5--GKrGZEDZ8fstTL23D66I1P2nuL5-6Gu4DtLQfuysfY7TQuaaLOzj8uW2tvD5VDZ1RZH-8olSXkmWtM5BLUU6Rowdz7lz5j7V55jxotdUlXxzefutW0DW9KCquTaW2h4P5jP-W4Eb8hHPkbk2EjqpwKYDtGN1KE7u7mg35Rg_sl3Pq2i5qumJLG0t-Z8LPjBiij1xpaFYES2l2Zo2ic_aW9t5izSfHXuM8zdJmFGfTU2qFAT_ujvNl0mU8Ua9JQzFUIjQ94r3WndX-FOPERcqj_0ZAcgkowMi7483dzTGLXaUCRRSRaUfAqKxpY0VU-inFHcN07ZyHQC8zuove1ME3fA114i8KC3c_2lVwdHDXh_90HDu-ypLu6ctz8gzf3rTFGL5kzmIDMLkHdQw; XSRF-TOKEN=CfDJ8JW1a4QJpJpDon2m1AlZPfKnqROJrWUaMcJr7hUi0kMYXCzWcZ7oNGA14wJijgObqxEllw4gCjyZWXYjvgyktrUDsde0ONDf_UXFfenJwYB_pN19If_jZEiZ5ZdRq39zpUXofVRoIj-Sv2nueMEUtCtZZNI_56-vq1g8rFJ38MjaYRNoZKtHcmZI4VcsQjm4IQ; UMB-XSRF-V=CfDJ8JW1a4QJpJpDon2m1AlZPfLbrEUJ66gKq6Hb0U1XZHgkd1twc-2xOeHJ2ZG8Kn0747L8Z37sO6pzXqakjqKlAWj8ukVnfZB1jK6tVtJYNosinPD4rIh9mvZ7Hzal6ZvHQq-OGS-TS5tkqHBiViXN9vY; UMB-XSRF-TOKEN=CfDJ8JW1a4QJpJpDon2m1AlZPfL1ZL_QvxGRUwYOEbJIkCFOdt32E7_qYJ-f8FG6iPFZyL7KxYVwDvGTgCmiyuW1zivcPn05rVGF6vuCPqlV9naWXd4FTD8wMWJNjpf_E5roXjqvE6IT8F9CKeVEnnDw-MZwnf-xRArKyuWDZ9cZDlltSrek5Qxd4odxzsrEnTX6_Q; UMB_UCONTEXT=CfDJ8JW1a4QJpJpDon2m1AlZPfIY3RZxe_S6KDLMU-ytWy3AEjG_IHDru_JMy7PQoU757a_qKUbLn2fe8UZ98_QBIG1Y-594IIhFbsevrz5PT8Vf7ZIxKENHXte9MySt0iXgkPu-QNtZiuRMJbUTL-s-bpKzEl5D4XDWe44D9ozYbrRLtif9T2mHOgVVB1B5a5cwd4etG-VY-6jCK9J_-m_OTJkUpYMfPJzeS2icOz9yq9ZD47QvB_7ZYnvClw8QIPDIlpRi8hw4UWn6nBDYcG6geEC49Pzeh_EhxhxTC8i1Qd_zYNpdMxIn2GNLVsf6ZFP_1-us7GofquwXwnI06aRU29Ex7iBukj8oVmzvtC0IhdYZ5GrW6JFcRGn20sJChAowX6ZogACxgINJurR0b4oAVw_awc5Sb7fc1DMGfMksVrOJPXmxkT5k4m6f2fGvttTS6rvcaYS9KTzpp0qkqhRHuv8XjI9HumxETaCJ1AvItN9L2kBWn4JooKnR14HIDfKBG9-GKoZw-dwt2PBfCM5vfd2K9TDSMs2Hon5KbEGC_AfTpqa-2gxACUkujc4P6us1x7mURf7S7786L2kz6If1DKXIYjdhfIhMHGwp8nqDrNWRGorqinKKp38A27_exWhuNEjJeF0b5C1u2G155wxGpYHUeLkdazhMuI5d7qehBuwosruOriKsiIHWP1fO-prhxRCDawowKr7OZEaJk_qa0E9Om-7aBYJIYXt35A7apEdmY7qFoJANC2Xtvh9HITKNuxP5qmooZgsYCqHivBU6B8EVKNmghdlkigS3brXdbs-PQ-5Xq0KV4VOGyW9kegVOhZ42c_dThAE6OZBZtpV_7FSadHf4f8_vKEA90ngdDBy9HvY2pJ4gMFWWyBS3qy60c8ornQr7ZvWlBbLEg2VzdAAidLB6INAdJI7KkYcmfSYF9aefhqxYDTo8pV8giSJC8Sv80Pq84_vhrl6xyE36fDSZMeSdxGaD171Veb0OSzGdqg62drhNZK2stKAVcKpUQLwQcDXyA8Icbfh64L9nhwlNu66qK3h90F4lp5EtsNVwkvgH8ICoxWaTQQxP4sI6iSroC5alYt0X4EUbeTwFiWpe6BLTFc1ziUo27bAH4YlCTsbqGNLMuNALEmwLHdHoTsJ8xpmuMGH8lfelGCiw8hB0LHpiHgXJWDHcDXSyQVRXb2WCYv5ENX7Hmz1XY8P0ClvPnimwtP6-wDjj0JtHThhtGF2y-RixSddAAfk_WDO0P3Tz0wK3oX3alA9zCayMk4SV5NJBY_GUbg5gQjdW-XouKpd5QiTddRkvwFPg8VvxhKS8EcGX0nWezHY4WuRzmhM0-FcewRcfG5_-B-wZKG6IMV7hIAe_KEgmTr6yfq3b1SYBf4TRg41E0skAj1IfAReagqlx2Kve3R4w_yP6JherSTWW_wVjafloqzOa1tS7yYNZReXyR3S1Q8Y_HnXpxWgva4VViI0qatqxW6Q7DBZEUiDjBfVD5ChkxH6TcBFANFWddOqS9rm__5LzeNoQE9ff5cTvi5km-D_Is2Af0KF7xagenri2DC_4Ap4z2-vzwzhDrAR-bLizugNWXyPf7vT7PvC6wcvkZ0P8EEtZJW-4YCMnEugJKmjzkLJl3xhIM1OcuJBkrNfO_uug4BOuwNcVS1Q6M6A6jvtazxHQCWvZRKncoHzTk_UNENGQY3MFKqFK-wRG-ksI77aQffn3vQiuX_6lykzcsIqIf--UMFJPVyVsnYDm9p0JoD2_WkQ_Ky_QvA92q_nf9e1p6jArcEk963TQSS_xwskHLvMjeYHofDmS3iHw6rgiH_L_lTHDIZkZgAgZgCNfzIlobp0d68-tMtc_eRi2AMlebZJBo-Tdt_OMJzgV1nj0SB-WQadbREPgK9bcqleeYKICfgHtL1OpQLuKrmY25ib3QU5YH5jaWHG1l-3ewHXFJajMuQG_whm-oyw3Ajs8Tv1No0sPy70GlNt2x5Qyz_4sdmBpxZJ_ts_TDTahmQS4N7PSvwoc5xIH73_nw82AsZvOC6R5OsUZ7Hcxg3hYWH72J1-PKsqaOzkyhQr4BVCZ5EWJdierm07GelG-yW7q4CYe277wlvqG7hwfOrR-VOwoH1lFP4Cd2PWu__lwIZhW6azxCtMiR166te2-TkHX0FMTTSJ542Agg1yz0ztsslCAYEQhG_u4DAine-4OyWv5oN7L0-smmqLOibiB-1rC-NUlIrVc-tf3Cw9fAvF8Gp_zEF4UfX9oxWKeLIGG2NcOY7UHiFjeZO1eTTowXGFMO60TRfsIFobvFakP_To5IJfMRKcWnCHsv-IHSX1ldcZFuzwtVJNgI3ZzMZCj8QFLi2JCCkDT13t8NjcSet7WUBoSRRu4SxhV-PNxzVbCYzFamAJpwutX\r\nHost: localhost:44396\r\nPragma: no-cache\r\nReferer: https://localhost:44396/umbraco\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\r\nsec-ch-ua: \".Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"103\", \"Chromium\";v=\"103\"\r\nx-umb-segment: null\r\nsec-ch-ua-mobile: ?0\r\nx-requested-with: XMLHttpRequest\r\nx-umb-xsrf-token: CfDJ8JW1a4QJpJpDon2m1AlZPfL1ZL_QvxGRUwYOEbJIkCFOdt32E7_qYJ-f8FG6iPFZyL7KxYVwDvGTgCmiyuW1zivcPn05rVGF6vuCPqlV9naWXd4FTD8wMWJNjpf_E5roXjqvE6IT8F9CKeVEnnDw-MZwnf-xRArKyuWDZ9cZDlltSrek5Qxd4odxzsrEnTX6_Q\r\nsec-ch-ua-platform: \"Windows\"\r\norigin: https://localhost:44396\r\nsec-fetch-site: same-origin\r\nsec-fetch-mode: cors\r\nsec-fetch-dest: empty\r\n","status":500}

I also noticed that the admin user is unable to delete a user which is linked to an external provider. The "ContentEditor" user is the user which is linked:

image

There is no "delete" button:

image

Also, it's not possible to see that the user is linked to an external account.

kjac commented 1 year ago

Hi @ben-powley,

Sorry for the late response 😞

I can't reproduce this on V10.3 ... I'm using GitHub as external authentication provider, but the provider doesn't make any difference in terms of unlinking. Here's a screencast of what I'm seeing:

11890

The process can be repeated over and over without errors; each time I login my account is auto-linked back up to my GitHub account.

My login provider options are entirely standard as per the docs, with the exception of explicitly approving the user when auto-linking is performed (see also https://github.com/umbraco/Umbraco-CMS/issues/12487):

public class GitHubBackOfficeExternalLoginProviderOptions : IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
    public const string SchemeName = "GitHub";

    public void Configure(string? name, BackOfficeExternalLoginProviderOptions options)
    {
        if (name != "Umbraco." + SchemeName)
        {
            return;
        }

        Configure(options);
    }

    public void Configure(BackOfficeExternalLoginProviderOptions options)
    {
        options.ButtonStyle = "btn-danger";
        options.Icon = "fa fa-cloud";
        options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
            // must be true for auto-linking to be enabled
            autoLinkExternalAccount: true,

            // Optionally specify default user group, else
            // assign in the OnAutoLinking callback
            // (default is editor)
            defaultUserGroups: new[] { Constants.Security.EditorGroupAlias },

            // Optionally specify the default culture to create
            // the user as. If null it will use the default
            // culture defined in the web.config, or it can
            // be dynamically assigned in the OnAutoLinking
            // callback.
            defaultCulture: null,
            // Optionally you can disable the ability to link/unlink
            // manually from within the back office. Set this to false
            // if you don't want the user to unlink from this external
            // provider.
            allowManualLinking: true
        )
        {
            // Optional callback
            OnAutoLinking = (autoLinkUser, loginInfo) =>
            {
                // You can customize the user before it's linked.
                // i.e. Modify the user's groups based on the Claims returned
                // in the externalLogin info

                // see https://github.com/umbraco/Umbraco-CMS/issues/12487
                autoLinkUser.IsApproved = true;
            },
            OnExternalLogin = (user, loginInfo) =>
            {
                // You can customize the user before it's saved whenever they have
                // logged in with the external provider.
                // i.e. Sync the user's name based on the Claims returned
                // in the externalLogin info

                return true; //returns a boolean indicating if sign-in should continue or not.
            }
        };

        // Optionally you can disable the ability for users
        // to login with a username/password. If this is set
        // to true, it will disable username/password login
        // even if there are other external login providers installed.
        options.DenyLocalLogin = false;

        // Optionally choose to automatically redirect to the
        // external login provider so the user doesn't have
        // to click the login button. This is
        options.AutoRedirectLoginToExternalProvider = false;
    }
}

Did you try upgrading to the latest V10?

Zeegaan commented 1 year ago

Hi @ben-powley Since we cannot reproduce this on our end, and we haven't heard back in a while, I am going to go ahead and close this for now.

Feel free to re-open if this is still an issue 😊