Copied to clipboard

Flag this post as spam?

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


  • Eric Schrepel 161 posts 226 karma points
    Sep 09, 2019 @ 23:17
    Eric Schrepel
    0

    How can I add [Authorize] to all front-end pages [via controller maybe]

    Odd question, but I'm trying to secure an Umbraco 8.1.4 intranet by using Okta to auto-login staff with their Windows credentials (using essentially these steps).

    I've modified a MyOwinStartup.cs to include the Okta code, plus added the line "GlobalFilters.Filters.Add(new AuthorizeAttribute())", which forces all controllers to require authorization. This approach works fine for the front end, but BackOffice users find that after login they can't do anything else (404 errors etc).

    Short of implementing the full UmbracoIdentity approach (trying to avoid the whole Members approach, don't really need that level of login control), is there a way to maybe include a surface controller on every page to which we could prepend the [Authorize] keyword? I'd then remove the GlobalFilters code from MyOwinStartup.

    Or if there's a better (but still sorta easy) approach, would love to hear what others recommend.

  • Marc Goodson 2138 posts 14321 karma points MVP 8x c-trib
    Sep 10, 2019 @ 08:56
    Marc Goodson
    1

    Hi Eric

    I'm not sure if this is what you need for what you are trying to achieve but you can replace the default RenderMvcController that Umbraco uses with your own implementation... Eg create a new controller; inherit RenderMvcController and set it as the default during composing...

    See here for how:

    https://our.umbraco.com/Documentation/Implementation/Default-Routing/Controller-Selection/

  • Eric Schrepel 161 posts 226 karma points
    Sep 12, 2019 @ 21:28
    Eric Schrepel
    1

    Still feel like I'm so close yet so far. Okta works for auto-logging in people to front-end pages (not as Members, strictly as Windows-authenticated users), but the BackOffice throws a bunch of errors after login: enter image description here

    Per Marc's suggestion, I replaced the default RenderMvcController to include the [Authorize] parameter, see code below from my App_Start/SetDefaultMvcControllerComposer.cs. This does seem to correctly trigger Okta to log users in when they first hit any front-end page. enter image description here

    Web.config has these lines which pertain to Okta login:

    <add key="owin:appStartup" value="MyOwinStartup" />
    <add key="ActiveDirectoryDomain" value="{ourdomain}.org" />
    <add key="okta:OktaDomain" value="https://{ouroktadomain}.com" />
    <add key="okta:ClientId" value="__" />
    <add key="okta:ClientSecret" value="__" />
    <add key="okta:RedirectUri" value="https://{ourdomain}/authorization-code/callback" />
    ...
    <!-- <membership><providers> section:  -->
    <add name="UmbracoMembershipProvider" type="Umbraco.Web.Security.Providers.MembersMembershipProvider, Umbraco.Web" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="10" useLegacyEncoding="false" enablePasswordRetrieval="false" enablePasswordReset="false" requiresQuestionAndAnswer="false" defaultMemberTypeAlias="Member" passwordFormat="Hashed" allowManuallyChangingPassword="false" />
    ...
    <!-- <system.web> section: -->
    <authentication mode="None" />
    ...
    <!-- <modules> section: -->
    <remove name="FormsAuthentication" />
    

    And lastly, App_Start/MyOwinStartup.cs which has both recently-added Okta code, and some earlier code for logging in Backoffice users: enter image description here

    If it's actually better/easier to incorporate Okta login into the UmbracoIdentity code example, we're happy to go that route also, it just seemed like more than we needed since we're not really using the Members section of Umbraco in this intranet, and only are needing Windows Authentication (and can't just set the overall app in IIS to use Windows Authentication because of some mobile and other out-of-office users).

  • John Bergman 483 posts 1132 karma points
    Apr 20, 2021 @ 15:21
    John Bergman
    1

    Did you get this working? We may be going down the same pathway

  • Nick 2 posts 72 karma points
    Oct 20, 2021 @ 11:41
    Nick
    0

    Any updates around this? I seem to have the same problem in reverse with the Google Authenticator, I've managed to get it working for the back office but not the front end. Although I only moved to using the Google Auth because I had trouble getting Okta to work so if you've managed that then I'd really appreciate any advice?

  • Eric Schrepel 161 posts 226 karma points
    Oct 20, 2021 @ 18:43
    Eric Schrepel
    1

    We ended up contracting with FYIN to finish our Okta integration. Works great for front-end intranet users, but for back office we're still logging in manually (which is fine by me). Attaching a little sample code that seems to make all the magic work. There may be a couple other files/controllers that assist, but this is the bulk of it I believe. (We're on Umbraco 8.17.)

    Web.config:

    <add key="owin:appStartup" value="MyOwinStartup" />
    <add key="ActiveDirectoryDomain" value="(ourdomain).org" />
    <add key="okta:OktaDomain" value="https://(ourdomain).okta.com" />
    <add key="okta:OrgUri" value="https://(ourdomain).okta.com/oauth2/default" />
    <add key="okta:ClientId" value="(clientid)" />
    <add key="okta:ClientSecret" value="(secret)" />
    <add key="okta:RedirectUri" value="https://(ourdomain)" />
    <add key="okta:PostLogoutRedirectUri" value="https://(ourdomain)" />
    

    App_Start/MyOwinStartup.cs:

    using Microsoft.Owin;
    using Owin;
    using System.Collections.Generic;
    using System.Configuration;
    using Microsoft.Owin.Security;
    using Microsoft.Owin.Security.Cookies;
    using Microsoft.AspNet.Identity;
    using Microsoft.Owin.Security.OpenIdConnect;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    using Microsoft.IdentityModel.Tokens;
    using IdentityModel.Client;
    using System.Security.Claims;
    using System;
    using System.Linq;
    using Microsoft.AspNet.Identity.Owin;
    using System.Threading.Tasks;
    using UmbracoIdentity;
    using intranet.Models.UmbracoIdentity;
    using intranet;
    using Umbraco.Core.Composing;
    using System.Web.Security;
    
    [assembly: OwinStartup("MyOwinStartup", typeof(MyOwinStartup))]
    namespace intranet
    {
        public class MyOwinStartup : UmbracoIdentityOwinStartupBase
        {
            private readonly string clientId = ConfigurationManager.AppSettings["okta:ClientId"];
            private readonly string redirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"];
            private readonly string authority = ConfigurationManager.AppSettings["okta:OrgUri"];
            private readonly string clientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"];
            private readonly string postLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"];
    
            protected override void ConfigureUmbracoUserManager(IAppBuilder app)
            {
                base.ConfigureUmbracoUserManager(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>();
            }
    
            protected override void ConfigureUmbracoAuthentication(IAppBuilder app)
            {
                base.ConfigureUmbracoAuthentication(app);
    
                app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    
                var cookieOptions = CreateFrontEndCookieAuthenticationOptions();
    
    
                cookieOptions.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),
                                identity =>
                                {
                                    var email = identity.Claims.FirstOrDefault(c => c.Type == "email")?.Value;
                                    var member = Current.Services.MemberService.GetByEmail(email);
    
                                    return member.Id;
                                })
                };
    
                app.UseCookieAuthentication(cookieOptions, PipelineStage.Authenticate);
    
                app.UseExternalSignInCookie(DefaultAuthenticationTypes.ApplicationCookie);
    
                app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    ClientSecret = clientSecret,
                    Authority = authority,
                    RedirectUri = redirectUri,
                    ResponseType = OpenIdConnectResponseType.CodeIdToken,
                    Scope = OpenIdConnectScope.OpenIdProfile + " email",
                    PostLogoutRedirectUri = postLogoutRedirectUri,
                    TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "name"
                    },
    
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthorizationCodeReceived = async n =>
                        {
                            // Exchange code for access and ID tokens
                            var tokenClient = new TokenClient(authority + "/v1/token", clientId, clientSecret);
                            var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(n.Code, redirectUri);
    
                            if (tokenResponse.IsError)
                            {
                                throw new Exception(tokenResponse.Error);
                            }
    
                            var userInfoClient = new UserInfoClient(authority + "/v1/userinfo");
                            var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
                            var claims = new List<Claim>();
                            claims.AddRange(userInfoResponse.Claims);
                            claims.Add(new Claim("id_token", tokenResponse.IdentityToken));
                            claims.Add(new Claim("access_token", tokenResponse.AccessToken));
    
                            if (!string.IsNullOrEmpty(tokenResponse.RefreshToken))
                            {
                                claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
                            }
    
                            n.AuthenticationTicket.Identity.AddClaims(claims);
    
                            var email = claims.FirstOrDefault(c => c.Type == "email")?.Value;
                            var name = claims.FirstOrDefault(c => c.Type == "name")?.Value;
    
                            // create member if not exists already.
                            var member = Services.MemberService.GetByEmail(email);
                            if (member == null)
                            {
                                member = Services.MemberService.CreateMember(email, email, name, "Member");
                                Services.MemberService.Save(member);
                            }
    
                            // login memeber
                            FormsAuthentication.SetAuthCookie(email, true);
    
                            return;
                        },
    
                        RedirectToIdentityProvider = n =>
                        {
                            // If signing out, add the id_token_hint
                            if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                            {
                                var idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token");
    
                                if (idTokenClaim != null)
                                {
                                    n.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
                                }
    
                            }
    
                            return Task.CompletedTask;
                        }
                    },
                });
            }
        }
    }
    
  • Nick 2 posts 72 karma points
    Oct 21, 2021 @ 07:32
    Nick
    0

    This is fantastic, thank you very much Eric.

Please Sign in or register to post replies

Write your reply to:

Draft