umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
MIT License
4.46k stars 2.68k forks source link

Umbraco issue when using Okta #14713

Open TheK272 opened 1 year ago

TheK272 commented 1 year ago

Which Umbraco version are you using? (Please write the exact version, example: 10.1.0)

10.4

Bug summary

We have an Umbraco site that also uses Okta for user management. We have several Umbraco Forms on the site, that work just fine, as long has you haven't logged in. After logging in, pages with Forms on them error out and don't load. Here is the error message: Unable to convert user ID (00u3j5olob8akHODo1d7)to int using InvariantCulture at Umbraco.Cms.Core.Security.UmbracoUserStore2.UserIdToInt(String userId)

I created a support ticket on the Umbraco.Forms repo, was told to make on here. https://github.com/umbraco/Umbraco.Forms.Issues/issues/1070

Specifics

Full call stack is at the bottom. The code that renders the form calls the Umbraco CMS method MemberManager.IsMemberAuthorizedAsync which is what throws the exception. The exception occurs when the code pulls the "UserId" from httpcontext.user.claimsprincipal[http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier]. The value stored there is an Okta userId. So it pulls in the okta id, fails to convert it to a GUID or int, and then throws the error.

ystem.InvalidOperationException: Unable to convert user ID (00u3j5olob8akHODo1d7)to int using InvariantCulture at Umbraco.Cms.Core.Security.UmbracoUserStore2.UserIdToInt(String userId) at Umbraco.Cms.Core.Security.MemberUserStore.FindUserAsync(String userId, CancellationToken cancellationToken) at Umbraco.Cms.Core.Security.UmbracoUserStore2.FindByIdAsync(String userId, CancellationToken cancellationToken) at Microsoft.AspNetCore.Identity.UserManager1.FindByIdAsync(String userId) at Microsoft.AspNetCore.Identity.UserManager1.GetUserAsync(ClaimsPrincipal principal) at Umbraco.Cms.Web.Common.Security.MemberManager.GetCurrentMemberAsync() at Umbraco.Cms.Web.Common.Security.MemberManager.IsMemberAuthorizedAsync(IEnumerable1 allowTypes, IEnumerable1 allowGroups, IEnumerable1 allowMembers) at Umbraco.Forms.Web.Services.FormRenderingService.GetFormModelAsync(HttpContext httpContext, Guid formId, Nullable1 recordId, String theme) at Umbraco.Forms.Web.ViewComponents.RenderFormScriptsViewComponent.InvokeAsync(Guid formId, String theme) at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentInvoker.InvokeAsyncCore(ObjectMethodExecutor executor, Object component, ViewComponentContext context) at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentInvoker.InvokeAsync(ViewComponentContext context) at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentInvoker.InvokeAsync(ViewComponentContext context) at Microsoft.AspNetCore.Mvc.ViewComponents.DefaultViewComponentHelper.InvokeCoreAsync(ViewComponentDescriptor descriptor, Object arguments) at AspNetCore.ViewsLayout.1() in C:\Code\Umbraco\Facs.Web.UI\Views\Layout.cshtml:line 42 at Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync() at AspNetCore.ViewsLayout.ExecuteAsync() in C:\Code\Umbraco\Facs.Web.UI\Views\Layout.cshtml:line 26 at

Steps to reproduce

  1. go to site and sign in with Okta
  2. go to page that has umbraco form on it or
  3. add this code to a view `@using Umbraco.Cms.Core.Security;

@inject IMemberManager _memberManager;

Is authorised: @(await _memberManager.IsMemberAuthorizedAsync())
` 2. Sign in with Okta 3. go to page with above code on it ### Expected result / actual result The page should load without error and any Umbraco forms on the page should load successfully
github-actions[bot] commented 1 year ago

Hi there @TheK272!

Firstly, a big thank you for raising this issue. Every piece of feedback we receive helps us to make Umbraco better.

We really appreciate your patience while we wait for our team to have a look at this but we wanted to let you know that we see this and share with you the plan for what comes next.

We wish we could work with everyone directly and assess your issue immediately but we're in the fortunate position of having lots of contributions to work with and only a few humans who are able to do it. We are making progress though and in the meantime, we will keep you in the loop and let you know when we have any questions.

Thanks, from your friendly Umbraco GitHub bot :robot: :slightly_smiling_face:

Zeegaan commented 1 year ago

Hello there 👋 Thanks for reporting 🐛 In your error message it says something about Unable to convert user ID (00u3j5olob8akHODo1d7), that looks like a malformed GUID to me, and might be something in your Okta setup 🤷, as this should be an integer ID that should be passed, as Andy mentions here https://github.com/umbraco/Umbraco.Forms.Issues/issues/1070#issuecomment-1689519202😊 I would also reccommend isolating Okta from this, so you can figure out if its an issue with your setup, or the CMS itself.

I would love some reproduction steps without Okta.

TheK272 commented 1 year ago

"00u3j5olob8akHODo1d7" is a user id in Okta. It is a string not a GUID. I can't reproduce this without Okta because it works until you sign in with Okta. I found that the root of the issue was Umbraco and Okta both using the same claims principal in httpcontext.user. I wish I could render an Umbraco form and not have it check if the member is authorized to view the form. Anyone on the page is authorized to view the form regardless of who they are or if they are signed in.

geann commented 8 months ago

I am having exactly the same problem with Umbraco 12.3.3 and Auth0 identity provider. Here are the steps to reproduce this error:

  1. Send a POST request to /umbraco/forms/api/v1/entries/{FORM_ID} with Authorization HTTP header that includes a Bearer token with a non-integer "sub" claim value. Example of the decoded token: image

  2. See 500 error response code with the error message "Unable to convert user ID (auth0|65afd768d431fc18d422e2c2)to int using InvariantCulture": image

Having looked into the code, I found that the error is thrown by FormSubmissionHelper.ApplyMemberKey() and UserManager.GetUserAsync(). The latter tries to get a value of the claim ClaimTypes.NameIdentifier and pass it to MemberUserStore.FindUserAsync() where it tries to parse it as a Guid or Int. If the claim is in a different format, the exception is thrown and the form entry is not processed correctly.

I would not say it is a misconfiguration of Auth0/Okta or any other identity provider as there is no standard saying that ClaimTypes.NameIdentifier has to be of Guid or Int type. Please can this scenario be properly handled? If it is not possible to parse this ID, it should be enough to write the raw string value as "Member Key" in the form entry.

Zeegaan commented 8 months ago

@geann I'm not that familiar with using that identity provider, could you provide me some steps on how to set that up and use it in postman? I'd love to reproduce this 😁

SanderLos-Speak commented 8 months ago

Hi,

we are having similar issues with making member requests while using Azure AD (or Entra ID, how it is called nowadays) with an openIdConnect implementation. (render an umbraco from, retreiving a current logged in umbraco member).

Basic setup for the azure part can be found here: https://learn.microsoft.com/en-us/power-pages/security/authentication/openid-settings. We first implemented this with a composer (which does log in users correctly through azure). Then we tried implementing the example that is linked on the umbraco page https://github.com/jbreuer/Umbraco-OpenIdConnect-Example. https://docs.umbraco.com/umbraco-cms/reference/security/external-login-providers

Umbraco: 10.6.1 (not hosted on cloud), umbraco forms 10.4