Shazwazza / UmbracoIdentity

ASP.NET Identity implementation for Umbraco's native member data
MIT License
111 stars 58 forks source link

Integration of the 3rd party Auth0 (auth0.com) login provider #79

Open bsubasic opened 7 years ago

bsubasic commented 7 years ago

Hi,

Is there any example of how we can integrate famous 3rd party Auth0 login provider? https://auth0.com/docs/quickstart/webapp/aspnet-owin/01-login

Where to hook up this piece of code in UmbracoIdentityStartup class:

         // Configure Auth0 parameters
          string auth0Domain = ConfigurationManager.AppSettings["auth0:Domain"];
          string auth0ClientId = ConfigurationManager.AppSettings["auth0:ClientId"];
          string auth0ClientSecret = ConfigurationManager.AppSettings["auth0:ClientSecret"];

          // Set Cookies as default authentication type
          app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
          app.UseCookieAuthentication(new CookieAuthenticationOptions
          {
                AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
                LoginPath = new PathString("/Account/Login")
          });

          // Configure Auth0 authentication
          var options = new Auth0AuthenticationOptions()
          {
                Domain = auth0Domain,
                ClientId = auth0ClientId,
                ClientSecret = auth0ClientSecret,
          };

          options.Scope.Add("offline_access"); // Request a refresh_token
          app.UseAuth0Authentication(options);

What I want to achieve is to enable the creation of Umbraco members on newly registered users and automatically login members for existing users. Can somebody give me some guidelines?

I tried to setup this in many ways, but I'm confused about the duplicate app.UseCookieAuthentication method definition (tried to merge it, but then I get Auth0 login loop) and what must be declared in which order.

Thanks

bsubasic commented 7 years ago

I managed to configure Auth0, but I didn't succeed to configure members cookie (members login) to work together. I read this post configuring-aspnet-identity-oauth-login-providers-for-multi-tenancy but I'm not sure why it doesn't work. Members.GetCurrentLoginStatus().IsLoggedIn function is false. Also, I don't see cookie "MemberCookie" stored in the browser. Can I have both Auth0 and Member login cookie so I can use Auth0 only for authentication and UmbracoMembership for custom member properties and authorization?

This is my current code:

protected override void ConfigureMiddleware(IAppBuilder app)
        {
            // workaround middleware saving owin auth cookies from Katana bug #197
            app.UseKentorOwinCookieSaver();

            // cookie for Auth0
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = "Auth0",
                AuthenticationMode = AuthenticationMode.Active,
                CookieName = "Auth0",
                Provider = new CookieAuthenticationProvider()
                {
                    OnResponseSignIn = (context => MemberHandler.EnsureMemberRegistrationAndSignIn(context)),
                    OnResponseSignOut = (context => Debug.WriteLine("Hook up member logout")) //TODO
                },
                LoginPath = new PathString("/Account/Login")

            }, PipelineStage.Authenticate);

            // Enable the application to use a cookie to store information for the 
            // signed in user and to use a cookie to temporarily store information 
            // about a user logging in with a third party login provider 
            // Configure the sign in cookie
            app.UseCookieAuthentication(new FrontEndCookieAuthenticationOptions
            {
                //Provider = new CookieAuthenticationProvider
                //{
                //     Enables the application to validate the security stamp when the user
                //     logs in. This is a security feature which is used when you
                //     change a password or add an external login to your account.
                //    OnValidateIdentity = SecurityStampValidator
                //        .OnValidateIdentity<UmbracoMembersUserManager<UmbracoApplicationMember>, UmbracoApplicationMember, int>(
                //            TimeSpan.FromMinutes(30),
                //            (manager, user) => user.GenerateUserIdentityAsync(manager),
                //            UmbracoIdentity.IdentityExtensions.GetUserId<int>),

                //    OnResponseSignIn = (async context => await MemberHandler.EnsureMemberRegistrationAndSignIn(context)),
                //    OnResponseSignOut= (context => Debug.WriteLine("Hook up member logout")) //TODO
                //},
                //LoginPath = new PathString("/Account/Login"),
                AuthenticationType = "MemberCookie",
                AuthenticationMode = AuthenticationMode.Passive,
                CookieName = "MemberCookie"
            });

            //Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN
            // cookie configuration, this must be declared after it.
            app
                .UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
                .UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate);

            // Uncomment the following lines to enable logging in with third party login providers
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Configure Auth0 parameters
            string auth0Domain = ConfigurationManager.AppSettings["auth0:Domain"];
            string auth0ClientId = ConfigurationManager.AppSettings["auth0:ClientId"];
            string auth0ClientSecret = ConfigurationManager.AppSettings["auth0:ClientSecret"];

            // Set Cookies as default authentication type
             //app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ApplicationCookie);
               app.SetDefaultSignInAsAuthenticationType("Auth0");

            // Configure Auth0 authentication
            var options = new Auth0AuthenticationOptions
            {
                Domain = auth0Domain,
                ClientId = auth0ClientId,
                ClientSecret = auth0ClientSecret,
                AuthenticationType = "Auth0",
                SignInAsAuthenticationType = "Auth0"
            };
            options.Scope.Add("offline_access"); // Request a refresh_token

            app.UseAuth0Authentication(options);

            //app.UseMicrosoftAccountAuthentication(
            //  clientId: "",
            //  clientSecret: "");

            //app.UseTwitterAuthentication(
            //  consumerKey: "",
            //  consumerSecret: "");

            //app.UseFacebookAuthentication(
            //  appId: "",
            //  appSecret: "");

            //app.UseGoogleAuthentication(
            //  clientId: "",
            //  clientSecret: ""); 

            //Lasty we need to ensure that the preview Middleware is registered, this must come after
            // all of the authentication middleware:
            app.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize);
        }
 public ClaimsIdentity GenerateUserIdentity(UserManager<UmbracoApplicationMember, int> manager)
        {
            // Note the authenticationType must match the one 
            // defined in CookieAuthenticationOptions.AuthenticationType - DefaultAuthenticationTypes.ApplicationCookie
            var userIdentity =  manager.CreateIdentity(this, "MemberCookie");

            // Add custom user claims here
            return userIdentity;
        }
Shazwazza commented 7 years ago

Hi,

I need to know exactly what you are trying to do here? Are you trying to use Auth0 as an OAuth provider? (i.e. with an OAuth button like logging int to Google?)

You need to understand that traditional APIs in Umbraco for members like:

Members.GetCurrentLoginStatus()

Are not valid when using UmbracoIdentity. Part of the setup for Umbraco Identity is to removes forms authentication, so now the auth is managed 100% with ASP.NET Identity and Umbraco itself doesn't know about this so you cannot use some of those traditional methods, you will need to use ASP.NET methods.

This project comes with a full demo and you should start by looking there, following the docs and looking at the OAuth examples like the Google one and getting those up and running before trying something more complicated.

Also be sure to call the base.ConfigureMiddleware

I don't know why you are using UseCookieAuthentication twice, there's nothing in the documentation here telling you to do this. I also don't know why you are trying to use 'MemberCookie' as this isn't part of this project either. I think you are confusing probably the Auth0 documentation in combination with this documentation.

bsubasic commented 7 years ago

Hi @Shazwazza,

Thanks for the answer!

Are you trying to use Auth0 as an OAuth provider?

Yes, Auth0 is a service which provides other OAuth providers out of the box (Facebook, Google, Twitter etc.)

You need to understand that traditional APIs in Umbraco for members like: Members.GetCurrentLoginStatus() Are not valid when using UmbracoIdentity. Part of the setup for Umbraco Identity is to remove forms authentication, so now the auth is managed 100% with ASP.NET Identity and Umbraco itself doesn't know about this so you cannot use some of those traditional methods, you will need to use ASP.NET methods.

Ok, that is clear now - I was looking through all the documentation and demo projects before starting to write code.

I'm trying to use Auth0 (OAuth provider) only for authentication and that works.

Besides that, I want to have regular members created by UmbracoIdentity during successful authentication (I'm using methods from UmbracoIdentityAccountController) which I will use for authorization (where the logged in member can store other information via user dashboard into custom member properties into Umbraco store (DB)).

When I access User.Identity there are claims from the provider I used to authenticate (Google or Facebook, which Auth0 provides). After creating a new member (with UserManager.Create) and calling SignIn method for that Umbraco member, I'm not sure what happens after that?

I can see that newly created member in the back office section, but how I can interact with it? I inspect to have Umbraco member in User.Identity and not Google of Facebook claims, or I'm wrong?

Thanks

alanmac commented 6 years ago

Hi @bsubasic. Did you ever manage to integrate Auth0 with Umbraco as you describe above?

stodolos commented 4 years ago

Hi @bsubasic ,

I think we're both trying to achieve similar things. I am using Auth0 for authentication with UmbracoIdentity, and I want to use my own data store for users and roles (basically bypass Umbraco's built-in member logic all together).

I have the authentication piece as well as the IMemberService and IMemberGroupService pieces working so everything seems to be coming from my custom classes. However, I also made a custom MembershipProbvider, which inherits from MembersMembershipProvider, in order to inject custom implementations of IMembershipMemberService and IMemberTypeService.

Hi @Shazwazza, I'm unsure if my own MembershipProvider is needed, or do I just need to register those custom implementations in my IUserComposer, and the standard IdentityEnabledMembersMembershipProvider will suffice?

This article was helpful for me, and I posed the question there too.

Thanks

Shazwazza commented 4 years ago

Hi @stodolos

I think you just want your own implementation, not sure why what you're doing needs to have anything to do with Umbraco specifically in that case

I am using Auth0 for authentication with UmbracoIdentity, and I want to use my own data store for users and roles (basically bypass Umbraco's built-in member logic all together).

IMO then you should just use the default aspnet identity implementation that uses entity framework

as well as the IMemberService and IMemberGroupService pieces working so everything seems to be coming from my custom classes

Generally speaking you should never replace Umbraco's services, this is basically unsupported. This is also an enormous amount of work compared to just using the default aspnet identity implementation of members and roles that you manage.

The only reason i can see you trying to shim all of your custom data through a custom IMemberService and this package is if you want to edit members in the back office. But if you want that, then why not just use Umbraco's members?

In my opinion if you don't want to use umbraco members and you want aspnet identity with your own data store, then this is just your own aspnet identity implementation which shouldn't require this package or really anything to do with Umbraco. But perhaps I'm missing some key thing?

stodolos commented 4 years ago

Thanks for the response @Shazwazza. You are correct in that I was hoping to reuse the existing back office UI to manage the members if possible.

I honestly don't want to use the built-in Umbraco member services b/c it has caused a lot of locking and slowness for us in Azure SQL. It doesn't happen every single day, but from time to time, blocking locks get created, and then no one can login to our site. We need to be able to handle at around 50k-100k simultaneous users (hopefully all won't be logging in for the first time), and we see hiccups at times with less than 100. Perhaps simply offloading the authentication to a third party will help alleviate that, but I figured why not have full control of the member interaction.

Initially I was trying to use UmbracoIdentity to stay within the standard Umbraco flow as much as possible, but then did realize that it will still use the built in Umbraco member pieces for CRUD. So, then I started trying to go further by implementing my own services, etc. However, if this is unsupported (and I understand why), then I may follow your advice of going back to bypassing the Umbraco member pieces all together.

Thanks!

Shazwazza commented 4 years ago

There's been a lot of fixes relating directly to SQL locks for members in this release: https://our.umbraco.com/download/releases/865 namely this PR https://github.com/umbraco/Umbraco-CMS/pull/8525 for this issue https://github.com/umbraco/Umbraco-CMS/issues/8433. There's quite a lot that has been resolved specifically about that so if you want to try with Umbraco members again that's the version you'll want to use (or later).

Else it's no problem at all rolling your own members on the front-end. The benefit of Umb members is really 2 things: you can manage them in the back office and they integrate nicely with the 'public access' feature - though you can still make that feature work in your own way with your own members and hijacked controllers (i.e. the normal mvc authorization way)