Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Max 80 posts 437 karma points
    Oct 18, 2019 @ 13:11
    Max
    0

    I'm trying to catch the backend login process with a composer/component...

    I don't want to change the OWIN Default, I want to perform some additional login checks, after successful login.

    Which services/events should I be attaching to?

  • Markus Johansson 1909 posts 5733 karma points MVP c-trib
    Oct 19, 2019 @ 16:28
    Markus Johansson
    0

    Hi!

    I did not know about this for sure but I had a look in the source of Umbraco, when logging in to the backoffice there is a post to the PostLogin-method of the AuthenticationController,

    Looking at like 232 it seems like there is something called UserManager that might fire an event you can subscribe to?

    https://github.com/umbraco/Umbraco-CMS/blob/74be0402982a303d03752f57a5208bc918ff35e5/src/Umbraco.Web/Editors/AuthenticationController.cs#L232

    // m

  • iNETZO 133 posts 496 karma points c-trib
    Oct 20, 2019 @ 17:41
    iNETZO
    0

    Hi Max,

    I think it is possible by creating a class that inherrrits from BackOfficeUserManager (using Umbraco.Web.Security). For example:

    public class CustomBackOfficeUserManager : BackOfficeUserManager
    {
        public CustomBackOfficeUserManager(IUserStore<BackOfficeIdentityUser, int> store, IdentityFactoryOptions<BackOfficeUserManager> options, MembershipProviderBase membershipProvider, IContentSection contentSectionConfig) : base(store, options, membershipProvider, contentSectionConfig)
        {
        }
    
        protected override void OnLoginSuccess(IdentityAuditEventArgs e)
        {
            // code here
            int userId = e.AffectedUser;
    
            base.OnLoginSuccess(e);
        }
    
    }
    

    Best regards,

    iNETZO

  • Max 80 posts 437 karma points
    Mar 31, 2020 @ 19:32
    Max
    0

    iNETZO, thanks for the reply:

    Would I need to register a composer for this class to get it to execute on login?

    Do I need to put this class in AppCode, or can it reside in my AppPlugin?

  • iNETZO 133 posts 496 karma points c-trib
    Mar 31, 2020 @ 21:29
    iNETZO
    0

    What version of Umbraco are you using?

  • Max 80 posts 437 karma points
    Apr 01, 2020 @ 12:30
    Max
    0

    We're running 8.5.5

  • iNETZO 133 posts 496 karma points c-trib
    Apr 02, 2020 @ 19:34
    iNETZO
    100

    I made a small example. In this example the classes are created in the namespace "Umbraco86.BackOffice" but you can create your own namespace:

    Create an custom class that inherrits BackOfficeUserManager :

    using Microsoft.AspNet.Identity;
    using Microsoft.AspNet.Identity.Owin;
    using Umbraco.Core.Configuration.UmbracoSettings;
    using Umbraco.Core.Models.Identity;
    using Umbraco.Core.Security;
    using Umbraco.Web.Security;
    
    namespace Umbraco86.BackOffice
    {
        public class CustomBackOfficeUserManager : BackOfficeUserManager
        {
            public CustomBackOfficeUserManager(IUserStore<BackOfficeIdentityUser, int> store, IdentityFactoryOptions<BackOfficeUserManager> options, MembershipProviderBase membershipProvider, IContentSection contentSectionConfig) : base(store, options, membershipProvider, contentSectionConfig)
            {
            }
    
            protected override void OnLoginSuccess(IdentityAuditEventArgs e)
            {
                // code here
                int userId = e.AffectedUser;
    
                base.OnLoginSuccess(e);
            }
    
        }
    }
    

    Then create a custom a class that inherrits from UmbracoDefaultOwinStartup to attach the created BackOfficeUserManager:

    using Microsoft.Owin;
    using Owin;
    using Umbraco.Core.Models.Identity;
    using Umbraco.Core.Security;
    using Umbraco.Core.Services;
    using Umbraco.Web;
    using Umbraco.Web.Security;
    using Umbraco86.BackOffice;
    
    [assembly: OwinStartup("UmbracoCustomStartup", typeof(UmbracoCustomStartup))]
    namespace Umbraco86.BackOffice
    {
        public class UmbracoCustomStartup : UmbracoDefaultOwinStartup
        {
            /// <summary>
            /// Configures services to be created in the OWIN context (CreatePerOwinContext)
            /// </summary>
            /// <param name="app"/>
            protected override void ConfigureServices(IAppBuilder app, ServiceContext services)
            {
                base.ConfigureServices(app, services);
    
                app.ConfigureUserManagerForUmbracoBackOffice<BackOfficeUserManager, BackOfficeIdentityUser>(
                    RuntimeState,
                    GlobalSettings,
                    (options, context) =>
                    {
                        var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
                        var store = new BackOfficeUserStore(
                            services.UserService,
                            services.MemberTypeService,
                            services.EntityService,
                            services.ExternalLoginService,
                            GlobalSettings,
                            membershipProvider,
                            Mapper);
                        return new CustomBackOfficeUserManager(store, options, membershipProvider, UmbracoSettings.Content);
                    });
            }
        }
    }
    

    In your web.config it's necessary to change the owin:appStartup setting from

    <add key="owin:appStartup" value="UmbracoDefaultOwinStartup" />
    

    to

    <add key="owin:appStartup" value="UmbracoCustomStartup"/>
    

    This is necessary to get the custom BackOfficeUserManager to be initalised.

    If you run your site in debugmode and put a breakpoint in the OnLoginSuccess method, your breakpoint should be hit when you login to the backoffice.

    Best regards,

    iNETZO

  • Thomas 315 posts 602 karma points c-trib
    Jul 16, 2020 @ 11:03
    Thomas
    0

    Hey :)

    I'm trying to use your code here.

    After I add it and try to login i'm getting "Login failed for user:... "

    and this error..

    Message":"An error has occurred.","ExceptionMessage":"The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. ","ExceptionType":"System.FormatException","StackTrace":"   at System.Convert.FromBase64_ComputeResultLength(Char* inputPtr, Int32 inputLength)\r\n   at System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)\r\n   at System.Convert.FromBase64String(String s)\r\n   at Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password)\r\n   at Microsoft.AspNet.Identity.PasswordHasher.VerifyHashedPassword(String hashedPassword, String providedPassword)\r\n   at Microsoft.AspNet.Identity.UserManager`2.<VerifyPasswordAsync>d__87.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Umbraco.Web.Security.BackOfficeUserManager`1.<VerifyPasswordAsync>d__23.MoveNext() in d:\\a\\1\\s\\src\\Umbraco.Web\\Security\\BackOfficeUserManager.cs:line 429\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Microsoft.AspNet.Identity.UserManager`2.<CheckPasswordAsync>d__81.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Umbraco.Web.Security.BackOfficeUserManager`1.<CheckPasswordAsync>d__19.MoveNext() in d:\\a\\1\\s\\src\\Umbraco.Web\\Security\\BackOfficeUserManager.cs:line 380\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Umbraco.Web.Security.BackOfficeSignInManager.<PasswordSignInAsyncImpl>d__7.MoveNext() in d:\\a\\1\\s\\src\\Umbraco.Web\\Security\\BackOfficeSignInManager.cs:line 107\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Umbraco.Web.Security.BackOfficeSignInManager.<PasswordSignInAsync>d__6.MoveNext() in d:\\a\\1\\s\\src\\Umbraco.Web\\Security\\BackOfficeSignInManager.cs:line 57\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at Umbraco.Web.Editors.AuthenticationController.<PostLogin>d__14.MoveNext() in d:\\a\\1\\s\\src\\Umbraco.Web\\Editors\\AuthenticationController.cs:line 223\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Threading.Tasks.TaskHelpersExtensions.<CastToObject>d__1`1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ApiControllerActionInvoker.<InvokeActionAsyncCore>d__1.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__6.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.ActionFilterAttribute.<ExecuteActionFilterAsyncCore>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__5.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Filters.AuthorizationFilterAttribute.<ExecuteAuthorizationFilterAsyncCore>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__15.MoveNext()"}
    

    Do you now what i'm doing wrong ?

    My startup:

    protected override void ConfigureServices(IAppBuilder app, ServiceContext services)
        {
            base.ConfigureServices(app, services);
            app.ConfigureUserManagerForUmbracoBackOffice<BackOfficeUserManager, BackOfficeIdentityUser>(
                RuntimeState,
                GlobalSettings,
                (configoptions, context) =>
                {
                    var membershipProvider = MembershipProviderExtensions.GetMembersMembershipProvider().AsUmbracoMembershipProvider();
                    var store = new BackOfficeUserStore(
                        services.UserService,
                        services.MemberTypeService,
                        services.EntityService,
                        services.ExternalLoginService,
                        GlobalSettings,
                        membershipProvider,
                        Mapper);
                    return new BackofficeLogin(store, configoptions, membershipProvider, UmbracoSettings.Content);
                });
    
            // Configure hangfire
            var options = new SqlServerStorageOptions { PrepareSchemaIfNecessary = true };
            const string umbracoConnectionName = Umbraco.Core.Constants.System.UmbracoConnectionName;
            var connectionString = System.Configuration
                .ConfigurationManager
                .ConnectionStrings[umbracoConnectionName]
                .ConnectionString;
            GlobalConfiguration.Configuration
                .UseSqlServerStorage(connectionString, options)
                .UseConsole();
    
            var dashboardOptions = new DashboardOptions { Authorization = new[] { new UmbracoAuthorizationFilter() } };
            // Give hangfire a URL and start the server                
            app.UseHangfireDashboard("/hangfire", dashboardOptions);
            app.UseHangfireServer();
            StartHangfireServer(app);
    
            // Schedule jobs
            var scheduler = new ScheduleHangfireJobs();
            scheduler.BaselineSync();
        }
    

    And my BackofficeUserManager extened:

    public class BackofficeLogin : BackOfficeUserManager
    {
        public BackofficeLogin(IUserStore<BackOfficeIdentityUser, int> store, IdentityFactoryOptions<BackOfficeUserManager> configoptions, MembershipProviderBase membershipProvider, IContentSection contentSectionConfig) : base(store, configoptions, membershipProvider, contentSectionConfig)
        {
        }
    
        protected override void OnLoginSuccess(IdentityAuditEventArgs e)
        {
            // code here
            int userId = e.AffectedUser;
    
            base.OnLoginSuccess(e);
        }
    }
    
  • Max 80 posts 437 karma points
    Jul 16, 2020 @ 11:20
    Max
    0

    It appears to me, on the surface, that your connection string to connect to your database is corrupted in one way or another.

    Have you debugged, and caught what line it fails on during authentication?

  • Max 80 posts 437 karma points
    Apr 03, 2020 @ 12:05
    Max
    0

    iNETZO,

    This has been the missing link, I will give it a try post haste. I have a question about convention on where you put the classes? Do you put them in the sites ~/controller folder, or ~/appcode, or in your ~/appplugin/[app_name] folder?

    Is there a best practice or convention for this type of extension to the owinstartup?

  • iNETZO 133 posts 496 karma points c-trib
    Apr 03, 2020 @ 14:13
    iNETZO
    0

    That depends on what type of project you are working on or what type of developer you are.

    If you are installing Umbraco without using Visual Studio then the only choice you have (as far as i know) is to put it in the App_Code folder.

    If you install Umbraco via Visual Studio then you can put the classes wherever you want. You could create a folder just for your own code. In this small example i've put the classes in a folder called "BackOffice", that why the namespace is "Umbraco86.Backoffice" (the project was named "Umbraco86").

    We're mostly using Umbraco as a foundation for a (pretty) large platform. We have lots of code and to keep it manageable we're using separate class libraries.

    So there are many ways to manage your code, it depends of the situation.

  • Max 80 posts 437 karma points
    Apr 03, 2020 @ 14:39
    Max
    0

    Thanks for the insight.

    The above solution, extending the OWIN Startup was the missing link. I had been hesitant about going down that road for fear of messing up the login procedure... Your time, effort and nudge was what I needed.

    I've been developing everything in my main Umbraco project in VS. I will start looking into breaking out my more complex code into class libraries.

    I'm a small (1.5 man) team, so I have the flexibility to do what i want.... which is what causes me the most difficulty. "Too many ways to skin that cat." and "Perfect is the enemy of good enough" are common themes in my development life cycles.

    Thank you, sincerely for your guidance and plugging the holes in my thought.

    Max

Please Sign in or register to post replies

Write your reply to:

Draft