Copied to clipboard

Flag this post as spam?

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


  • Hassan Mourad 5 posts 75 karma points
    Aug 13, 2018 @ 14:25
    Hassan Mourad
    0

    i am trying to authenticate my umbraco front-end members using my azure b2c tenant , i have installed the package "UmbracoIdentity" https://github.com/Shazwazza/UmbracoIdentity that allows to create owin startup class to have the b2c middleware.

    authentication works just fine , but on-receiving ( method "OnAuthorizationCodeReceivedAsync" ) the code from B2C i have an error , that the session is NULL ! and i need that session to store my token.

    /// <summary>
    /// OWIN Startup class for UmbracoIdentity 
    /// </summary>
    public class UmbracoIdentityStartup : UmbracoDefaultOwinStartup
    {
        // App config settings
        public static string ClientId = ConfigurationManager.AppSettings["ida:ClientId"];
        public static string ClientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
        public static string AadInstance = ConfigurationManager.AppSettings["ida:AadInstance"];
        public static string Tenant = ConfigurationManager.AppSettings["ida:Tenant"];
        public static string RedirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
        public static string ServiceUrl = ConfigurationManager.AppSettings["api:TaskServiceUrl"];
    
        // B2C policy identifiers
        public static string SignUpSignInPolicyId = ConfigurationManager.AppSettings["ida:SignUpSignInPolicyId"];
        public static string ResetPasswordPolicyId = ConfigurationManager.AppSettings["ida:ResetPasswordPolicyId"];
    
        public static string DefaultPolicy = SignUpSignInPolicyId;
    
        // API Scopes
        public static string ApiIdentifier = ConfigurationManager.AppSettings["api:ApiIdentifier"];
        public static string ReadTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:ReadScope"];
        public static string WriteTasksScope = ApiIdentifier + ConfigurationManager.AppSettings["api:WriteScope"];
        public static string[] Scopes = new string[] { "" /*ReadTasksScope, WriteTasksScope*/ };
    
        // OWIN auth middleware constants
        public const string ObjectIdElement = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
    
        // Authorities
        public static string Authority = String.Format(AadInstance, Tenant, DefaultPolicy);
    
        /// <summary>
        /// Configures services to be created in the OWIN context (CreatePerOwinContext)
        /// </summary>
        /// <param name="app"/>
        protected override void ConfigureServices(IAppBuilder app)
        {
            app.Use((context, next) =>
            {
                var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
                httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
                return next();
            }).UseStageMarker(PipelineStage.MapHandler);
    
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
    
            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    // Generate the metadata address using the tenant and policy information
                    MetadataAddress = String.Format(AadInstance, Tenant, DefaultPolicy),
    
                    // These are standard OpenID Connect parameters, with values pulled from web.config
                    ClientId = ClientId,
                    RedirectUri = RedirectUri,
                    PostLogoutRedirectUri = RedirectUri,
    
                    // Specify the callbacks for each type of notifications
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        RedirectToIdentityProvider = OnRedirectToIdentityProviderAsync,
                        AuthorizationCodeReceived = OnAuthorizationCodeReceivedAsync,
                        AuthenticationFailed = OnAuthenticationFailedAsync,
                    },
    
                    // Specify the claim type that specifies the Name property.
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "name"
                    },
    
                    // Specify the scope by appending all of the scopes requested into one string (separated by a blank space)
                    Scope = $"openid profile offline_access",// {ReadTasksScope}", {WriteTasksScope}",
                }
            );
    
            app.Use((context, next) =>
            {
                if (HttpContext.Current.Session != null)
                { 
                    HttpContext.Current.Session["test"] = 1;
                } 
                return next();
            }).UseStageMarker(PipelineStage.PostAcquireState);
    
            base.ConfigureServices(app);
    
            //Single method to configure the Identity user manager for use with Umbraco
            app.ConfigureUserManagerForUmbracoMembers<UmbracoApplicationMember>();
    
            //Single method to configure the Identity user manager for use with Umbraco
            app.ConfigureRoleManagerForUmbracoMembers<UmbracoApplicationRole>();
        }
    
        /// <summary>
        /// Configures middleware to be used (i.e. app.Use...)
        /// </summary>
        /// <param name="app"/>
        protected override void ConfigureMiddleware(IAppBuilder app)
        {
            //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);
    
            // 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>)
                }
            }, PipelineStage.Authenticate);
    
            // Uncomment the following lines to enable logging in with third party login providers
    
            //app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
    
            //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);
        }
    
    
    
    
    
    
    
        /*
         *  On each call to Azure AD B2C, check if a policy (e.g. the profile edit or password reset policy) has been specified in the OWIN context.
         *  If so, use that policy when making the call. Also, don't request a code (since it won't be needed).
         */
        private Task OnRedirectToIdentityProviderAsync(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            notification.OwinContext.Set<string>("policy", DefaultPolicy);
    
            var policy = notification.OwinContext.Get<string>("Policy");
    
            if (!string.IsNullOrEmpty(policy) && !policy.Equals(DefaultPolicy))
            {
                notification.ProtocolMessage.Scope = OpenIdConnectScope.OpenId;
                notification.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
                notification.ProtocolMessage.IssuerAddress = notification.ProtocolMessage.IssuerAddress.ToLower().Replace(DefaultPolicy.ToLower(), policy.ToLower());
            }
    
            return Task.FromResult(0);
        }
    
        /*
         * Catch any failures received by the authentication middleware and handle appropriately
         */
        private Task OnAuthenticationFailedAsync(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        { 
            // Handle the error code that Azure AD B2C throws when trying to reset a password from the login page 
            // because password reset is not supported by a "sign-up or sign-in policy"
            if (notification.ProtocolMessage.ErrorDescription != null && notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
            {
                // If the user clicked the reset password link, redirect to the reset password route
                notification.Response.Redirect("/Account/ResetPassword");
            }
            else if (notification.Exception.Message == "access_denied")
            {
                notification.Response.Redirect("/");
            }
            else
            {
                notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
            }
    
            return Task.FromResult(0);
        }
    
        /*
         * Callback function when an authorization code is received 
         */
        private Task OnAuthorizationCodeReceivedAsync(AuthorizationCodeReceivedNotification notification)
        { 
            // Extract the code from the response notification
            var code = notification.Code;
    
            string signedInUserID = notification.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
            TokenCache userTokenCache = new MSALSessionCache(signedInUserID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase).GetMsalCacheInstance();
            ConfidentialClientApplication cca = new ConfidentialClientApplication(ClientId, Authority, RedirectUri, new ClientCredential(ClientSecret), userTokenCache, null);
            try
            {
                AuthenticationResult result = cca.AcquireTokenByAuthorizationCodeAsync(code, Scopes).Result;
                return Task.FromResult(0);
            }
            catch (Exception ex)
            {
                //TODO: Handle
                throw;
            }
        }
    }
    

    }

    anyone tried to do that before ? and faced that issue ???

  • Sumu Cooper 1 post 71 karma points
    Feb 26, 2019 @ 19:12
    Sumu Cooper
    0

    Hassan, were you able to resolved your session issue? I want to implement Azure B2C for my member area on my site.

  • Robert Foster 459 posts 1820 karma points MVP 2x admin c-trib
    Oct 29, 2019 @ 00:19
    Robert Foster
    0

    I know this is an old question, but to help others with the same problem, here's an explanation as to why Session isn't available in Owin callbacks:

    SignalR MUST run before session is acquired to prevent long session locks. Why is this relevant? Because Umbraco uses SignalR. Even if you try to force the Session to be initialised before your authentication callbacks, it won't work due to this.

    Unfortunately there's no way around this without crippling Umbraco that I can currently find; so in order to cache details, my suggestion is to currently create a database cache instead of using Session state.

    For more details, see the following SO articule:

    https://stackoverflow.com/questions/23565543/can-owin-middleware-use-the-http-session

Please Sign in or register to post replies

Write your reply to:

Draft